What happened to AddOrUpdate in EF 7 / Core?

后端 未结 11 1278
暖寄归人
暖寄归人 2020-12-16 09:30

I\'m writing a seed method using EntityFramework.Core 7.0.0-rc1-final.

What happened to the AddOrUpdate method of DbSet?

11条回答
  •  一个人的身影
    2020-12-16 10:10

    None of the answers worked for me using Entity Framework Core (2.0) so here's the solution that worked for me:

    public static class DbSetExtensions
    {
    
        public static void AddOrUpdate(this DbSet dbSet, Expression> identifierExpression, params T[] entities) where T : class
        {
            foreach (var entity in entities)
                AddOrUpdate(dbSet, identifierExpression, entity);
        }
    
    
        public static void AddOrUpdate(this DbSet dbSet, Expression> identifierExpression, T entity) where T : class
        {
            if (identifierExpression == null)
                throw new ArgumentNullException(nameof(identifierExpression));
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));
    
            var keyObject = identifierExpression.Compile()(entity);
            var parameter = Expression.Parameter(typeof(T), "p");
    
            var lambda = Expression.Lambda>(
                Expression.Equal(
                    ReplaceParameter(identifierExpression.Body, parameter),
                    Expression.Constant(keyObject)),
                parameter);
    
            var item = dbSet.FirstOrDefault(lambda.Compile());
            if (item == null)
            {
                // easy case
                dbSet.Add(entity);
            }
            else
            {
                // get Key fields, using KeyAttribute if possible otherwise convention
                var dataType = typeof(T);
                var keyFields = dataType.GetProperties().Where(p => p.GetCustomAttribute() != null).ToList();
                if (!keyFields.Any())
                {
                    string idName = dataType.Name + "Id";
                    keyFields = dataType.GetProperties().Where(p => 
                        string.Equals(p.Name, "Id", StringComparison.OrdinalIgnoreCase) || 
                        string.Equals(p.Name, idName, StringComparison.OrdinalIgnoreCase)).ToList();
                }
    
                // update all non key and non collection properties
                foreach (var p in typeof(T).GetProperties().Where(p => p.GetSetMethod() != null && p.GetGetMethod() != null))
                {
                    // ignore collections
                    if (p.PropertyType != typeof(string) && p.PropertyType.GetInterface(nameof(System.Collections.IEnumerable)) != null)
                        continue;
    
                    // ignore ID fields
                    if (keyFields.Any(x => x.Name == p.Name))
                        continue;
    
                    var existingValue = p.GetValue(entity);
                    if (!Equals(p.GetValue(item), existingValue))
                    {
                        p.SetValue(item, existingValue);
                    }
                }
    
                // also update key values on incoming data item where appropriate
                foreach (var idField in keyFields.Where(p => p.GetSetMethod() != null && p.GetGetMethod() != null))
                {
                    var existingValue = idField.GetValue(item);
                    if (!Equals(idField.GetValue(entity), existingValue))
                    {
                        idField.SetValue(entity, existingValue);
                    }
                }
            }
        }
    
    
        private static Expression ReplaceParameter(Expression oldExpression, ParameterExpression newParameter)
        {
            switch (oldExpression.NodeType)
            {
                case ExpressionType.MemberAccess:
                    var m = (MemberExpression)oldExpression;
                    return Expression.MakeMemberAccess(newParameter, m.Member);
                case ExpressionType.New:
                    var newExpression = (NewExpression)oldExpression;
                    var arguments = new List();
                    foreach (var a in newExpression.Arguments)
                        arguments.Add(ReplaceParameter(a, newParameter));
                    var returnValue = Expression.New(newExpression.Constructor, arguments.ToArray());
                    return returnValue;
                default:
                    throw new NotSupportedException("Unknown expression type for AddOrUpdate: " + oldExpression.NodeType);
            }
        }
    }
    

    You may need to update the ReplaceParameter() method if you have a more complicated identifierExpression. Simple property accessors will work fine with this implementation. e.g.:

    context.Projects.AddOrUpdate(x => x.Name, new Project { ... })
    context.Projects.AddOrUpdate(x => new { x.Name, x.Description }, new Project { ... })
    

    Then context.SaveChanges() will commit the data to the database

提交回复
热议问题