2011-07-05 14 views
3

以下があります。UserIdとUserテーブルで注文します。Entity Framework 4.1でキャッシュされた非接続エンティティをアタッチするコード

すべてのUserオブジェクトを切断されたエンティティとしてキャッシュし、ASP.NETやWebサービスなどの環境で使用するとします。

環境はUnitOfWorkとIOCフレームワークを使用しているため、コンテキスト操作は避けたいと考えています。ユニットテストは次のように:

    1. 切断されたユーザオブジェクト
    2. コンテキストを作成しなさいOrderオブジェクト
    3. を作成したユーザオブジェクトかID
    4. を介してユーザを接続しますが、全体Orderオブジェクトを永続化します。

    激しいGoogle検索の2日後、解決策が見つかりませんでした。

    問題は、次のとおりです。

    1.ユーザーオブジェクト

    我々はEFが新しいユーザーであると考えているデフォルトの動作で、(= 1 userIdを持つ)ユーザーオブジェクトを装着している場合新しいユーザオブジェクトをdbに永続化しようとしています。これは、dbが既にuserId == 1を持っているために吹き飛ばされます。

    可能な解決策は、DbContext.SaveChangesをオーバーライドして、ユーザーが「切り離され」、コンテキストに強制的に添付するかどうかを試してみることです。 SaveChangesから呼び出されたときに拡張メソッドEFExtensionMethods.IsAttachedまたはEFExtensionMethods.AttachItemがTがオブジェクトだと思うので、その方法を理解できませんでした。

    2. USER_ID

    これは、あなたが全体のエンティティを永続化し、リロードする前にOrder.Userオブジェクトにアクセスしたい場合を除き、動作します。

    さらに、実際のオブジェクト(Order.User)ではなくオブジェクトのID(つまりOrder.UserId)を使用して保存するように、APIを分割する必要があります。オブジェクトをリロードしたら、両方を使うことができます。しかし、私は実際にAPIを通してそれを強制する方法がないのがわかります。

    3.両方のユーザーオブジェクトとユーザーが外部キーとしてユーザーIDを使用するものとしてマークされている場合でも、このシナリオで

    USER_IDは、EFはまだ1で説明した問題を打つコンテキストにユーザーオブジェクトを保存しようとしています。

    私は基本的な何か(またはロット)を欠けているとの質問があるように見える:

    • あなたが行うことをお勧めでしょうか?
    • DbContext.SaveChanges
    • からEFExtensionMethods.IsAttached作品を作る素晴らしく、一般的な方法はありDbContextからEFExtensionMethods.AttachItem作品を作る素晴らしく、一般的な方法がありますです。SaveChanges

    何か助けていただければ幸いです。

    [TestMethod] 
    public void Creating_Order_With_Both_User_And_Id() 
    { 
        int userCount = CountAll<User>(); 
        User user; 
        using (var db = GetContext()) { user = db.Users.AsNoTracking().First(); } 
        using (var db = GetContext()) 
        { 
         var o = CreateOrder(); 
         o.OrderUser = user;   // attach user entity 
         o.UserId = user.UserID;  // attach by id 
         db.Orders.Add(o); 
         db.SaveChanges(); 
        } 
        int newUserCount = CountAll<User>(); 
        Assert.IsTrue(userCount == newUserCount, string.Format("Expected {0} got {1}", userCount, newUserCount)); 
    } 
    

    コンテキストとクラス:

    public class User 
    { 
        public User() {} 
        public int UserID {get;set;} 
        public string UserName {get;set;} 
    } 
    
    public class Order 
    { 
        public Order() { } 
    
        public int OrderID { get; set; } 
        public DateTime OrderDate { get; set; } 
        public string OrderName { get; set; } 
    
        public int UserId { get; set; } 
        public virtual User OrderUser { get; set; } 
    } 
    
    public class OrderConfiguration : EntityTypeConfiguration<Order> 
    { 
        public OrderConfiguration() 
        { 
         this.ToTable("ORDERS"); 
         this.Property(x => x.OrderName).HasMaxLength(200); 
         this.HasRequired(u => u.OrderUser).WithMany().HasForeignKey(u => u.UserId); 
        } 
    } 
    
    public static class EFExtensionMethods 
    { 
        // does not work, when called from DbContext.SaveChanges thinks T is an Object. 
        public static bool IsAttached<T>(this PPContext db, T entity) where T: class 
        { 
         return db.Set<T>().Local.Any(e => e == entity); 
        } 
    
        // does not work, when called from DbContext.SaveChanges thinks T is an Object. 
        public static void AttachItem<T>(this PPContext db, T entity) where T: class 
        { 
         db.Set<T>().Attach(entity); 
        } 
    } 
    
    public class PPContext : DbContext 
    { 
        public PPContext() : base() { } 
        public PPContext(DbConnection connection) : base(connection, true) { } 
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder) 
        { 
         modelBuilder.Configurations.Add(new OrderConfiguration()); 
        } 
    
        public override int SaveChanges() 
        { 
         var modified = ChangeTracker.Entries().Where(e => e.State == EntityState.Modified || e.State == EntityState.Added); 
         foreach (var item in modified) 
         { 
          ???? 
         } 
        } 
    
        public DbSet<User> Users {get;set;} 
    } 
    
  • 答えて

    6

    ユーザーオブジェクトにマーカーインターフェイスを追加し、すべてのICacheableEntityエンティティをエンティティとしてマークしました。 SaveChangesでは、アイテムをUnchangedとしてマークしています。そのようにして動作します。

    public class User : ICacheableEntity 
    { 
        public User() { } 
        public int UserID {get;set;} 
        public string UserName {get;set;} 
    } 
    
    class PPContext 
    { 
        public bool IsSeeding {get;set;} 
    
        public override int SaveChanges() 
        { 
         var modified = ChangeTracker.Entries().Where(e => e.State == EntityState.Modified || e.State == EntityState.Added); 
         if (!IsSeeding) 
         { 
          foreach (var item in modified.Where(item => (item.Entity is ICacheableEntity))) 
          { 
           item.State = EntityState.Unchanged; 
          } 
         } 
        } 
    } 
    
    +0

    これは素晴らしい解決策です! – Slauma

    +0

    キャッシュされたアイテムはすべて不変ですか?キャッシュされているもののように見えるので、変更することはできませんか? IsSeedingフラグで何かしていますか? – Jafin

    +0

    このエクササイズの範囲では、キャッシュの不変性は考慮されません。そして、私はそれについてもっと考えるほど、重要ではありません。本当に :) – b0rg

    1

    単にそこにあなたのtestMethodに欠けAttachされていません。

    // ... 
    using (var db = GetContext()) 
    { 
        var o = CreateOrder(); 
    
        db.Users.Attach(user); 
    
        o.OrderUser = user;   // attach user entity 
        o.UserId = user.UserID;  // attach by id 
        db.Orders.Add(o); 
        db.SaveChanges(); 
    } 
    // ... 
    

    そうでない場合はEFがAdded状態にユーザーを置きますコンテキストに注文を追加すると、新しいユーザーはSaveChangesに電話してください。

    +0

    ありがとうございました。アタッチを呼び出すことは、私が避けようとしていたものです。そして、私は、SaveChangesメソッドのすべての "Added"エンティティを追跡し、それらをコンテキストに強制的に追加するような他の可能性があるかどうかを調べようとしていました。 – b0rg

    +0

    @ b0rg:ええ、あなたは私の答えをあまりにも早く受け入れました。私は「アタッチ」と呼ぶ以外の方法はない、と言いたくはありませんでした。私はあなたがこれを正確に避けたいと思っていたことを知らなかった。おそらく 'SaveChanges'メソッドの' ???? 'のために' item.State = EntityState.Unchanged; 'のようなものが可能です。 (しかし、両方が「追加」状態にあるため、ユーザーとの順序を区別する必要があります)。しかし、なぜあなたが「Attach」を避けたいのかがわかりません。それは私にははるかに容易に見えます。 – Slauma

    +0

    私はまだこれに新しいです。簡単なやり方がないというあなたの答えは正しいです。そして、私はたくさんのコードを読むあなたの努力のために+1したかったのです。私たちのアプリでは、注文とユーザーではないが、20以上のキャッシュされた "辞書"オブジェクトを持ち、毎回それらのすべてをアタッチしている大きなオブジェクトがちょっと退屈なので、アタッチを避ける主な理由: – b0rg

    関連する問題