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

b0r*_*0rg 3 .net ef-code-first entity-framework-4.1

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

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

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

  1. 获取断开连接的用户对
  2. 创建上下文
  3. 创建订单对象
  4. 通过用户对象或id附加用户
  5. 保留整个Order对象.

经过两天激烈的谷歌搜索,我找不到任何解决方案.

问题是:

1.用户对象

如果我们附加用户对象(使用userId = 1),则使用默认行为,EF认为它是新的用户对象,并且正在尝试将新用户对象持久保存到db中.这是因为db已经有userId == 1.

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

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));
}
Run Code Online (Sandbox Code Playgroud)

上下文和类:

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;}
}
Run Code Online (Sandbox Code Playgroud)

b0r*_*0rg 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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)