Answers:
如果您正在使用附加对象(从上下文的同一实例加载的对象),则可以简单地使用:
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();
using
。将上下文保留在内存中一段时间可以吗?例如,在Windows窗体的生命周期内?我通常会尝试清理数据库对象以确保最小的数据库负载。等待破坏我的EF上下文没有问题吗?
从Entity Framework 4.3开始,AddOrUpdate
在namespace处有一个方法System.Data.Entity.Migrations
:
public static void AddOrUpdate<TEntity>(
this IDbSet<TEntity> set,
params TEntity[] entities
)
where TEntity : class
由文档:
调用SaveChanges时,通过键添加或更新实体。等效于数据库术语中的“更新”操作。使用“迁移”为数据播种时,此方法很有用。
为了回答@ Smashing1978的评论,我将粘贴@Colin提供的链接中的相关部分。
AddOrUpdate的工作是确保在开发过程中播种数据时不创建重复项。
首先,它将在您的数据库中执行查询以查找一条记录,其中您提供的任何键(第一个参数)与AddOrUpdate中提供的映射列值(或多个值)相匹配。因此,对于匹配而言,这有点松散,但对于播种设计时间数据而言,则完全可以。
更重要的是,如果找到匹配项,则更新将全部更新,并将所有未包含在您的AddOrUpdate中的内容清除。
就是说,我遇到这样的情况,我正在从外部服务中提取数据并通过主键插入或更新现有值(并且我的本地消费者数据是只读的)- AddOrUpdate
在生产中已经使用了6个月以上,因此远没有问题。
调用时会发生魔力,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();
}
}
如果您无法检查Id = 0
以确定它是否是新实体,请检查Ladislav Mrnka的答案。
如果您知道您使用的是相同的上下文并且未分离任何实体,则可以制作如下的通用版本:
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
当然可以是一个类字段,也可以将方法设为静态和扩展,但这是基础。
Ladislav的答案很接近,但我必须进行一些修改才能使其在EF6中起作用(数据库优先)。我使用onAddOrUpdate方法扩展了数据上下文,到目前为止,这似乎可以与分离对象一起很好地工作:
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);
}
}
[....]
在我看来,值得一提的是,通过新发布的用于实体框架代码的EntityGraphOperations,您可以省去编写一些重复的代码来定义图中所有实体的状态的麻烦。我是该产品的作者。我已经在github,代码项目(包括分步演示和示例项目准备下载)和nuget中发布了它。
它将自动将实体的状态设置为Added
或Modified
。并且,如果不再存在,您将手动选择必须删除的实体。
这个例子:
假设我有一个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();
});
众所周知,唯一的键属性在定义Phone实体的状态时可能会发挥作用。出于此类特殊目的,我们有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 });
}
}
就这样。
插入其他同时更新
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();
}
使用“任何”检查现有行。
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();
}
}
@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);
}
}
已更正
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();
}