Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDbCommandTreeInterceptor: NavigationProperties not present #101

Closed
divega opened this issue Nov 9, 2016 · 3 comments
Closed

IDbCommandTreeInterceptor: NavigationProperties not present #101

divega opened this issue Nov 9, 2016 · 3 comments

Comments

@divega
Copy link
Contributor

divega commented Nov 9, 2016

From @gentledepp on August 12, 2014 14:19

Hi!

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<TEntity, bool>, 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 :/

Copied from original issue: dotnet/efcore#515

@divega
Copy link
Contributor Author

divega commented Nov 9, 2016

From @BrennanConroy on August 25, 2014 17:52

Investigation:
In order to filter with foreign key relationships you need to use the IStoreModelConvention. EntityFramework.Filters uses Configuration Conventions which doesn't know about Independent Associations (IAs) and therefore when you try filter on a foreign relationship it won't know what you are trying to access.

@divega divega self-assigned this Nov 9, 2016
@divega
Copy link
Contributor Author

divega commented Nov 9, 2016

From @gentledepp on September 23, 2014 15:27

Could you please elaborate a bit (or even better: much more) on that? :)

I looked through the code and I cannot find any "Configuration Convention" things... or I do not know what that is.

Additionally I have the problem, that EntityFramework.Filters seems to change the query before it gets cached. This is because when I change the filter in another DbContext instance and then look into the generated SQL, the parameter value is still the old one.
In order to get this to work, I had to disable QueryCaching.... Quite bad...

Is this also because EF.Filters uses Configuration Convention instead of StoreModel Configuration?

Any hints how this can be solved? E.G.: when to replace parameters of a cached query, etc.

A short code listing would be greatly appreciated! :)

@divega
Copy link
Contributor Author

divega commented Nov 9, 2016

Note for triage: I have just moved here this EF6 issue that had been on my investigate queue for a long time.

Unfortunately, I never made progress on responding to the customer, but it seems to me that Brennan's original response was correct and from the last comment about query caching it seems that the approach taken by EntityFramework.Filters could only ever work in simple scenarios with EF6.

At this point I think we can close this issue. I also think the filtering scenario described in EntityFramework.Filters is something we can address in the future in EF Core:

Filters allow you to define a parameterized filter at configuration time. At runtime, you turn on the filter and apply parameters, and every query for that entity will include the filter.

Currently it is tracked as part of lifecycle hooks (dotnet/efcore#626) but we can consider having a separate time if think it would make sense to have a first-class API for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants