What's wrong with this Lambda query? I want to be able to only include ProjectDocs of a certain type, there could be many types of ProjectDocs
ICollection<Project> projects = db.Projects
.Include(i => i.ProjectDoc.OfType<Cover>().Where(s => s.Status == "Active"))
.Include(i => i.ProjectDoc.OfType<Summary>().Where(s => s.Status == "Active"))
.Include(i => i.User)
.Include(i => i.ProjectTag.Select(t => t.Tag)).ToList();
I have a model ProjectDoc with the derived classes Cover, Segment and Summary. Should I just include ProjectDoc and use the discriminator column in a condition later? Some of the types could have a large number of results, others just a few.
The Error I get...
The Include path expression must refer to a navigation property defined
on the type. Use dotted paths for reference navigation properties and the
Select operator for collection navigation properties.
Parameter name: path
The Navigation Property on "Project" is ProjectDoc. There is no navigation property for the derived classes. When I tried that I got tons of extra keys.
This scenario is not supported - you can only load or not load a set of related entities but you can not apply filter expressions to load only a subset of the entities.
The API documentation for Include() lists the different expressions that are supported and states that the method just delegates the work to an underlying Include() method taking a string as argument, for example ObjectQuery.Include(). The documentation of this method and the linked page Shaping Query Results make it more or less obvious that this is not supported.
Related
This is more of a syntax question than an actual bug or error, as I finally got what I wanted working. But I want to understand and perhaps improve upon my current solution.
Schema
Let's assume I have a Users table, with a one-to-many relationship to a table Posts, and a further one-to-one relationship table of Authors - one for each Post.
I want to write a custom repository function to get all Users, with all Posts, with each Author per Post.
Attempt #1 (Doesn't Work)
I thought I could do something like:
public IQueryable<User> GetUsersWithPostsAndAuthors()
{
var query = GetAll();
// include all details on user object
return query
.Include(user => user.Posts.Select(x => x.Author));
}
it doesn't seem to include the Author entity. Actually, I was getting the following error:
Lambda expression used inside Include is not valid.
Attempt #2 (Also Doesn't Work)
Then I thought that maybe those Posts need to be in the query first, so I tried this:
public IQueryable<User> GetUsersWithPostsAndAuthors()
{
var query = GetAll();
// include all details on user object
return query
.Include(user => user.Posts)
.Include(user => user.Posts.Select(x => x.Author)
}
Unfortunately, I got the same error:
Lambda expression used inside Include is not valid.
Attempt #3 (Works!)
However, if I use the version of Include where you can provide a string navigationPropertyPath (which actually I don't like since it's just a hardcoded string), with something like this:
public IQueryable<User> GetUsersWithPostsAndAuthors()
{
var query = GetAll();
// include all details on user object
return query
.Include(user => user.Posts)
.Include("Posts.Author");
}
The query works as expected!
What is going on here? I thought the Select projection would do the same as Include. (And there seem to be some answers on Stackoverflow suggesting that.)
More importantly, is there a way to accomplish what I want without hardcoding the Posts.Author in the Include call? I'd like to have static type checks here.
What is going on here?
No offense, but nothing more than not quite understanding what Include is for. It's only for including navigation properties, not for projections.
The syntax is quite clear:
Include for navigation properties off of the root of the query:
.Include(user => user.Posts)
ThenInclude for navigation properties off of included navigation properties:
.Include(user => user.Posts).ThenInclude(p => p.Author)
The latter example is equivalent to .Include("Posts.Author"), but the lambda syntax is preferred because of compile-time checking. In the old EF6 version there was no ThenInclude and the syntax for including more levels was as you wrote: .Include(user => user.Posts.Select(x => x.Author)).
A projection is a Select in the LINQ query, not inside an Include statement. For example:
return query.Select(u => new { u.Id, u.Name });
Projections and Includes exclude one another. In the projection there's nothing in which a navigation property can be included. A query like:
return query
.Include(u => u.Posts)
.Select(u => new
{
u.Id,
u.Name,
Posts = u.Posts.Select(p => p.Title)
});
will completely ignore the Include. There's no trace of it in the generated SQL: only Post.Title will be queried, not all Post fields, as an Include would do.
Poorly worded title, I know.
I have an object User that has an property (a list of objects) (ICollection<Alert>) Alerts. I want to order the alerts by the Alert's property (DateTime) LastActivatedDt
I tried (within a method w/ a parameter of int id):
user = users
.Include(user => user.Alerts.OrderBy(alert => alert.LastActivatedDt)
.FirstOrDefault(user => user.Id === id)
But I get the following error:
System.InvalidOperationException: The Include property lambda expression 'user => {from Alert alert in user.Alerts orderby [alert].LastActivatedDt asc select [alert]}' is invalid. The expression should represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, specify an explicitly typed lambda parameter of the target type, E.g. '(Derived d) => d.MyProperty'.
You can't do that inside the include, and actually you don't need the include at all for this specific purpose. Just do:
users.OrderBy(o => o.Alerts.Min(a => a.LastActivatedDt)).FirstOrDefault();
You could use Max as well. It depends on how you ultimately want them ordered, but since you're dealing with a collection of things, you have to pick one out to actually order by.
EDIT
For some reason, I totally spaced out on the getting the user by a particular id part. I suppose what you're actually trying to achieve is to pull out a specific user, while having their collection of alerts ordered appropriately. That's not actually possible. What you'll have to do is something along the lines of:
var user = users.Include(x => x.Alerts).SingleOrDefault(x => x.Id == id);
var alerts = user.Alerts.OrderBy(o => o.LastActivatedDt);
You cannot order the user's alerts in place. You'll have to set some other variable with the result of the ordering.
I have a query here that fetches a single entity object and only one nested entity that meets a specific condition, but I'm receiving an error when executing it.
Here is the query
Profile profile = dbContext.Profiles.Where(i => i.ApplicationUserGuid == guiId)
.Include(i => i.ProfileImages.Where(k => k.IsMainImage == true)).First();
And here is the exception error message
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parameter name: path
I also tried running it with another .First(), but still same error message
Profile profile = dbContext.Profiles.Where(i => i.ApplicationUserGuid == guiId)
.Include(i => i.ProfileImages.Where(k => k.IsMainImage == true).First()).First();
You can't filter related entities inside an Include, you are going to need to project your query with the expected result or use explicit loading:
Profile profile = dbContext.Profiles
.Where(i => i.ApplicationUserGuid == guiId)
.First();
dbContext.Entry(profile) //Explicit Loading
.Collection(b => b.ProfileImages)
.Query()
.Where(k => k.IsMainImage == true).Take(1)
.Load();
If you do the projection it will be only one round trip to your database, and if you use explicit loading it will be two.
Just a FYI, in case you think to project the result, project to an anonymous type or onto a DTO. More info here.
I have a Linq To Entities query I am making in my POCO Entity Framework application, that looks like this:
var orders = stateRepository.Get()
.OfType<OrderedState>()
.Where(x => x.Id == stateId)
.Include(x => x.Order);
return orders.FirstOrDefault();
The OfType there is because the State entities use Table Per Hierarchy inheritance. Only OrderedState and its children have the Order property available to them.
However, when I run it, I hit the following error:
An object of type
'System.Data.Entity.DynamicProxies.OrderedState_6F372135E57CB68DDA1A42541A941E1F0848887EABD8BD4833D0159C9D8B055F' cannot be added, attached, or removed from an EntityCollection that contains objects of type My.NameSpace.Entities.OrderedState'."
Is this because EntityFramework is being confused by the fact that OrderedState inherits from OrderState and shares common properties, which are what I am interested in? If I remove the .Include(x => x.Order) from the Linq statement the error is avoided, but the Order itself related to this State comes back as null when my entire purpose is to retrieve precisely that value.
Edit: With more research I have realised something even weirder is going on. My TPH inheritance hierarchy is like this:
BasicState
has many -> StatefulEntities
has a -> CreationDate
LocationState : BaseState
has a -> EntityLocation
ReceivedState : LocationState
has a -> ReceivedDate
ApprovedState : LocationState
has a -> ApprovedBy
OrderedState
has a -> Order
DispatchedState : OrderedState
has a -> DispatchNumber
So when I seek an OrderedState it could be either an OrderedState or A DispatchedState but what I am interested in doing is loading the Order property, which I can't do if I have an untyped list of BaseState objects.
What I am now realising is that when I have the .Include( x => x.Order) Entity Framework ignores the type argument. This got clearer as I tweaked my seed data and started getting this type of error message:
An object of type 'System.Data.Entity.DynamicProxies.ReceivedState_6F372135E57CB68DDA1A42541A941E1F0848887EABD8BD4833D0159C9D8B055F' cannot be added, attached, or removed from an EntityCollection that contains objects of type My.NameSpace.Entities.OrderedState'."
I was surprised by this so I thought I would try reinforcing the type:
var orders = stateRepository.Get()
.OfType<OrderedState>()
.Where(x => x is OrderedState && x.Id == stateId )
.Include(x => x.Order);
Sure enough, when I don't try to include the Order property I get only OrderedStates back. As soon as I use Include I get an exception because the types that I am deliberately trying to avoid returning cannot be returned. The Id I am querying is definitely the ID of an OrderedState- in fact the method I am calling only accepts OrderedState objects as yet as a test to see whether that would prevent this problem.
My goal is to retrieve a single typed element from the TPH table including the child property that only this typed element will have.
I would like to find a way using Linq to filter a navigation property to a subset of related entities. I know all answers around this subject suggest doing an anonymous selector such as:
query.Where(x => x.Users.Any(y => y.ID == actingUser.ID))
.Select(x => new
{
Event = x,
Discussions = x.Discussions.Where(actingUser.GenerateSecurityFilterFor<Domain.Discussion>())
})
.OrderBy(x => x.Discussions.Count())
.ThenBy(x => x.Event.Name);
However, this is significantly less than ideal due to the general nature of our query generation and also yields significantly horrific sql queries if you throw up profiler.
I would like to be able to accomplish something like:
query.Include(x => x.Discussions.Where(actingUser.GenerateSecurityFilterFor<Domain.Discussion>()))
.OrderBy(x => x.Discussions.Count())
.ThenBy(x => x.Name);
I realize that this is not supported in EF5 (or any version for that matter) but there has to be a way to accomplish constraining the result set through Linq without delving into anonymous type select statements.
I have attempted doing something to the tune of:
query.GroupJoin(discquqery,
x => x.ID,
x => x.Event.ID,
(evt, disc) => evt.Discussions = disc.Where(actingUser.GenerateSecurityFilterFor<Domain.Discussion>())).ToList();
However you cannot have assignment inside a lambda expression and selecting an anonymous type here causes the same dilemma that it does using the select.
I guess I cannot comprehend why EF does not provide a way (that I can find) to generate:
SELECT
--Properties
FROM Event e
LEFT OUTER JOIN Discussions d
ON e.ID = d.EventID AND --Additional constraints
WHERE
--Where conditions
ORDER BY
--Order Conditions
It is so simple to constrain the join in SQL there HAS to be a way to do it through Linq as well.
PS: I have searched stack, MSDN, experts-exchange, etc. Please realize this is not a duplicate. Anything even touching on this subject either has a cop-out "It can't be done" answer or no answer at all. Nothing is impossible... including this.
Anything even touching on this subject either has a cop-out "It can't
be done" answer or no answer at all. Nothing is impossible...
including this.
Sure. It is possible. You can download EF source code and add this feature yourselves. It will be great contribution to open source project and the community. I believe EF team will gladly help you with your effort.
With the current version "it can't be done" is the answer. You can either use projection to anonymous or special unmapped type as you have described in the beginning of your question. Other options are separate explicit query to load related entities for single parent or separate query to load related entities for all parents.
Load relations for single parent:
context.Entry(event)
.Collection(e => e.Discussions)
.Query()
.Where(d => ...)
.Load();
Load relations for all parents (requires lazy loading to be turned off):
// load all parents
var events = query.Where(e => ...).ToList();
// load child filtered by same condition for parents and new condition for children
childQuery.Where(d => e.Event ... && d.Something ...).Load();
The second solution requires child to have navigation property back to parent (for constructing same query condition used initially to loads parent). If you have everything correctly configured and entities are attached EF should automatically fix your relations (collections) in parent entities (but it will not mark collection in dynamic proxy as loaded so that is the reason why you cannot use this together with lazy loading).