LINQ Include error - c#

I am new to using asp .net and LINQ queries. I wrote the following query but am getting an error.
Include path expression must refer to a navigation property defined on the type
In the above question there is the same error but they don't explain why it is happening at all. I believe it is because I included the inline .First() method on the third line below but again I want to know why this occurs and what it means. Thank you for your help.
Error:
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
Query:
IQueryable<User> users = db.Users
.Where(u => u.Email == email)
.Include(cu => cu.CompanyUsers.First())
.Include(c => c.Companies)
.Include(p => p.ParentCompanyAccounts );

The problem lies in the 3rd line of your query. When you are including something using the Include method, you can't just take one of an object. You have to take them all.
So where you have:
.Include(cu => cu.CompanyUsers.First())
Should be:
.Include(cu => cu.CompanyUsers);
For a good look at how to use Include, I recommend taking a look at this MSDN post.

You cannot use First in an Include call. If you're going to use Include, you need to include all of the related values.

You can user a transient property along with a persistent property.
class User
{
....
public virtual ICollection<User> CompanyUsers {get; set;} //persistent property
[NotMapped]
public User FirstCompanyUser //transient property
{
get{ return CompanyUsers.FirstOrDefault(); }
}
}
You can user a partial class to avoid code loss on regeneration if you use data first approach.

Related

EntityFramework: Discrepency between Includes with Navigation Property vs. Includes with Select?

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.

EF Core 2.0 ThenInclude() navigation not reachable

I'm trying to fetch entities which have children and grandchildren
The Entities are following code first conventions and are as follows
//This is the father class
public partial Class Solicitud{
[InverseProperty("Solicitud")]
public virtual ICollection<Operacion> Operaciones { get; set; }
//Other properties
}
//This is the child class
public partial Class Operacion{
[JsonIgnore] //This is so when serializing we don't get a circular reference
[InverseProperty("Operaciones")]
public virtual Solicitud Solicitud { get; set; }
public virtual Practica Practica { get; set; }
//Other Properties
}
//This is the grandchild class
public partial Class Practica
{
String Nombre;
//Other Properties
}
If I do
context.Solicitudes
.Include(w => w.Operaciones)
.Where(x => x.Profesional == profesional).OrderBy(something);
It works out ok, populating the "Operaciones" collections, and leaving the "Practica" property as null as expected.
The problem arises when I try to get the grandchildren, by use of
context.Solicitudes
.Include(w => w.Operaciones)
.ThenInclude(o => o.Practica)
.Where(x => x.Profesional == profesional);
There, it still populates Operaciones, but in each Operacion the property practica stays null, and I get the following message
warn: Microsoft.EntityFrameworkCore.Query[100106]
The Include operation for navigation '[w].Operaciones.Practica' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information.
Which to me makes no sense because I could very well do
String something = solicitud.Operaciones.ElementAt(0).Practica.Nombre;
Is this a bug? Is there any way I can avoid using nested selects? The classes are really big in that they have a lot of Properties and it becomes difficult to mantain changes to the domain model using that approach.
Thanks.
Edit: edited title.
It seems that you need to start the query from the entity you want as a result. In your example Practica is not present in the result of your query because is nested (there is no direct path between your resulting query and Practica).
You could try to rewrite your query this way (and add a navigation property inside Practica if not already present):
context.Practicas
.Include(p => p.Operacion)
.ThenInclude(o => o.Solicitud)
.Where(p => p.Operacion.Solicitud.Profesional == profesional)
.ToList();
Well, I think this is actually a bug.
This database is running in SQL Server 2016 and migrated from an old, kind of unmaintained Visual Fox Pro database via an Integration Services package.
Somehow something went wrong while writing said package and the database ended up with rows that broke a foreign key restriction (specifically the one relating Operaciones to Practicas), once I deleted the rows that broke the restriction I ran the query again and it successfully populated every member it was supposed to.
I think this is a bug because the warning message was, in my opinion, a bit misleading. It said that I couldn't access Practica from Solicitud which is tecnically true because it could never get me any Practica as the database was broken, but not very accurate in why I couldn't get them.

.Include() derived class of specific Type?

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.

Entity Framework - InverseProperty and IQueryable (or Equivalent)

I'm still fairly new to EF (v4.1), so correct me if I'm wrong, but if I have an InverseProperty as follows:
public virtual ICollection<ItemComment> Comments { get; set; }
This will be lazy loaded when the property is accessed. However, if I wish to filter this list - for example, to get only active comments, I could just add another property as follows:
public IEnumerable<ItemComment> ActiveComments {
get { return Comments.Where(x => x.IsActive); }
}
However, this will load the entire Comments collection first, and then filter right? So not using IQueryable? For performance, ideally I'd like to get the list using IQueryable.
So my question is, can this be done using a property of an entity like this? Or am I going to have to do a where on the ItemComments directly:
var comments = itemCommentRepository.QueryAll()
.Where(x => x.IsActive && x.ItemId == XX).
This will obviously work... but going forward I wonder if there's a better solution?
Update: It seems the entire result set IS loaded, and any filtering would be done on the whole dataset client-side. Aside from hacks, or changing the entity to pass the context in (yuck!), there doesn't appear to be an in-built way to do so. Have marked #Slauma's response as the answer.
this will load the entire Comments collection first, and then filter
right?
Yes.
can this be done using a property of an entity
In theory, by injecting the repository or even a context into the entity constructor. But you would have a dependency of your POCO entities on a data access layer. I would not like this solution.
Your proposed solution is a way. You could alternatively use explicit loading:
itemRepository.LoadActiveComments(item);
Implemented like this:
void LoadActiveComments(Item item)
{
context.Entry(item).Collection(i => i.Comments).Query()
.Where(c => c.IsActive).Load();
}

Converting IQueryable<T> object to another object?

I have no idea what the keywords are so here is an example of what I want:
return from i in userRepo.GetUsers()
select new SimpleUser{
i.UserId,
i.Name
};
userRepo.GetUsers() returns type IQueryable<User>. I'd like to convert this to IQueryable<SimpleUser> to I can restrict access to certain properties of the User domain.
How do I do this without hard coding the translation like this? What about tools like automapper or ValueInjecter, how can they do this?
Also, what is this technique called?
You must hardcode the translation or you must first convert it to IEnumerable. IQueryable represents expression tree translated to some execution in used provider - in your case I believe it will be Entity framework. You can't use any automatic mapping in such query because it will be translated to SQL which will not understand your .net methods or AutoMapper. Projections to custom types are part of the query and must be hardcoded. You can create custom extension method to IQueryable and reuse it where you need:
public static IQueryable<SimpleUser> ProjectToSimpleUser(this IQueryable<User> query)
{
return query.Select(u => new SimpleUser
{
// Here map your fields
});
}
Now you can use:
return repo.GetUsers().ProjectToSimpleUser();
In case of Entity framework SimpleUser mustn't be an mapped entity.
Provided that SimpleUser is assigneable to User (User is an interface of baseclass of SimpleUser), you can
var users = simpleUsers.Cast<User>();
optionally with
var users = simpleUsers.Cast<User>().AsQueryable();
And if you're not sure whether all items are actually Users, then you can use OfType<User> instead of Cast<User>
AutoMapper is the tool you want; it works via reflection and unless you tell it to do otherwise, it will map properties with the same name directly.
Auto-mapping is the technique.
This question is 9 years old so idk if it existed then but for anyone searching now, using .FirstOrDefault() works if you pick a single user.

Categories

Resources