Entity framework core EF 7/Core中的AddOrUpdate发生了什么?
我正在使用EntityFramework.Core 7.0.0-rc1-final编写一个种子方法Entity framework core EF 7/Core中的AddOrUpdate发生了什么?,entity-framework-core,Entity Framework Core,我正在使用EntityFramework.Core 7.0.0-rc1-final编写一个种子方法 DbSet的AddOrUpdate方法发生了什么变化?它正在等待实现。请参阅问题& 更新:根据下面的评论(未经验证)-此功能最终将在.NETCore2.1中发布 您可以使用我创建的扩展方法修补代码库,以便迁移到EF Core: public static void AddOrUpdate<T>(this DbSet<T> dbSet, T data) where T
DbSet的AddOrUpdate方法发生了什么变化?它正在等待实现。请参阅问题&
更新:根据下面的评论(未经验证)-此功能最终将在.NETCore2.1中发布 您可以使用我创建的扩展方法修补代码库,以便迁移到EF Core:
public static void AddOrUpdate<T>(this DbSet<T> dbSet, T data) where T : class
{
var t = typeof(T);
PropertyInfo keyField = null;
foreach (var propt in t.GetProperties())
{
var keyAttr = propt.GetCustomAttribute<KeyAttribute>();
if (keyAttr != null)
{
keyField = propt;
break; // assume no composite keys
}
}
if (keyField == null)
{
throw new Exception($"{t.FullName} does not have a KeyAttribute field. Unable to exec AddOrUpdate call.");
}
var keyVal = keyField.GetValue(data);
var dbVal = dbSet.Find(keyVal);
if (dbVal != null)
{
dbSet.Update(data);
return;
}
dbSet.Add(data);
}
publicstaticvoidaddorupdate(此DbSet DbSet,T数据),其中T:class
{
var t=类型(t);
PropertyInfo关键字字段=null;
foreach(t.GetProperties()中的var propt)
{
var keyAttr=propt.GetCustomAttribute();
if(keyAttr!=null)
{
keyField=propt;
break;//假定没有组合键
}
}
if(keyField==null)
{
抛出新异常($“{t.FullName}没有KeyAttribute字段。无法执行AddOrUpdate调用。”);
}
var keyVal=keyField.GetValue(数据);
var dbVal=dbSet.Find(keyVal);
if(dbVal!=null)
{
更新(数据);
返回;
}
添加(数据);
}
我想这就是你想要的
public static class DbSetExtension
{
public static void AddOrUpdate<T>(this DbSet<T> dbSet, T data) where T : class
{
var context = dbSet.GetContext();
var ids = context.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties.Select(x => x.Name);
var t = typeof(T);
List<PropertyInfo> keyFields = new List<PropertyInfo>();
foreach (var propt in t.GetProperties())
{
var keyAttr = ids.Contains(propt.Name);
if (keyAttr)
{
keyFields.Add(propt);
}
}
if (keyFields.Count <= 0)
{
throw new Exception($"{t.FullName} does not have a KeyAttribute field. Unable to exec AddOrUpdate call.");
}
var entities = dbSet.AsNoTracking().ToList();
foreach (var keyField in keyFields)
{
var keyVal = keyField.GetValue(data);
entities = entities.Where(p => p.GetType().GetProperty(keyField.Name).GetValue(p).Equals(keyVal)).ToList();
}
var dbVal = entities.FirstOrDefault();
if (dbVal != null)
{
context.Entry(dbVal).CurrentValues.SetValues(data);
context.Entry(dbVal).State = EntityState.Modified;
return;
}
dbSet.Add(data);
}
public static void AddOrUpdate<T>(this DbSet<T> dbSet, Expression<Func<T, object>> key, T data) where T : class
{
var context = dbSet.GetContext();
var ids = context.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties.Select(x => x.Name);
var t = typeof(T);
var keyObject = key.Compile()(data);
PropertyInfo[] keyFields = keyObject.GetType().GetProperties().Select(p=>t.GetProperty(p.Name)).ToArray();
if (keyFields == null)
{
throw new Exception($"{t.FullName} does not have a KeyAttribute field. Unable to exec AddOrUpdate call.");
}
var keyVals = keyFields.Select(p => p.GetValue(data));
var entities = dbSet.AsNoTracking().ToList();
int i = 0;
foreach (var keyVal in keyVals)
{
entities = entities.Where(p => p.GetType().GetProperty(keyFields[i].Name).GetValue(p).Equals(keyVal)).ToList();
i++;
}
if (entities.Any())
{
var dbVal = entities.FirstOrDefault();
var keyAttrs =
data.GetType().GetProperties().Where(p => ids.Contains(p.Name)).ToList();
if (keyAttrs.Any())
{
foreach (var keyAttr in keyAttrs)
{
keyAttr.SetValue(data,
dbVal.GetType()
.GetProperties()
.FirstOrDefault(p => p.Name == keyAttr.Name)
.GetValue(dbVal));
}
context.Entry(dbVal).CurrentValues.SetValues(data);
context.Entry(dbVal).State = EntityState.Modified;
return;
}
}
dbSet.Add(data);
}
}
public static class HackyDbSetGetContextTrick
{
public static DbContext GetContext<TEntity>(this DbSet<TEntity> dbSet)
where TEntity : class
{
return (DbContext)dbSet
.GetType().GetTypeInfo()
.GetField("_context", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(dbSet);
}
}
公共静态类DbSetExtension
{
公共静态void AddOrUpdate(此DbSet DbSet,T data),其中T:class
{
var context=dbSet.GetContext();
var id=context.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties.Select(x=>x.Name);
var t=类型(t);
List keyFields=新列表();
foreach(t.GetProperties()中的var propt)
{
var keyAttr=ids.Contains(propt.Name);
if(keyAttr)
{
keyFields.Add(propt);
}
}
if(keyFields.Count p.GetType().GetProperty(keyField.Name).GetValue(p).Equals(keyVal)).ToList();
}
var dbVal=entities.FirstOrDefault();
if(dbVal!=null)
{
context.Entry(dbVal).CurrentValues.SetValues(数据);
context.Entry(dbVal.State=EntityState.Modified;
返回;
}
添加(数据);
}
公共静态void AddOrUpdate(此DbSet DbSet,表达式键,T数据),其中T:class
{
var context=dbSet.GetContext();
var id=context.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties.Select(x=>x.Name);
var t=类型(t);
var keyObject=key.Compile()(数据);
PropertyInfo[]keyFields=keyObject.GetType().GetProperties().Select(p=>t.GetProperty(p.Name)).ToArray();
if(keyFields==null)
{
抛出新异常($“{t.FullName}没有KeyAttribute字段。无法执行AddOrUpdate调用。”);
}
var-keyVals=keyFields.Select(p=>p.GetValue(数据));
var entities=dbSet.AsNoTracking().ToList();
int i=0;
foreach(在keyVals中的var keyVal)
{
entities=entities.Where(p=>p.GetType().GetProperty(keyFields[i].Name).GetValue(p).Equals(keyVal)).ToList();
i++;
}
if(entities.Any())
{
var dbVal=entities.FirstOrDefault();
var-keyAttrs=
data.GetType().GetProperties().Where(p=>ids.Contains(p.Name)).ToList();
if(keyAttrs.Any())
{
foreach(keyAttrs中的var keyAttr)
{
keyAttr.SetValue(数据,
dbVal.GetType()
.GetProperties()
.FirstOrDefault(p=>p.Name==keyAttr.Name)
.GetValue(dbVal));
}
context.Entry(dbVal).CurrentValues.SetValues(数据);
context.Entry(dbVal.State=EntityState.Modified;
返回;
}
}
添加(数据);
}
}
公共静态类HackyDbSetGetContextTrick
{
公共静态DbContext GetContext(此DbSet DbSet)
地点:班级
{
返回(DbContext)dbSet
.GetType().GetTypeInfo()
.GetField(“_context”,BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(dbSet);
}
}
我认为,如果假设基本实体类是一个合法选项,那么这个解决方案是这个问题的一个更简单的解决方案。简单性来自于实现DomainEntityBase的域实体,这减轻了其他建议解决方案中的许多复杂性
public static class DbContextExtensions
{
public static void AddOrUpdate<T>(this DbSet<T> dbSet, IEnumerable<T> records)
where T : DomainEntityBase
{
foreach (var data in records)
{
var exists = dbSet.AsNoTracking().Any(x => x.Id == data.Id);
if (exists)
{
dbSet.Update(data);
continue;
}
dbSet.Add(data);
}
}
}
public class DomainEntityBase
{
[Key]
public Guid Id { get; set; }
}
公共静态类DbContextensions
{
公共静态void AddOrUpdate(此DbSet DbSet,IEnumerable记录)
其中T:DomainEntityBase
{
foreach(记录中的var数据)
{
var exists=dbSet.AsNoTracking().Any(x=>x.Id==data.Id);
如果(存在)
{
更新(数据);
继续;
}
添加(数据);
}
}
}
公共类DomainEntityBase
{
[关键]
公共Guid Id{get;set;}
}
我找到了一个很好的解决方案,允许您指定应该匹配的属性。但是,它不需要单个实体,而是每个调用中的一个列表。它可能会给你一些提示,告诉你如何实现一个更好的版本,就像旧版本一样
(代码不是我的)有一个扩展方法
使用Entity Framework Core(2.0)时,没有一个答案对我有效,因此以下是对我有效的解决方案:
public static class DbSetExtensions
{
public static void AddOrUpdate<T>(this DbSet<T> dbSet, Expression<Func<T, object>> identifierExpression, params T[] entities) where T : class
{
foreach (var entity in entities)
AddOrUpdate(dbSet, identifierExpression, entity);
}
public static void AddOrUpdate<T>(this DbSet<T> dbSet, Expression<Func<T, object>> 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<Func<T, bool>>(
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<KeyAttribute>() != 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<Expression>();
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);
}
}
}
然后context.SaveChanges()将数据提交到数据库我从的答案开始,修改了两件事:
public static class DbSetExtensions
{
public static void AddOrUpdate<T>(this DbSet<T> dbSet, Expression<Func<T, object>> identifierExpression, params T[] entities) where T : class
{
foreach (var entity in entities)
AddOrUpdate(dbSet, identifierExpression, entity);
}
public static void AddOrUpdate<T>(this DbSet<T> dbSet, Expression<Func<T, object>> 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<Func<T, bool>>(
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<KeyAttribute>() != 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<Expression>();
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);
}
}
}
context.Projects.AddOrUpdate(x => x.Name, new Project { ... })
context.Projects.AddOrUpdate(x => new { x.Name, x.Description }, new Project { ... })
public TEntity AddOrUpdate(TEntity entity)
{
var entityEntry = Context.Entry(entity);
var primaryKeyName = entityEntry.Context.Model.FindEntityType(typeof(TEntity)).FindPrimaryKey().Properties
.Select(x => x.Name).Single();
var primaryKeyField = entity.GetType().GetProperty(primaryKeyName);
var t = typeof(TEntity);
if (primaryKeyField == null)
{
throw new Exception($"{t.FullName} does not have a primary key specified. Unable to exec AddOrUpdate call.");
}
var keyVal = primaryKeyField.GetValue(entity);
var dbVal = DbSet.Find(keyVal);
if (dbVal != null)
{
Context.Entry(dbVal).CurrentValues.SetValues(entity);
DbSet.Update(dbVal);
entity = dbVal;
}
else
{
DbSet.Add(entity);
}
return entity;
}
public static TEntity AddOrUpdate<TEntity>(this DbSet<TEntity> dbSet, DbContext context, Func<TEntity, object> identifier, TEntity entity) where TEntity : class
{
TEntity result = dbSet.Find(identifier.Invoke(entity));
if (result != null)
{
context.Entry(result).CurrentValues.SetValues(entity);
dbSet.Update(result);
return result;
}
else
{
dbSet.Add(entity);
return entity;
}
}
dbContext.MyModels.AddOrUpdate(dbContext, model => model.Id, new MyModel() { Id = 3 });