在Entity Framework 4.1 Code First中附加缓存的断开连接的实体

时间:2011-07-05 14:13:33

标签: .net entity-framework-4.1 ef-code-first

我们有以下内容:使用UserId和User表订购。

假设我们要将所有User对象缓存为断开连接的实体,然后在ASP.NET或Web服务(如环境)中使用它们。

因为environmnet正在使用UnitOfWork和IOC框架,所以我们希望避免任何上下文操作。就像下面的单元测试一样:

  1. 获取断开连接的用户对象
  2. 创建背景
  3. 创建订单对象
  4. 通过用户对象或ID
  5. 附加用户
  6. 坚持整个Order对象。
  7. 经过两天激烈的谷歌搜索后,我找不到任何解决方案。

    问题是:

    1。用户对象

    如果我们附加了用户对象(userId = 1),那么EF会认为它是一个新的用户对象,并且尝试将新的用户对象保存到db中。这是因为db已经有userId == 1。

    可能的解决方案是覆盖DbContext.SaveChanges,尝试确定用户是否“已分离”并强制将其附加到上下文。我无法弄清楚如何做到这一点,因为从SaveChanges调用时,扩展方法EFExtensionMethods.IsAttached或EFExtensionMethods.AttachItem认为T是一个Object。

    2。 USER_ID

    这是有效的,除非您想在持久化和重新加载整个实体之前访问Order.User对象。

    除此之外,我们需要拆分API,以便仅使用对象的id(即Order.UserId)而不是实际对象(Order.User)进行保存。我们重新加载对象后,我们可以使用它们。但我看不出通过API实际执行它的方法。

    第3。两个用户对象和USER_ID

    在这种情况下,即使用户被标记为使用UserId作为外键,EF仍在尝试将User对象保存到上下文中,以解决1中描述的问题。

    看起来我缺少一些(或很多)基础知识,问题是:

    • 你会建议做什么?
    • 有没有很好的通用方法让EFExtensionMethods.IsAttached从DbContext.SaveChanges开始工作
    • 是否有一种很好的通用方法可以使EFExtensionMethods.AttachItem在DbContext.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;}
    }
    

2 个答案:

答案 0 :(得分:6)

我们已将标记接口添加到用户对象,并将所有ICacheableEntity entites标记为Entity。在SaveChanges中,我们只是将项目标记为未更改。这样就行了。

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;
            }
        }
    }
}

答案 1 :(得分: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时创建新用户。