我正在努力完成一些非常简单的事情,但我无法使用Entity Framework 4.1找到它。
我想要一个接受对象然后执行UPSERT的控制器方法(根据数据库中是否存在记录,插入或更新)。
我使用的是自然键,所以我无法查看我的POCO并判断它是否是新的。
这就是我这样做的方式,对我来说似乎不对:
[HttpPost]
public JsonResult SaveMyEntity(MyEntity entity)
{
MyContainer db = new MyContainer(); // DbContext
if (ModelState.IsValid)
{
var existing =
db.MyEntitys.Find(entity.MyKey);
if (existing == null)
{
db.MyEntitys.Add(entity);
}
else
{
existing.A = entity.A;
existing.B = entity.B;
db.Entry(existing).State = EntityState.Modified;
}
db.SaveChanges();
return Json(new { Result = "Success" });
}
}
理想情况下,整件事情就是这样:
db.MyEntities.AddOrModify(entity);
答案 0 :(得分:21)
不幸的是,如果不查询数据库或使用存储过程,就无法执行此操作。简约代码应为:
public void AddOrModify<T>(T entity, string key) where T : class, IEntity // Implements MyKey
{
using (var context = new MyContainer())
{
if (context.Set<T>().Any(e => e.MyKey == key))
{
context.Entry(entity).State = EntityState.Modified;
}
else
{
context.Entry(entity).State = EntityState.Added;
}
context.SaveChanges();
}
}
答案 1 :(得分:2)
在大多数情况下,除非您已禁用更改跟踪,否则无需明确设置EntityState.Modified
。
我们采取的解决方案是检查实体标识符的值:
if (entity.Id == default(int)) {
// transient entity so insert
} else {
// update
}
答案 2 :(得分:2)
实际上有一种信息数据库上下文的方法,您尝试插入的实体已更改,现在是新的实体
_context.MyEntity.Attach(entity);
_context.MyEntity(entity).State = System.Data.EntityState.Modified;
答案 3 :(得分:2)
要执行UPSERT操作,您可能需要考虑创建执行MERGE的SP。
无论你选择哪种方式,操作必须是原子的,否则你就会遇到竞争条件。 你的SP可能需要一个HOLDLOCK来取消这个......
http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
答案 4 :(得分:0)
我知道这个问题已经过时并且有一个可接受的答案,但我认为有一个更好的解决方案:它不需要实现额外的接口或定义键类型。
public static class DbSetExtensions
{
public static EntityEntry<TEnt> AddIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, TEnt entity, Func<TEnt, TKey> predicate) where TEnt : class
{
var exists = dbSet.Any(c => predicate(entity).Equals(predicate(c)));
return exists
? null
: dbSet.Add(entity);
}
public static void AddRangeIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, IEnumerable<TEnt> entities, Func<TEnt, TKey> predicate) where TEnt : class
{
var entitiesExist = from ent in dbSet
where entities.Any(add => predicate(ent).Equals(predicate(add)))
select ent;
dbSet.AddRange(entities.Except(entitiesExist));
}
}
所以稍后它可以像这样使用:
using (var context = new MyDbContext())
{
var user1 = new User { Name = "Peter", Age = 32 };
context.Users.AddIfNotExists(user1, u => u.Name);
var user2 = new User { Name = "Joe", Age = 25 };
context.Users.AddIfNotExists(user2, u => u.Age);
// Adds user1 if there is no user with name "Peter"
// Adds user2 if there is no user with age 25
context.SaveChanges();
}