更新行,如果它存在,否则插入逻辑实体框架

使用实体框架实现如果行存在,则更新该行,否则插入新行逻辑的最有效方法是什么?或者这有什么规律吗?

221394 次浏览

如果你正在使用附加对象(从上下文的同一个实例加载的对象),你可以简单地使用:

if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
context.MyEntities.AddObject(myEntity);
}


// Attached object tracks modifications automatically


context.SaveChanges();

如果你可以使用任何关于对象键的知识,你可以使用这样的东西:

if (myEntity.Id != 0)
{
context.MyEntities.Attach(myEntity);
context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
context.MyEntities.AddObject(myEntity);
}


context.SaveChanges();

如果你不能通过Id判断对象是否存在,你必须执行查找查询:

var id = myEntity.Id;
if (context.MyEntities.Any(e => e.Id == id))
{
context.MyEntities.Attach(myEntity);
context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
context.MyEntities.AddObject(myEntity);
}


context.SaveChanges();

如果你知道你正在使用相同的上下文并且没有分离任何实体,你可以像这样创建一个通用版本:

public void InsertOrUpdate<T>(T entity, DbContext db) where T : class
{
if (db.Entry(entity).State == EntityState.Detached)
db.Set<T>().Add(entity);


// If an immediate save is needed, can be slow though
// if iterating through many entities:
db.SaveChanges();
}

db当然可以是一个类字段,或者方法可以是静态的和一个扩展,但这是基本的。

从实体框架4.3开始,在命名空间System.Data.Entity.Migrations处有一个AddOrUpdate方法:

public static void AddOrUpdate<TEntity>(
this IDbSet<TEntity> set,
params TEntity[] entities
)
where TEntity : class

通过医生:

当调用SaveChanges时按键添加或更新实体。等效 到数据库术语中的“upsert”操作。此方法可以是


为了回答@Smashing1978,我将从@Colin提供的链接中粘贴相关部分

AddOrUpdate的任务是确保您不会创建副本

首先,它将在数据库中执行查询以查找记录 您提供的键(第一个参数)与 AddOrUpdate提供的映射列值(或多个值)。所以这 对于匹配来说有点松散,但对于播种来说完全没问题

.设计时间数据 更重要的是,如果找到匹配,则更新将更新所有 并排除任何不在你的AddOrUpdate

也就是说,我有一种情况,我从外部服务中提取数据,并通过主键插入或更新现有值(并且我的本地消费者数据是只读的)-在生产中使用AddOrUpdate已经超过6个月了,到目前为止没有问题。

Ladislav的答案很接近,但我不得不做了一些修改,以使其在EF6(数据库优先)中工作。我用我的AddOrUpdate方法扩展了我的数据上下文,到目前为止,这似乎与分离的对象工作得很好:

using System.Data.Entity;


[....]


public partial class MyDBEntities {


public void AddOrUpdate(MyDBEntities ctx, DbSet set, Object obj, long ID) {
if (ID != 0) {
set.Attach(obj);
ctx.Entry(obj).State = EntityState.Modified;
}
else {
set.Add(obj);
}
}
[....]

神奇的是在调用SaveChanges()时发生的,取决于当前的EntityState。如果实体有EntityState.Added,它将被添加到数据库中,如果它有EntityState.Modified,它将在数据库中被更新。所以你可以像下面这样实现InsertOrUpdate()方法:

public void InsertOrUpdate(Blog blog)
{
using (var context = new BloggingContext())
{
context.Entry(blog).State = blog.BlogId == 0 ?
EntityState.Added :
EntityState.Modified;


context.SaveChanges();
}
}

更多关于EntityState的信息

如果你不能检查Id = 0来确定它是否是一个新实体,检查Ladislav Mrnka的回答. c。

在我看来,值得说的是,有了新发布的实体框架代码的EntityGraphOperations,你可以节省自己编写一些重复的代码来定义图中所有实体的状态。我是这个产品的作者。我已经在github代码项目 (包括一步一步的演示和示例项目,可供下载)nuget

它将自动设置实体的状态转换为AddedModified。如果它不再存在,您将手动选择必须删除哪些实体。

示例:

假设我有一个Person对象。Person可以有很多电话,一个文档,可以有一个配偶。

public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public int Age { get; set; }
public int DocumentId {get; set;}


public virtual ICollection<Phone> Phones { get; set; }
public virtual Document Document { get; set; }
public virtual PersonSpouse PersonSpouse { get; set; }
}

我想确定图中包含的所有实体的状态。

context.InsertOrUpdateGraph(person)
.After(entity =>
{
// Delete missing phones.
entity.HasCollection(p => p.Phones)
.DeleteMissingEntities();


// Delete if spouse is not exist anymore.
entity.HasNavigationalProperty(m => m.PersonSpouse)
.DeleteIfNull();
});

另外,如你所知,在定义电话实体的状态时,唯一的键属性可以发挥作用。出于这种特殊目的,我们有ExtendedEntityTypeConfiguration<>类,它继承自EntityTypeConfiguration<>。如果要使用这样的特殊配置,则必须从ExtendedEntityTypeConfiguration<>继承映射类,而不是EntityTypeConfiguration<>。例如:

public class PhoneMap: ExtendedEntityTypeConfiguration<Phone>
{
public PhoneMap()
{
// Primary Key
this.HasKey(m => m.Id);
…
// Unique keys
this.HasUniqueKey(m => new { m.Prefix, m.Digits });
}
}

这是所有。

插入else更新

public void InsertUpdateData()
{
//Here TestEntities is the class which is given from "Save entity connection setting in web.config"
TestEntities context = new TestEntities();


var query = from data in context.Employee
orderby data.name
select data;


foreach (Employee details in query)
{
if (details.id == 1)
{
//Assign the new values to name whose id is 1
details.name = "Sanjay";
details. Surname="Desai";
details.address=" Desiwadi";
}
else if(query==null)
{
details.name="Sharad";
details.surname=" Chougale ";
details.address=" Gargoti";
}
}


//Save the changes back to database.
context.SaveChanges();
}

@LadislavMrnka的替代答案。这是实体框架6.2.0。

如果你有一个特定的DbSet和一个需要更新或创建的项:

var name = getNameFromService();


var current = _dbContext.Names.Find(name.BusinessSystemId, name.NameNo);
if (current == null)
{
_dbContext.Names.Add(name);
}
else
{
_dbContext.Entry(current).CurrentValues.SetValues(name);
}
_dbContext.SaveChanges();

然而,这也可以用于具有单个主键或复合主键的泛型DbSet

var allNames = NameApiService.GetAllNames();
GenericAddOrUpdate(allNames, "BusinessSystemId", "NameNo");


public virtual void GenericAddOrUpdate<T>(IEnumerable<T> values, params string[] keyValues) where T : class
{
foreach (var value in values)
{
try
{
var keyList = new List<object>();


//Get key values from T entity based on keyValues property
foreach (var keyValue in keyValues)
{
var propertyInfo = value.GetType().GetProperty(keyValue);
var propertyValue = propertyInfo.GetValue(value);
keyList.Add(propertyValue);
}


GenericAddOrUpdateDbSet(keyList, value);
//Only use this when debugging to catch save exceptions
//_dbContext.SaveChanges();
}
catch
{
throw;
}
}
_dbContext.SaveChanges();
}


public virtual void GenericAddOrUpdateDbSet<T>(List<object> keyList, T value) where T : class
{
//Get a DbSet of T type
var someDbSet = Set(typeof(T));


//Check if any value exists with the key values
var current = someDbSet.Find(keyList.ToArray());
if (current == null)
{
someDbSet.Add(value);
}
else
{
Entry(current).CurrentValues.SetValues(value);
}
}

使用Any检查现有行。

public static void insertOrUpdateCustomer(Customer customer)
{
using (var db = getDb())
{


db.Entry(customer).State = !db.Customer.Any(f => f.CustomerId == customer.CustomerId) ? EntityState.Added : EntityState.Modified;
db.SaveChanges();


}


}

纠正

public static void InsertOrUpdateRange<T, T2>(this T entity, List<T2> updateEntity)
where T : class
where T2 : class
{
foreach(var e in updateEntity)
{
context.Set<T2>().InsertOrUpdate(e);
}
}




public static void InsertOrUpdate<T, T2>(this T entity, T2 updateEntity)
where T : class
where T2 : class
{
if (context.Entry(updateEntity).State == EntityState.Detached)
{
if (context.Set<T2>().Any(t => t == updateEntity))
{
context.Set<T2>().Update(updateEntity);
}
else
{
context.Set<T2>().Add(updateEntity);
}


}
context.SaveChanges();
}

试试这个算法

public void InsertOrUpdate(Item item)
{
using (var context = new ItemContext())
{
var existedItem = context.Items.Where(x => x.Id==item.Id).FirstOrDefault();


if(existedItem != null)
{
context.Entry(existedItem).CurrentValues.SetValues(item);
          

//or only if you want to update some special properties
existedItem.Prop1=item.Prop1;
existedItem.Prop2=item.Prop2
context.Entry(existedItem).State =EntityState.Modified;
//-----------


}
else
{
context.Items.Add(item);
}


context.SaveChanges();
}
}