有同样的问题,最后实现了我自己AddOrUpdate
的。
首先,我们必须获取实体的实际主键(也许您需要在此处添加额外的命名约定......):
private static PropertyInfo[] PrimaryKeys<TEntity>()
where TEntity : class
{
return typeof(TEntity).GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(KeyAttribute))
|| "Id".Equals(p.Name, StringComparison.Ordinal))
.ToArray();
}
然后,我们需要解析“标识符表达式”,它用于AddOrUpdate
匹配现有项(如原始项AddOrUpdate
):
private static PropertyInfo[] Properties<TEntity>(
Expression<Func<TEntity, object>> identifiers)
where TEntity : class
{
// e => e.SomeValue
var direct = identifiers.Body as MemberExpression;
if (direct != null)
{
return new[] { (PropertyInfo)direct.Member };
}
// e => (object)e.SomeValue
var convert = identifiers.Body as UnaryExpression;
if (convert != null)
{
return new[] { (PropertyInfo)((MemberExpression)convert.Operand).Member };
}
// e => new { e.SomeValue, e.OtherValue }
var multiple = identifiers.Body as NewExpression;
if (multiple != null)
{
return multiple.Arguments
.Cast<MemberExpression>()
.Select(a => (PropertyInfo)a.Member)
.ToArray();
}
throw new NotSupportedException();
}
因此,.AddOrUpdate(m => m.Name, ...)
应该和.AddOrUpdate(m => new { m.Name, m.ClubId }, ...)
.
最后,我们为每个实体动态构建where 表达式,将其与我们当前的数据库匹配,并采取相应的行动(添加或更新):
public static void AddOrUpdate<TEntity>(this DbContext context,
Expression<Func<TEntity, object>> identifiers,
params TEntity[] entities)
where TEntity : class
{
var primaryKeys = PrimaryKeys<TEntity>();
var properties = Properties<TEntity>(identifiers);
for (var i = 0; i < entities.Length; i++)
{
// build where condition for "identifiers"
var parameter = Expression.Parameter(typeof(TEntity));
var matches = properties.Select(p => Expression.Equal(
Expression.Property(parameter, p),
Expression.Constant(p.GetValue(entities[i]), p.PropertyType)));
var match = Expression.Lambda<Func<TEntity, bool>>(
matches.Aggregate((p, q) => Expression.AndAlso(p, q)),
parameter);
// match "identifiers" for current item
var current = context.Set<TEntity>().SingleOrDefault(match);
if (current != null)
{
// update primary keys
foreach (var k in primaryKeys)
k.SetValue(entities[i], k.GetValue(current));
// update all the values
context.Entry(current).CurrentValues.SetValues(entities[i]);
// replace updated item
entities[i] = current;
}
else
{
// add new item
entities[i] = context.Set<TEntity>().Add(entities[i]);
}
}
}
希望这会有所帮助,尽管这个问题有点老了。