【发布时间】:2011-09-28 20:55:13
【问题描述】:
我们有以下内容:带有 UserId 和 User 表的订单。
假设我们要将所有用户对象缓存为断开连接的实体,然后在 ASP.NET 或类似环境的 Web 服务中使用它们。
因为 environmnet 使用 UnitOfWork 和 IOC 框架,我们希望避免任何上下文操作。就像下面的单元测试一样:
- 获取断开连接的用户对象
- 创建上下文
- 创建订单对象
- 通过用户对象或 ID 附加用户
- 保留整个 Order 对象。
经过两天的疯狂谷歌搜索,我找不到任何解决方案。
问题是:
1.用户对象
如果我们附加用户对象(userId = 1),默认行为EF认为它是一个新的用户对象并试图将新的用户对象持久化到数据库中。这很糟糕,因为 db 已经有 userId == 1。
可能的解决方案是覆盖 DbContext.SaveChanges,尝试确定用户是否“分离”并强制将其附加到上下文。我不知道该怎么做,因为从 SaveChanges 调用扩展方法 EFExtensionMethods.IsAttached 或 EFExtensionMethods.AttachItem 认为 T 是一个对象。
2。 USER_ID
这可行,除非您想在持久化和重新加载整个实体之前访问 Order.User 对象。
除此之外,我们还需要拆分 API,以便仅使用对象的 id(即 Order.UserId)而不是实际对象(Order.User)进行保存。重新加载对象后,我们可以同时使用两者。但我看不到通过 API 实际执行它的方法。
3. USER OBJECT 和 USER_ID
在这种情况下,即使用户被标记为使用 UserId 作为外键,EF 仍会尝试将用户对象保存到上下文中,从而遇到 1 中描述的问题。
看起来我缺少一些(或很多)基本知识,问题是:
- 您会建议做什么?
- 是否有从 DbContext.SaveChanges 使 EFExtensionMethods.IsAttached 工作的好方法和通用方法
- 是否有使 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;}
}
【问题讨论】:
标签: .net entity-framework-4.1 ef-code-first