前段时间,我想实现一个能够确定是否对给定实体进行插入或更新的方法,因此我不必公开“插入”和“更新”方法,而只是一个简单的“插入或更新” ”。
确定实体是否是新实体的代码部分是:
public virtual T GetEntityByPrimaryKey<T>(T entity) where T : class
{
var entityType = entity.GetType();
var objectSet = ((IObjectContextAdapter)this.DatabaseContext).ObjectContext.CreateObjectSet<T>();
var keyNames = objectSet.EntitySet.ElementType.KeyMembers.Select(edmMember => edmMember.Name);
var keyValues = keyNames.Select(name => entityType.GetProperty(name).GetValue(entity, null)).ToArray();
return this.DatabaseContext.Set<T>().Find(keyValues);
}
InsertOrUpdate 方法是这样的:
public virtual T InsertOrUpdate<T>(T entity) where T : class
{
var databaseEntity = this.GetEntityByPrimaryKey(entity);
if (databaseEntity == null)
{
var entry = this.DatabaseContext.Entry(entity);
entry.State = EntityState.Added;
databaseEntity = entry.Entity;
}
else
{
this.DatabaseContext.Entry(databaseEntity).CurrentValues.SetValues(entity);
}
return databaseEntity;
}
现在,只要对象的“主键”由代码确定,这种方法就可以创造奇迹。有效的示例是 GUID、HI-LO 算法、自然键等。
然而,这对于“数据库生成的身份”场景来说是非常糟糕的,原因很简单:因为我在代码中的“Id”对于我要插入的所有对象都是 0,所以该方法将考虑它们相同。如果我添加 10 个对象,第一个将导致“新”,但接下来的九个将导致“已经存在”。这是因为 EF 的“查找”方法从 objectcontext 中读取数据,并且只有当它不存在时才会下到数据库进行查询。
在第一个对象之后,将跟踪 ID 为 0 的给定类型的实体。连续调用将导致“更新”,这是错误的。
现在,我知道数据库生成的 id 是邪恶的,绝对不适合任何 ORM,但我坚持使用这些方法,我需要修复此方法或完全删除它并退回到单独的“插入”和“更新”方法并将任务委托给调用者以确定要做什么。由于我们有一个高度解耦的解决方案,我宁愿避免这样做。
如果有人可以提供帮助并找到修复 GetEntityByPrimaryKey 方法的方法,那就太棒了。
谢谢。