EntityFramework.Filters - Filter expression with join to another table

730 Views Asked by At

I modified jbogards EntityFramework.Filters which adds the support for "global filters" to an EF DbContext. (see https://github.com/jbogard/EntityFramework.Filters)

As I am trying to get this to work with a simple join expression, I stumbled over a problem.

I my scenario, I have an "AccessPredicate" table which contains an id of an IResource (Document or Chapter) and the id of a User. Now what I am trying to achieve is to filter the Documents and Chapters a User is not allowed to see like so

`modelBuilder.Conventions.Add(FilterConvention.Create<IResource, long>("UserId",
                (e, userId) => e.AccessPredicates.Any(@as => @as.UserId == userId)));`

What I had to change in EntityFramework.Filters in order to support this was the ParameterReplacer.VisitLamda method, as it tries to always return a lamda expression of type Func, but as I have a sub-lamda (as.UserId == 2l) I have to add a type-check:

`protected override Expression VisitLambda<T>(Expression<T> node)
    {
        var arg1 = typeof (T).GetGenericArguments().FirstOrDefault();
        if(arg1 != null && !arg1.IsAssignableFrom(typeof(TEntity)))
            return base.VisitLambda<T>(node);

        var body = Visit(node.Body);
        return Expression.Lambda<Func<TEntity, bool>>(body, node.Parameters[0]);
    }`

so now it blows up in the second last line of FilterQueryVisitor.Visit(...)

var output = result.Predicate.Accept(normalizer);

with the error: A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in EntityFramework.dll

Additional information: No property with the name 'AccessPredicates' is declared by the type 'CodeFirstDatabaseSchema.Document'.

AccessPredicates is a navigationproperty on Document, however the EdmType (I debugged entity framework where the error is thrown: System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder.Internal.ArgumentValidation.ValidateProperty) only has two items in its "Member" collection: "Id" and "Title"

However, if I do not use the filter, and write the query manually like so: var x = ctx.Documents .Where(@d => @d.AccessPredicates.Any(@a => @a.UserId == userId)) .Take(1).FirstOrDefault(); then the "Member" collection contains "Id", "Title", "AccessPredicates" and "Chapter" as I'd expect. Do you have any idea how that can be?

It seems to me that EntityFramework constructs the StructuralTypes (which inherit EdmType) for every query it parses and when I add my filter, I misses something... :/

For ease of debugging, I added a test project: http://1drv.ms/1BbrBAL Just run "update-database" in package-manager and then hit F5.

I do not know if this is a really EF issue, but I do not know any other place where people could help me with that amount of insight into EF internals that you have. I really hope you can help me on this one :/

0

There are 0 best solutions below