更新行,如果它存在其他插入逻辑与实体框架

时间:2011-04-05 20:06:22

标签: c# entity-framework

有没有人对使用Entity Framework实现“更新行(如果存在其他插入”逻辑)的最有效方法有建议?

11 个答案:

答案 0 :(得分:151)

如果您正在处理附加对象(从上下文的同一实例加载的对象),您只需使用:

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();

答案 1 :(得分:32)

从Entity Framework 4.3开始,命名空间AddOrUpdate有一个System.Data.Entity.Migrations方法:

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

doc

  

调用SaveChanges时按键添加或更新实体。当量   从数据库术语到“upsert”操作。这种方法可以   在使用迁移播种数据时非常有用。


要回答comment by @Smashing1978,我会粘贴@Colin

提供的链接中的相关部分
  

AddOrUpdate的工作是确保您不创建重复项   当你在开发过程中播种数据时。

     

首先,它将在您的数据库中执行查询以查找记录   你作为一个键提供的任何东西(第一个参数)匹配   AddOrUpdate中提供的映射列值(或值)。所以这   是一个松散的小鹅匹配,但播种非常好   设计时间数据。

     

更重要的是,如果找到匹配项,则更新将全部更新   并删除AddOrUpdate中没有的任何内容。

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

答案 2 :(得分:7)

如果您知道自己使用相同的上下文而不是分离任何实体,则可以制作如下通用版本:

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当然可以是一个类字段,或者该方法可以是静态的和扩展名,但这是基础知识。

答案 3 :(得分:5)

调用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(); 
    } 
}

More about EntityState

如果您无法检查Id = 0以确定它是否是新实体,请查看answer of Ladislav Mrnka

答案 4 :(得分:4)

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);
      }
  }
[....]

答案 5 :(得分:2)

在我看来,值得一提的是,通过新发布的EntityGraphOperations for Entity Framework Code First,您可以避免编写一些重复代码来定义图中所有实体的状态。我是这个产品的作者。我已将其发布在githubcode-project包括逐步演示,示例项目已准备好下载)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();
       });

另外,您知道在定义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 });
        }
    }

就是这样。

答案 6 :(得分:2)

插入其他更新

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();
}

答案 7 :(得分:1)

@LadislavMrnka答案的替代方法。如果是针对Entity Framework 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);
    }
}

答案 8 :(得分:0)

使用“任意”检查现有行。

    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();

        }

    }

答案 9 :(得分:0)

试试这个算法

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();
   }
}

答案 10 :(得分:-1)

更正

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();
        }