C# Create a LambdaExpression that compares two member properties (for EF Core HasQueryFilter)

.net .net-core c# entity-framework entity-framework-core

Question

I try to apply a query filter in the OnModelCreating method (Entity Framework Core) for all entities which have a ClientId property (int). So far I am able to filter the entities but I struggle to invoke the HasQueryFilter for those entities.

The filter should compare the ClientId property of the current Entity with a property from a service called ITenantProvider.

This is how I do it manually:

modelBuilder.Entity<MyEntity>().HasQueryFilter(a => a.ClientId == _tenantProvider.TenantId);

Unfortunately, the EF Core HasQueryFilter method without Generic takes a LambdaExpression:

public virtual EntityTypeBuilder HasQueryFilter([CanBeNullAttribute] LambdaExpression filter);

I don't know how I can translate the above call to a Lambda Expression. My current code looks like this:

foreach (var entityType in modelBuilder.Model.GetEntityTypes().Where(e =>
    e.GetProperties().Select(property => property.Name).Any(pName => pName.Equals("ClientId"))))
{
    var clientId = entityType.FindProperty("ClientId");
    if (clientId != null && clientId.ClrType == typeof(int))
    {
        var parameter = Expression.Parameter(entityType.ClrType, "p");
        var filter = Expression.Lambda(Expression.Equal(Expression.Property(parameter, clientId.PropertyInfo), Expression.Constant(_tenantProvider.TenantId), parameter);
        entityType.QueryFilter = filter;
    }
}

Which basically works for the first call but since I use Expression.Constant it doesn't work for the next request if the _tenantProvider.TenantId changes.

How can I compare the Entity ClientId property with _tenantProvider.TenantId at runtime?

1
2
12/14/2018 9:52:03 AM

Accepted Answer

The easiest way to get the runtime expression equivalent of the

_tenantProvider.TenantId

is to build a compile time parameterless lambda expression and get its Body:

var parameter = Expression.Parameter(entityType.ClrType, "p");
var left = Expression.Property(parameter, clientId.PropertyInfo);
Expression<Func<int>> tenantId = () => _tenantProvider.TenantId;
var right = tenantId.Body;
var filter = Expression.Lambda(Expression.Equal(left, right), parameter);
4
12/14/2018 12:16:20 PM

Popular Answer

Suppose the instance of TenantProvider does not change:

Then instead of the constant expression you could use

Expression.Property(Expression.Constant(_tenantProvider),"TenantId");

If you reinstantiate it at some point this will not work anymore.

Suppose you have some static property that provides the tennant id, then you could use:

Expression.MakeMemberAccess(null,typeof(TennantIdProvider).GetMember("CurrentId")[0]);


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow