C
C#•3y ago
M B V R K

EF Core Global Query Filter Exception

Hi friends, I'm try to create a Global query filter for my EF Core, to be honest this is the first time for me with Global Query Filters In my AppDbContext => OnModelCreating I tried this:
foreach ( var entityType in modelBuilder.Model.GetEntityTypes() )
{
if ( typeof( IArchivable ).IsAssignableFrom( entityType.ClrType ) )
{
Expression<Func<IArchivable, bool>> exression = x=> (bool?) x.IsArchived != null || (bool?) x.IsArchived == false;

modelBuilder.Entity( entityType.ClrType ).HasQueryFilter( exression );
}
}
foreach ( var entityType in modelBuilder.Model.GetEntityTypes() )
{
if ( typeof( IArchivable ).IsAssignableFrom( entityType.ClrType ) )
{
Expression<Func<IArchivable, bool>> exression = x=> (bool?) x.IsArchived != null || (bool?) x.IsArchived == false;

modelBuilder.Entity( entityType.ClrType ).HasQueryFilter( exression );
}
}
How my IArchivable looks like:
public interface IArchivable
{
bool? IsArchived { get; set; }
string ArchivedBy { get; set; }
DateTime? ArchivedOn { get; set; }
}
public interface IArchivable
{
bool? IsArchived { get; set; }
string ArchivedBy { get; set; }
DateTime? ArchivedOn { get; set; }
}
The Issue: When I start the app, automatically II get this exception:
InvalidOperationException: The filter expression 'x => ((Convert(x.IsArchived, Nullable1) != null) OrElse (Convert(x.IsArchived, Nullable1) == Convert(False, Nullable`1)))' specified for entity type 'ContinuousFormation' is invalid. The expression must accept a single parameter of type 'MBSM.Core.Entities.ContinuousFormation' and return bool.
Which ContinuousFormation is an Entity type/class and it looks like :
public class ContinuousFormation : IAuditable, IArchivable
{
//Other properties removed for clarity
public bool ? IsArchived { get; set; }
public string ArchivedBy { get; set; }
public DateTime ? ArchivedOn { get; set; }
}
public class ContinuousFormation : IAuditable, IArchivable
{
//Other properties removed for clarity
public bool ? IsArchived { get; set; }
public string ArchivedBy { get; set; }
public DateTime ? ArchivedOn { get; set; }
}
My Questions: Please how do I can fix this issue ? and massive thanks in advance <3
20 Replies
M B V R K
M B V R KOP•3y ago
The Exception screenshot:
Greenthy
Greenthy•3y ago
I'd wager you gotta look at getting the proper argument in the expression which now is set as IArchivable and should be of the formentioned type so you're going to have to rework how you get the filter expression tree using the parameter p
M B V R K
M B V R KOP•3y ago
oh damn, btw I just forgot to let that parameter variable there it is not a part of what I try
Greenthy
Greenthy•3y ago
no but I believe it'll be part of your solution 😄 your passing an expression that takes IArchivable as argument, and I believe that's why he's complaining as it expect ContiuousFormation as argument (or well, the entityType.ClrType)
M B V R K
M B V R KOP•3y ago
hmmmm so what should I do/ I change in my code ?
Greenthy
Greenthy•3y ago
haven't done enough things with expression trees to know by heart, but you need to get a Expresion<Func<ContinuousFormation, bool>> I imagine by either manually building it (using the parameter before) or trying to do some visitor replacement
M B V R K
M B V R KOP•3y ago
omg if I use Expresion<Func<ContinuousFormation, bool>> I need to use it with over than 50 entitiespepehands
Greenthy
Greenthy•3y ago
which is why you're better of trying to either replace the expression you made for the interface to use the specific type you have or you make it more in a programmatic way
M B V R K
M B V R KOP•3y ago
hmmmm do you have any peice of code you sggest to achieve that ?'
Greenthy
Greenthy•3y ago
again I only get by with a little bit of expression tree work, so you'll probably be able to slim this down and it's not complete but it might get you going
Greenthy
Greenthy•3y ago
M B V R K
M B V R KOP•3y ago
I really appreciate bro, I have another question if I want to add another check because at this time I just checked the Null but in the same time I want to check if the IsArchived Equals false too, because I want only the entities that are have IsArchived != null && IsArchived == false Please how do I can transform this to an Expression
Greenthy
Greenthy•3y ago
every part is another expression and for the most simple ones you have built in support like isArchived is combined with null using the not equals combine the expression you have using an andalso with a similar statement checking the value
M B V R K
M B V R KOP•3y ago
sorry for annoying I do what you said it works perfectly but Ihave last issue IIdk how to combiine the current check with another check using the OR please can you provide a piece of code about it ? And thanks in advance
Greenthy
Greenthy•3y ago
you can keep combining the binary expressions
M B V R K
M B V R KOP•3y ago
how ?
Greenthy
Greenthy•3y ago
Expression.OrElse( Expression.AndAlso(null check, value check), Expression.AndAllso( null check 2, valuecheck 2))
M B V R K
M B V R KOP•3y ago
and this should be assigned to a variable ?
Greenthy
Greenthy•3y ago
the result is another expression, you can use it similar or then use it as the input to convert it to a lambda function
M B V R K
M B V R KOP•3y ago
@Greenthy really really appreciate you valuable help, massive thanks for it and your time the finale solutions looks like
foreach ( var entityType in modelBuilder.Model.GetEntityTypes() )
{
if ( typeof( IArchivable ).IsAssignableFrom( entityType.ClrType ) )
{
var parameter = Expression.Parameter( entityType.ClrType , "p" );

var isArchivedProperty = MemberExpression.Property(parameter, "IsArchived");

// check isArchived == null

var isArchivedNullCheck = Expression.Equal( isArchivedProperty , Expression.Constant( null , typeof( bool ? ) ) );
var isArchivedNullFunc = Expression.Lambda( isArchivedNullCheck , parameter );

// check isArchived == false
var isArchivedFalseCheck = Expression.Equal( isArchivedProperty , Expression.Constant( false , typeof( bool ? ) ) );
var isArchivedFalseFunc = Expression.Lambda( isArchivedFalseCheck , parameter );

// Combine the two expressions using OR
var isArchivedCExpression = Expression.OrElse( isArchivedNullCheck , isArchivedFalseCheck );

// Create the lambda expression
var isArchivedFunc = Expression.Lambda( isArchivedCExpression , parameter );

modelBuilder.Entity( entityType.ClrType ).HasQueryFilter( isArchivedFunc );

}
}
foreach ( var entityType in modelBuilder.Model.GetEntityTypes() )
{
if ( typeof( IArchivable ).IsAssignableFrom( entityType.ClrType ) )
{
var parameter = Expression.Parameter( entityType.ClrType , "p" );

var isArchivedProperty = MemberExpression.Property(parameter, "IsArchived");

// check isArchived == null

var isArchivedNullCheck = Expression.Equal( isArchivedProperty , Expression.Constant( null , typeof( bool ? ) ) );
var isArchivedNullFunc = Expression.Lambda( isArchivedNullCheck , parameter );

// check isArchived == false
var isArchivedFalseCheck = Expression.Equal( isArchivedProperty , Expression.Constant( false , typeof( bool ? ) ) );
var isArchivedFalseFunc = Expression.Lambda( isArchivedFalseCheck , parameter );

// Combine the two expressions using OR
var isArchivedCExpression = Expression.OrElse( isArchivedNullCheck , isArchivedFalseCheck );

// Create the lambda expression
var isArchivedFunc = Expression.Lambda( isArchivedCExpression , parameter );

modelBuilder.Entity( entityType.ClrType ).HasQueryFilter( isArchivedFunc );

}
}

Did you find this page helpful?