Lambda IfThenElse expresión con otra lambda interior (para SetQueryFilter()))

Mi objetivo es tener filtro para IMutableEntityType, donde escojo filas de datos CurrentTenantId valor de columna. Para más contexto, consulte: https://github.com/dotnet/efcore/issues/23718

Esto ya está hecho por mí:

var parameter = Expression.Parameter(entityType.ClrType, "p");
                
// TODO: This filter is working, but we must prevent filtering with TenantId = 0
var workingFilter = Expression.Lambda(
    Expression.Equal(
        Expression.Property(parameter, colName),
        // See https://github.com/dotnet/efcore/issues/23718
        Expression.MakeMemberAccess(Expression.Constant(myContext), currentTenantIdMemberInfo)
        ),
    parameter);

entityType.SetQueryFilter(filter);

Pero quiero conseguir todas las entidades si TenantId == 0Así que escribí este Lambda:

var conditionalExpression = Expression.IfThenElse(
    // Check if TenantId > 0
    Expression.GreaterThan(
        Expression.MakeMemberAccess(Expression.Constant(myContext), currentTenantIdMemberInfo),
        Expression.Constant((long) 0)
        ),
    // build filter: (p) => p.{colName} == {myContext.CurrentTenantId}
    // i.e. (p) => p.TenantId == 123)
    Expression.Lambda(
        Expression.Equal(
            Expression.Property(parameter, colName),

            // See https://github.com/dotnet/efcore/issues/23718
            Expression.MakeMemberAccess(Expression.Constant(myContext), currentTenantIdMemberInfo)
            )//,
        ),
    // TenantId <= 0, so act like (p => p)
    Expression.Lambda(Expression.Constant(parameter)) // just p // TODO: Not working
    //Expression.Lambda(Expression.Constant(parameter), parameter) // p => p // TODO: Also not working...
);

var filter = Expression.Lambda(conditionalExpression);

Pero después de llamar entityType.SetQueryFilter(filter); Tengo InvalidOperationException:

'The filter expression '() => IIF((value(My.Project.MyContext).CurrentTenantId > 0), () => (p.TenantId == value(My.Project.MyContext).CurrentTenantId), () => p)' specified for entity type 'MyEntity' is invalid. The expression must accept a single parameter of type 'My.Project.MyEntity' and return bool.'

Y mi pregunta es: ¿cómo puedo lograr mi objetivo?

Pregunta hecha hace 3 años, 4 meses, 28 días - Por codesculptor


3 Respuestas:

  • Algo así. Te has perdido agregando parameter a Lambda y cometió errores con el uso de la lambda. También seleccionó la manera más corta de hacer eso.

    var parameter = Expression.Parameter(entityType.ClrType, "p");
    
    // EF just needs to know that you have member access to DbContext
    var contextExpr = Expression.Constant(null, typeof(MyContext));
    var tenantExpr = Expression.MakeMemberAccess(contextExpr, currentTenantIdMemberInfo);
    
    var conditionalExpression = Expression.OrElse(
        // Check if TenantId <= 0
        Expression.LessThanOrEqual(
            tenantExpr,
            Expression.Constant((long) 0)
            ),
            Expression.Equal(
                Expression.Property(parameter, colName),
                tenantExpr
            )
        )
    );
    
    var filter = Expression.Lambda(conditionalExpression, parameter);
    
    entityType.SetQueryFilter(filter);
    

    También no estás solo y esta tarea ha sido hecha por muchas bibliotecas, por ejemplo ASP.NET Boilerplate

    Para simplificar tales tareas, recomiendo encarecidamente utilizar los visualizadores de Árbol de Expresión: https://github.com/agileobjects/ReadableExpresionshttps://github.com/zspitz/ExpressionTreeVisualizer

    Respondida el Dec 19, 2020 a las 03:29 - por pixelpioneerx

    Votos positivos: 0 | Votos negativos: 0

  • El problema en tu código actual radica en que estás creando el filtro condicional utilizando Expression.IfThenElse, lo cual no genera una expresión de filtro válida para SetQueryFilter de Entity Framework Core. En su lugar, debes construir la expresión del filtro de una forma diferente para que acepte un único parámetro de tipo MyEntity y devuelva un booleano.

    Para lograr tu objetivo, puedes crear la expresión de filtro condicional de la siguiente manera:

    csharp
    var parameter = Expression.Parameter(entityType.ClrType, "p");
    
    var filter = Expression.Lambda>(
        Expression.Condition(
            Expression.GreaterThan(
                Expression.MakeMemberAccess(Expression.Constant(myContext), currentTenantIdMemberInfo),
                Expression.Constant((long)0)
            ),
            Expression.Equal(
                Expression.Property(parameter, colName),
                Expression.MakeMemberAccess(Expression.Constant(myContext), currentTenantIdMemberInfo)
            ),
            Expression.Constant(true)
        ),
        parameter
    );
    
    entityType.SetQueryFilter(filter);
    

    En este código, estamos utilizando Expression.Condition en lugar de Expression.IfThenElse para construir el filtro condicional. La expresión del filtro ahora acepta un parámetro de tipo MyEntity y devuelve un booleano. Esto debería resolver el error de InvalidOperationException que estás experimentando y lograr tu objetivo de filtrar las entidades en función del valor de CurrentTenantId.

    Haz la prueba con este código y comprueba si ahora puedes aplicar el filtro correctamente a tu entidad.

    Respondida el Dec 20, 2020 a las 03:34 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Para crear un filtro que incluya filas donde TenantId es igual a 0, puedes utilizar el siguiente código:

    var workingFilter = Expression.Lambda( Expression.OrElse( // Filtro existente para `TenantId` mayor que 0 workingFilter, // Filtro adicional para `TenantId` igual a 0 Expression.Equal( Expression.Property(parameter, colName), Expression.Constant(0) ) ), parameter );

    Este filtro verifica si TenantId es mayor que 0 o igual a 0 y devuelve true si alguna de las condiciones es verdadera.

    Actualiza tu código con este filtro modificado:

    
    var filter = Expression.Lambda(conditionalExpression, parameter);
    entityType.SetQueryFilter(filter);
    

    Esto debería permitirte filtrar entidades donde TenantId es igual a 0 o mayor que 0.

    Respondida el Dec 20, 2020 a las 03:42 - por Gemini

    Votos positivos: 0 | Votos negativos: 0