带有复合键的自定义 EF Core AddOrUpdate
Custom EF Core AddOrUpdate with composite keys
我为 Microsoft.EntityFrameworkCore
构建了一个实现 AddOrUpdateMethod
的扩展。它工作正常,但是对于具有复合主键的实体,AnyAsync
方法 return 始终为 false,即使存在具有相同键的对象也是如此。
这是方法:
public static async Task AddOrUpdateAsync<TEntity>(this DbSet<TEntity> table, Expression<Func<TEntity, object>> key, Expression<Func<TEntity, bool>> deleteExpression, params TEntity[] entities) where TEntity : class
{
var getKeyFunction = key.Compile();
var getShouldDeleteFunction = deleteExpression.Compile();
var context = GetDbContext(table);
foreach (var entity in entities)
{
var primaryKey = getKeyFunction(entity);
var body = Expression.Equal(Expression.Convert(key.Body, primaryKey.GetType()), Expression.Constant(primaryKey));
Expression<Func<TEntity, bool>> query = Expression.Lambda<Func<TEntity, bool>>(body, key.Parameters);
var exist = await table.AnyAsync(query);
context.Entry(entity).State = exist
? getShouldDeleteFunction(entity) ? EntityState.Deleted : EntityState.Modified
: getShouldDeleteFunction(entity) ? EntityState.Detached : EntityState.Added;
}
}
private static DbContext GetDbContext<T>(this DbSet<T> table) where T : class
{
var infrastructure = table as IInfrastructure<IServiceProvider>;
var serviceProvider = infrastructure.Instance;
var currentDbContext = serviceProvider.GetService(typeof(ICurrentDbContext)) as ICurrentDbContext;
return currentDbContext.Context;
}
我是这样使用它的:
await db.Reports.AddOrUpdateAsync(r => new { r.Number, r.Year }, r => r.Active == false, response.Reports.ToArray());
我认为这是因为我使用匿名类型作为键,但我不知道如何解决这个问题。
问题似乎是匿名类型常量表达式的使用,目前导致 client evaluation,而 C# 运算符 ==
通过引用比较匿名类型,因此总是 returns false
.
获得所需服务器翻译的技巧是 "invoke" key
表达式 entity
通过用 Expression.Constant(entity)
替换参数(Expression.Invoke
不'在这种情况下工作)
因此删除不再需要的行 var getKeyFunction = key.Compile();
,并使用以下内容:
foreach (var entity in entities)
{
var parameter = key.Parameters[0];
var body = Expression.Equal(
key.Body,
key.Body.ReplaceParameter(parameter, Expression.Constant(entity))
);
var query = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
var exist = await table.AnyAsync(query);
// ...
}
其中ReplaceParameter
是常用的表达式辅助方法:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
=> new ParameterReplacer { Source = source, Target = target }.Visit(expression);
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : node;
}
}
我为 Microsoft.EntityFrameworkCore
构建了一个实现 AddOrUpdateMethod
的扩展。它工作正常,但是对于具有复合主键的实体,AnyAsync
方法 return 始终为 false,即使存在具有相同键的对象也是如此。
这是方法:
public static async Task AddOrUpdateAsync<TEntity>(this DbSet<TEntity> table, Expression<Func<TEntity, object>> key, Expression<Func<TEntity, bool>> deleteExpression, params TEntity[] entities) where TEntity : class
{
var getKeyFunction = key.Compile();
var getShouldDeleteFunction = deleteExpression.Compile();
var context = GetDbContext(table);
foreach (var entity in entities)
{
var primaryKey = getKeyFunction(entity);
var body = Expression.Equal(Expression.Convert(key.Body, primaryKey.GetType()), Expression.Constant(primaryKey));
Expression<Func<TEntity, bool>> query = Expression.Lambda<Func<TEntity, bool>>(body, key.Parameters);
var exist = await table.AnyAsync(query);
context.Entry(entity).State = exist
? getShouldDeleteFunction(entity) ? EntityState.Deleted : EntityState.Modified
: getShouldDeleteFunction(entity) ? EntityState.Detached : EntityState.Added;
}
}
private static DbContext GetDbContext<T>(this DbSet<T> table) where T : class
{
var infrastructure = table as IInfrastructure<IServiceProvider>;
var serviceProvider = infrastructure.Instance;
var currentDbContext = serviceProvider.GetService(typeof(ICurrentDbContext)) as ICurrentDbContext;
return currentDbContext.Context;
}
我是这样使用它的:
await db.Reports.AddOrUpdateAsync(r => new { r.Number, r.Year }, r => r.Active == false, response.Reports.ToArray());
我认为这是因为我使用匿名类型作为键,但我不知道如何解决这个问题。
问题似乎是匿名类型常量表达式的使用,目前导致 client evaluation,而 C# 运算符 ==
通过引用比较匿名类型,因此总是 returns false
.
获得所需服务器翻译的技巧是 "invoke" key
表达式 entity
通过用 Expression.Constant(entity)
替换参数(Expression.Invoke
不'在这种情况下工作)
因此删除不再需要的行 var getKeyFunction = key.Compile();
,并使用以下内容:
foreach (var entity in entities)
{
var parameter = key.Parameters[0];
var body = Expression.Equal(
key.Body,
key.Body.ReplaceParameter(parameter, Expression.Constant(entity))
);
var query = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
var exist = await table.AnyAsync(query);
// ...
}
其中ReplaceParameter
是常用的表达式辅助方法:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
=> new ParameterReplacer { Source = source, Target = target }.Visit(expression);
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : node;
}
}