我们有以下内容:使用UserId和User表订购。
假设我们要将所有User对象缓存为断开连接的实体,然后在ASP.NET或Web服务(如环境)中使用它们。
因为environmnet正在使用UnitOfWork和IOC框架,所以我们希望避免任何上下文操作。就像下面的单元测试一样:
经过两天激烈的谷歌搜索后,我找不到任何解决方案。
问题是:
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中描述的问题。
看起来我缺少一些(或很多)基础知识,问题是:
非常感谢任何帮助。
[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;}
}
答案 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
时创建新用户。