I am very new with C# and need some help. I am working on someone elses code and they are pulling data from a Model. I am trying to join two tables and need to use Include but the error is '==' cannot be applied to Guid and IQueryable. Could someone help with this please. Thanks in advance!
Yes, I am.
.Where() represents your filter. .Select() represents what you want back. If you just want the entities back you don't need a .Select().
If you have an association between menu items and MenuItemProgramData, for example, a MenuItem holds a reference to a MenuItemProgramData then you don't even need the first ID select statement:
return context.DbMenuItems
.Where(x => x.MenItemsProgramData.Plu == plu);
Note: If your context defines DbSet<T> for your various top level entities, you can just use context.Ts rather than .GetItems<T>.
If the relationship exists then this is the preferred approach. Let SQL do the work. The consumer of your method can further .Select() the applicable data, sort it, paginate it, and even append .Include() if you do want to interact with the entire entity graph.
If you don't have a relationship between the menu item and that program data, and know that the # of item IDs from the first query will remain relatively small (say, sub-100) then:
var itemIds = context.DbMenuItemProgramDatas
.Where(x => x.Plu == plu)
.Select(x => x.MenuItemId)
.ToList();
Without the .ToList() you are dealing with an IQueryable which EF would potentially still attempt to translate to SQL statements when later consumed. By using .ToList() it will execute the SQL and populate a List<int>. (Assuming the menu item ID is an int)
To get the IQueryable menu item data rows:
return context.DbMenuItems
.Where(x => itemIds.Contains(x.Id));
And that is it.
Edit: Based on the comment "I want to return a field named ParentId to know if it is empty or not. That's all but I need both tables linked to get that answer."
Additionally, looking back at the original code, the naming of the method is a bit misleading. GetItemProgramDataForSubItems implies returning MenuItemsProgramData rather than MenuItems... However, if ParentId is a property of MenuItem, then the caller of this method can use:
var hasParentId = context.GetItemProgramDataForSubItems(plu)
.Any(x => x.ParentId.HasValue);
If the ParentId is on the MenuItemsProgramData:
var hasParentId = context.GetItemProgramDataForSubItems(plu)
.Any(x => x.MenuItemsProgramData.ParentId.HasValue);
Beyond that, you may want to elaborate on what your entities and relationships look like, and what exactly you aim to accomplish from your method or business logic.
Related
This question already has answers here:
Filtering on Include in EF Core
(9 answers)
Closed 1 year ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
I am trying to get something like the following to work:
_dbmsParentSections = FactoryTools.Factory.PdfSections
.Include(x => x.Children.OrderBy(y => y.Order).ToList())
.Include(x => x.Hint).Include(x => x.Fields)
.Where(x => x.FormId == FormId && x.Parent == null)
.OrderBy(o => o.Order)
.ToList();
The part that causes the exception is:
.Include(x => x.Children.OrderBy(y => y.Order).ToList())
EDIT:
Upon further observation,
_dbmsParentSections.ForEach(x => x.Children = x.Children.OrderBy(y => y.Order).ToList());
did the job for me (after the initial Factory call and without the Children.OrderBy).
It seems you cannot sort the children collection in your query.
Either sort after the query or load the children in a second query.
Similar question and answer here
According to this documentation, starting with EF Core 5.0, you can sort by a property of your Included Entity:
await context.Parents
.OrderBy(parent => parent.Order)
.Include(parent => parent.Children.OrderBy(child => child.Order))
.ToListAsync();
The above example sorts Parent entities by their Order, as well as their Children entities by the Children entities' Order property.
The extension method Includeis a mere wrapper around DbQuery.Include. Internally it does not execute the expressions but only parses them, i.e. it takes their member expressions and converts them to a path as string. The path is used as input for DbQuery.Include.
It has been requested before to enhance the functionality of Include, e.g. to allow partly loaded collections by including a Where clause. Ordering could be another change request. But as you see, because of the internal working of Include the whole mechanism will have to be re-engineered to implement such enhancements. I don't see it on the current road map so it may take a while...
Depending on the use case you might not need to load in separate query or sort afterwards.
In my case I needed them ordered for when looping in the view so I just ordered there
#foreach (var subObject in Object.SubObjects.OrderBy(x=>x.Order))
I use this code por order the include, using a select and a function to order the collection.
Is not the best but work fine if subcollection is small
// GET: api/Tareas
[HttpGet]
public IEnumerable<Tarea> GetTareas()
{
var result = _context.Tareas
.Include(p => p.SubTareas)
.Select(p => SortInclude(p));
return result;
}
private Tarea SortInclude(Tarea p)
{
p.SubTareas = (p.SubTareas as HashSet<SubTarea>)?
.OrderBy(s => s.Position)
.ToHashSet<SubTarea>();
return p;
}
This will never gona work. EF include is try to understand and translate everything to SQL, but you want to much from this. Load all entities without sorting and .ToList()-ing, and write an extension method for IEnumerable to get an ordered result.
Generally if you're using a bunch of includes, it's because you need to access child properties in a view. What I do is order the child collection when I need to access it in a view.
For example, I might build some Include statements for a master/detail form. There's no sense ordering this at the initial EF query. Instead, why not order these child records at the view level when you're actually accessing them?
I might have a survey with multiple survey questions. If I want to present the questions in a particular order at do it at the partial view level when I'm passing the model child collection to the partial view.
#Html.Partial("_ResponsesPartial",Model.SurveyResponses.OrderBy(x =>
x.QuestionId))
You should not convert an IQueryable type to IEnumerable and call Include because Include is not supported by IEnumerable type.
In short, never call Include after ToList
IQueryable = server side call (SQL)
IEnumerable = client side (loaded in memory)
I have Parent and Child entities related to each other as 1 to M. I need to query childs together with parents within a single SQL query, but the Include method is not properly working for some cases.
This one makes a correct single query for both Parent and Child tables (via JOIN):
var r1 =
ctx.Set<Parent>()
.Include(p => p.Childs)
.Where(p => p.Id == 1)
.ToList();
Once i create an anonymous object on the fly, children are getting lost and SQL contains only Parent's fields. Retrieval of children remains lazy - they are still not loaded:
var r2 =
ctx.Set<Parent>()
.Include(p => p.Childs)
.Where(p => p.Id == 2)
.Select(p => new { myParent = p})
.ToList();
Questions:
Why it's so?
How can I construct a new anonymous object in my LINQ so childs are not geting lost?
p.s. i'd like keep Childs property of Parent virtual.
This is a general problem in all versions of EF known to me. EF tries hard to pass the 'includes' as far as possible, but when "the shape of the query changes", the 'includes' are irreversibly lost.
The "shape" of the query changes for example, when:
a projection is used (select not whole object but just some fields, or different object)
a group-by or other aggregation is used
.. and probably in some more cases, which currently I dont remember.
Sadly, I also dont remember where on MSDN I stumbled upon the "shape of the query" explanation. If I find it, I'll drop here a link.
The solution is actually quite simple: simply specify the 'include' part not early, but at the final result. So, instead of set.include(x) at beginning, do .Select( .. => new { .., x }) to include the 'x' manually. It also works during grouping, as you can do a projection there too.
However, this is not a solution. This is a manual patch/hotfix, which does not solve anything. Considering that you might want to expose a "IQueryable<>" through some interface, you may like to expose a "base query" with some things already .Included. And of course, this is simply not possible to do in a general way, as if the client of the interface does a projection or grouping, he will lose the includes and he will not even know which ones should be. For me, this is a major deficiency in EF.
EDIT: just found one: .Include in following query does not include really Not MSDN, but just as good.
As you're creating an anonymous object the Parent DbSet Set<Parent>() of the context is not being populated with any data and therefore neither are the Children being stored in the context. One solution could be to add the children to the anonymous object but I'm not sure this would cause them to be added to the Set<Child> DbSet.
var r2 = ctx.Set<Parent>()
.Include(p => p.Childs)
.Where(p => p.Id == 2)
.Select(p => new { myParent = p, children = p.Childs })
.ToList();
I have a many to many relationship in EF Code First between Contacts and Lists. ProxyCreation and LazyLoading are disabled to allow serialization of the entities.
I have a query that is meant to return contacts that are in a given list.
// GET api/Contacts
[Queryable]
public IQueryable<Contact> GetContacts(int bulkListId)
{
var bulkList = db.BulkLists.Include(c => c.Contacts).Where(c => c.ID == bulkListId).SingleOrDefault();
if (bulkList == null)
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
return bulkList.Contacts.AsQueryable().OrderBy(c => c.ID).Include(c => c.AddressBookType).Include(c => c.BulkLists);
}
Although this works, it doesn't work as intended. It results in the correct set of contacts who are in a given list but these contacts only have that list populated in their Lists property of the relationship. So when this is serialized and gets back to the client it hides the other lists that the contacts are members of.
I can't see how the query is filtering it in this way and how I might change it to include the full set of lists. Any advice would be very much appreciated.
You're cheating! :)
By adding AsQueryable() to bulkList.Contacts you make it possible to continue with Include without making the compiler complain. BUT...
As per MSDN on DbExtensions.Include:
This extension method calls the Include(String) method of the IQueryable source object, if such a method exists. If the source IQueryable does not have a matching method, then this method does nothing.
(emphasis mine)
And EntityCollection does not have an Include method, so nothing happens.
You'll have to expand the include list in your first statement, probably like so:
db.BulkLists
.Include(c => c.Contacts.Select(c => c.AddressBookType))
.Include(c => c.Contacts.Select(c => c.BulkLists))
I retrieve a collection with the following query:
var numbers = _betDetailItem.GetBetDetailItems().Where(betDetailItem => betDetailItem.BetDetail.Bet.DateDrawing == resultToCreate.Date && betDetailItem.BetDetail.Bet.Status == 1).Where(condition);
Right there I'm able to access my navigation properties and navigate through binded info. Note how I actually use them to filter the data.
After I group the results, the navigation properties become null.
var grouped = numbers.GroupBy(p => p.BetDetail.Bet);
//Iterate through the collection created by the Grouping
foreach (IGrouping<Bet, BetDetailItem> group in grouped)
{
var details = group.Key.BetDetails; //This is what doesn't work. BetDetails is a navigation property which was accessible in the previous query.
}
Am I doing something wrong?
You are confusing LINQ to Entities and object operations.
This is LINQ to Entities:
var numbers = _betDetailItem.GetBetDetailItems().Where(betDetailItem => betDetailItem.BetDetail.Bet.DateDrawing == resultToCreate.Date && betDetailItem.BetDetail.Bet.Status == 1).Where(condition);
So is this:
var grouped = numbers.GroupBy(p => p.BetDetail.Bet);
These are object operations:
foreach (IGrouping<Bet, BetDetailItem> group in grouped)
{
var details = group.Key.BetDetails; //This is what doesn't work. BetDetails is a navigation property which was accessible in the previous query.
}
In LINQ to Entities, there is never any need to think about loading related instances. You can always refer to any property of any object. However, at some point, you want to move out of the LINQ to Entities world and into object space, because you want to work with instances of type BetDetail instead of type IQueryable<BetDetail>. This means that the Entity Framework is now required to generate SQL to retrieve data from the database. At that point, it doesn't snow which related instances you will be accessing in your code later on. Nothing in your LINQ to Entities query forces the loading of the related Bet. So unless you do something to cause it to be loaded, like use eager loading, explicit loading, or EF 4 lazy loading, it won't be loaded.
Using lazy loading (e.g., in Entity Framework 4, or in another ORM) will make this code appear to function, but it will be unnecessarily slow, due to the large number of database queries generated. A better solution would be to use eager loading or projection. This way there will be only one DB roundtrip.
Once you do a GroupBy(), you're no longer dealing with your entities -- they have been... well, grouped, so the var in var grouped = ... is now of type IEnumerable<IGrouping<.... As such, the methods available on the items in the grouped collection are the methods of the IGrouping<> interface.
You may want to OrderBy() instead of GroupBy(), depending on your need, or you'll need to iterate on two levels: iterate over each group in grouped, and over each member within each of those.
Once you are inside of a particular IGrouping<>, you should have access to the properties for which you are looking.
Class Customer has the following method, which returns an IQueryable with the linq query to get the active sales for a customer:
public IQueryable<SalesHeader> QueryCurrentSales()
{
// this.SalesHeaders is an association defined in the DBML between
// the Customer and SalesHeader tables (SalesHeader.CustomerId <-> Customer.Id).
return this.SalesHeaders
.Where(sh => sh.Status == 1).AsQueryable();
}
The idea is to centralise the query definition in a single point, so that later we can get the SalesHeader rows, do a count, paginate using the PaginatedList class (from NerdsDinner), and add further Where conditions when searching Sales within active SalesHeaders.
However, after running the SQL Sever profiler, I've discovered that doing the following, gets all the rows and then does the Count in memory.
// cust is an object of type Customer.
int rows = cust.QueryCurrentSales().count();
I guess that the problem lies in the fact that we have used the AsQueryable() method, but I don't know what would be the correct way to accomplish what we intended.
You haven't shown what SalesHeaders is. The fact that you feel you need to do AsQueryable suggests that it's returning an IEnumerable<T> rather than an IQueryable<T>... which is probably the problem. If you could show SalesHeaders, we may be able to help.
It's possible that instead of using AsQueryable you may be able to just cast to IQueryable<SalesHaeder> if the SalesHeaders property is returning an expression which could actually be returned as IQueryable<SalesHeaders> in the first place - but in that case I'd suggest changing the property type instead.
EDIT: Okay, if it's an entity set then that's probably tricky to fix directly.
I know it's ugly, but how about something like:
public IQueryable<SalesHeader> QueryCurrentSales()
{
// You *may* be able to get away with this in the query; I'm not sure
// what LINQ to SQL would do with it.
int id = this.Id;
return context.SalesHeaders
.Where(sh => sh.Status == 1 && sh.CustomerId == id);
}
Here I'm assuming you have some way of getting to the data context in question - I can't remember offhand whether there's an easy way of doing this in LINQ to SQL, but I'd expect so.
This works right. Doing count() on SQL Server.
// cust is an object of type Customer.
int rows = cust.QueryCurrentSales().AsQueryable<SalesHeaders>().count();