使用复合键自定义EF Core AddOrUpdate

问题描述 投票:2回答:1

我为Microsoft.EntityFrameworkCore构建了一个扩展,用于实现AddOrUpdateMethod。它运行正常,但是对于具有复合主键的实体,即使存在具有相同键的对象,AnyAsync方法也始终返回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());

我认为这种情况正在发生,因为我使用匿名类型作为关键,但我不知道如何解决这个问题。

entity-framework entity-framework-core composite-primary-key any
1个回答
3
投票

问题似乎是使用匿名类型常量表达式,它当前导致client evaluation,而C#运算符==通过引用比较匿名类型,因此总是返回false

获得所需服务器转换的技巧是用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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.