Where can I learn about creating a 'dynamic' linq expression? I need to turn this into something dynamic because the currentUser is null when the code is excecuted in the OnModelCreating of Entity Framework. I am trying to create a global query filter.
User currentUser = currentUserService.GetUser();
IPermissionService permissionService = DependencyResolver.Current.GetService<IPermissionService>();
var allowedToSee = Entities.Where(e => permissionService.HasPermission(e, currentUser).View);
modelBuilder.Entity<Audit>().HasQueryFilter(x => allowedToSee.Contains(x.EntityType));
This is the audit class:
public class Audit
{
// Information about changes
public virtual Entity EntityType { get; set; }
}
Example of other dynamic linq expression:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
//If the actual entity is an auditable type.
if(typeof(Auditable).IsAssignableFrom(entityType.ClrType))
{
//This adds (In a reflection type way), a Global Query Filter
//https://docs.microsoft.com/en-us/ef/core/querying/filters
//That always excludes deleted items. You can opt out by using dbSet.IgnoreQueryFilters()
var parameter = Expression.Parameter(entityType.ClrType, "p");
var deletedCheck = Expression.Lambda(Expression.Equal(Expression.Property(parameter, "DateDeleted"), Expression.Constant(null)), parameter);
modelBuilder.Entity(entityType.ClrType).HasQueryFilter(deletedCheck);
}
}
base.OnModelCreating(modelBuilder);
}
CodePudding user response:
Assuming that you have the following DbContext
and AllowedToSee
property is properly implemented, ApplyAuditFilters
should apply query filter to all Audit
descendants.
public class MyDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
ApplyAuditFilters(modelBuilder);
base.OnModelCreating(modelBuilder);
}
// implement this property
public IEnumerable<Entity> AllowedToSee => throw new NotImplementedException();
private void ApplyAuditFilters(ModelBuilder modelBuilder)
{
var types = modelBuilder.Model.GetEntityTypes()
.Where(et => typeof(Audit).IsAssignableFrom(et.ClrType));
var thisExpression = Expression.Constant(this);
// this.AllowedToSee
var allowedToSeeExpr = Expression.Property(thisExpression, nameof(AllowedToSee));
foreach (var et in types)
{
var param = Expression.Parameter(et.ClrType, "e");
// e.EntityType
var filterPropertyExpr = Expression.Property(param, nameof(Audit.EntityType));
// this.AllowedToSee.Contains(e.EntityType)
var filterBody = Expression.Call(typeof(Enumerable), nameof(Enumerable.Contains), new[] { typeof(Entity) },
allowedToSeeExpr, filterPropertyExpr);
// e => this.AllowedToSee.Contains(e.EntityType)
var queryFilter = Expression.Lambda(filterBody, param);
modelBuilder.Entity(et.ClrType).HasQueryFilter(queryFilter);
}
}
... // other members
}
CodePudding user response:
If you need 'dynamic' linq, I recommend using System.Linq.Dynamic.Core.
And here is the documentation for it.
However, I'm not sure that this would be the best solution in your case. But if you want to dynamically generate linq expressions, then this is a good and easy way to do it.