实体框架添加如果没有更新则不存在

时间:2015-07-01 13:20:09

标签: c# entity-framework

我喜欢AddOrUpdate允许您指定要检查的过滤器以避免添加重复项的事实。但我想要没有更新的类似功能。

现在我做这样的事情:

var checkProfile = from p in db.Profile
    where p => p.LastName == newProfile.lastName
         && p => p.FirstName == newProfile.firstName
         && p => p.Middle== newProfile.middle
    select p;
if (checkProfile.FirstOrDefault() == null)
{
    db.Profile.Add(newProfile);
    db.SaveChanges();
}

我知道我可以这样做:

db.Profile.AddOrUpdate(p => new {p.LastName, p.FirstName, p.Middle}, newProfile);
db.SaveChanges();

但我宁愿在这种情况下跳过修改数据。

第一个例子做了我想要的但有更多代码。在第一个例子中,有没有更简单/更清洁的方法来做我想要的事情?

更新

我喜欢Ognyan Dimitrov的建议。我正在努力实现它。我的模型继承自BaseEntity。我可以在那里放一个通用版本吗?

我的模型已定义:

public class Address :BaseEntity
{

我的BaseEntity:

public class BaseEntity 
{
    public virtual T AddIfNotExists<T>(T entity, Expression<Func<T, bool>> predicate = null)
    {
        var exists = predicate != null ? DbSet.Any(predicate) : DbSet.Any();
        return !exists ? DbSet.Add(entity) : null;
    }
}

我收到Any(...)和Add(...)的错误。 Add(...)的错误是'非静态字段,方法或属性'System.Data.Entity.DbSet.Add(object)''

需要对象引用

我应该使用这个。添加(对象)?

更新2:

我已经创建了这段代码:

public static class DbSetExtensions
{
    public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
    {
        var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
        return !exists ? dbSet.Add(entity) : null;
    }

}

现在我试着这样称呼它,但这不正确。原谅我缺乏理解。

_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
            pi => new {pi.ProfileId, pi.ProfileIdentifierTypeId, pi.ProfileIdentifierValue});

更新 - 解决方案:

我可以像这样调用DbSetextensions:

_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
            pi => pi.ProfileId == profileId &&  
            pi.ProfileIdentifierTypeId == (int)type &&  
            pi.ProfileIdentifierValue == value);

非常感谢与我合作Ognyan !!!

7 个答案:

答案 0 :(得分:32)

您是否尝试检查实体是否存在,如果不存在 - 添加它?像这样:

<强>更新

class Blah < ActiveRecord::Base
  scope :no_scope, -> {all}
  scope :for_user, -> (user_id) {where(user_id: user_id)} 

end


Blah.no_scope.for_user(1) #  where(user_id: 1)

您可以直接使用此方法,并记住在调用后调用DbContext.SaveChanges()。

答案 1 :(得分:6)

我用了类似的东西,阅读这两篇帖子来制作我的代码。 我希望帮助那些需要与AddOrUpdate类似签名的人。

Entity Framework Add if not exist without update

Making AddOrUpdate change only some properties

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace System.Data.Entity.Migrations
{
    //
    // Summary:
    //     Metodos de extensão para System.Data.Entity.IDbSet
    public static class DbSetMigrationsGustavoExtensions
    {
        /// <summary>
        /// Adiciona uma entidade se ela não existe ainda
        /// Assinatura semelhante ao AddOrUpdate
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="set">Set onde serão adicionadas as entidades</param>
        /// <param name="identifierExpression">Campos usados na comparação</param>
        /// <param name="entities">Entidades para adicionar</param>
        public static void AddIfNotExists<TEntity>(this IDbSet<TEntity> set, Expression<Func<TEntity, object>> identifierExpression, params TEntity[] entities) where TEntity : class
        {

            var identifyingProperties = GetProperties<TEntity>(identifierExpression).ToList();
            var parameter = Expression.Parameter(typeof(TEntity));
            foreach (var entity in entities)
            {
                var matches = identifyingProperties.Select(pi => Expression.Equal(Expression.Property(parameter, pi.Name), Expression.Constant(pi.GetValue(entity, null))));
                var matchExpression = matches.Aggregate<BinaryExpression, Expression>(null, (agg, v) => (agg == null) ? v : Expression.AndAlso(agg, v));

                var predicate = Expression.Lambda<Func<TEntity, bool>>(matchExpression, new[] { parameter });
                if (!set.Any(predicate))
                {
                    set.Add(entity);
                }
            }
        }

        private static IEnumerable<PropertyInfo> GetProperties<T>(Expression<Func<T, object>> exp) where T : class
        {
            Debug.Assert(exp != null);
            Debug.Assert(exp.Body != null);
            Debug.Assert(exp.Parameters.Count == 1);

            var type = typeof(T);
            var properties = new List<PropertyInfo>();

            if (exp.Body.NodeType == ExpressionType.MemberAccess)
            {
                var memExp = exp.Body as MemberExpression;
                if (memExp != null && memExp.Member != null)
                    properties.Add(type.GetProperty(memExp.Member.Name));
            }
            else if (exp.Body.NodeType == ExpressionType.Convert)
            {
                var unaryExp = exp.Body as UnaryExpression;
                if (unaryExp != null)
                {
                    var propExp = unaryExp.Operand as MemberExpression;
                    if (propExp != null && propExp.Member != null)
                        properties.Add(type.GetProperty(propExp.Member.Name));
                }
            }
            else if (exp.Body.NodeType == ExpressionType.New)
            {
                var newExp = exp.Body as NewExpression;
                if (newExp != null)
                    properties.AddRange(newExp.Members.Select(x => type.GetProperty(x.Name)));
            }

            return properties.OfType<PropertyInfo>();
        }

        /// <summary>
        /// Faz um set.Any(predicate)
        /// Se não existe nada no set então adiciona
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="set">Set onde será adicionada a entidade</param>
        /// <param name="predicate">Condição (exemplo: dbUser => dbUser.Nome == "Gustavo")</param>
        /// <param name="entity">Entidade para adicionar</param>
        /// <returns></returns>
        public static T AddIfNotExists<T>(this IDbSet<T> set, Expression<Func<T, bool>> predicate, T entity) where T : class, new()
        {
            return !set.Any(predicate) ? set.Add(entity) : null;
        }
    }
}

答案 2 :(得分:5)

引自MSDN EF article

  

插入或更新模式

     

某些应用程序的常见模式是将实体添加为   new(导致数据库插入)或将实体附加为现有实体   并将其标记为已修改(导致数据库更新),具体取决于   主键的值。例如,使用数据库时   生成的整数主键通常用一个实体处理一个实体   零密钥作为新密钥和具有非零密钥的实体作为现有密钥。这个   可以通过基于检查设置实体状态来实现模式   主键值。

     

请注意,当您将状态更改为已修改的所有属性时   该实体将被标记为已修改且所有属性值都将被标记为   调用SaveChanges时发送到数据库。

context.Entry(blog).State = blog.BlogId == 0 ? 
                           EntityState.Added : 
                           EntityState.Modified; 

context.SaveChanges(); 

答案 3 :(得分:4)

解决方案是可以的,当你只需要添加一个项目时,如果你必须添加多个项目,它在性能方面非常昂贵。 我认为有更好的解决方案:

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

答案 4 :(得分:2)

所有其他答案都不正确。

“先写后读”可能会违反数据完整性,而不会放入事务控件中。

在SQL Server中,可以使用merge语句。但是,合并语句在EF中不可用。

答案 5 :(得分:1)

唯一想到的是使用IEqualityComparer<T>,但这并没有真正停止工作,只是将其抽象出来并创建更清晰的代码。

答案 6 :(得分:1)

对我有用的是:

public static void AddIfNotExists<T>(this DbSet<T> dbSet, Func<T, object> predicate, params T [] entities) where T : class, new()
{
    foreach (var entity in entities)
    {
        var newValues = predicate.Invoke(entity);
        Expression<Func<T, bool>> compare = arg => predicate(arg).Equals(newValues);
        var compiled = compare.Compile();
        var existing = dbSet.FirstOrDefault(compiled);
        if (existing == null)
        {
            dbSet.Add(entity);
        }
    }
}