Entity Framework: Why do navigation properties disappear after a group by? - c#

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.

Related

C# Join two tables using Include. Data comes from a Model

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.

Accessing properties of second table via element of first table

I have two tables which are in a relationship. They are called tblX and tblY.
var x = (from v in db.tblX select v).First();
Now x has these properties:
x.name
x.id
x.tblY
tblY has these properties: idY, nameY.
After I use the linq statement above, I can get to idY without making a join. I can access x.tblY.idY and x.tblY.nameY. Is it ok if I access them like this? Is it a good programming practise?
tblY's properties will be loaded lazily. It is fine unless you do this operation repeteadly for big numbers of tblX objects. Than you might consider eager loading.
Related: Entity Framework - what's the difference between using Include/eager loading and lazy loading?

Load a single related object

I need to load a single related object from an navigation property (ICollection) to send to my MVC View.
This can save me from unnecessary db access and load.
I've found this article about loading related objects but didn't figure out how to load one single related object from the list.
To be short, I need the object and inside its navigation property one single related object.
How to achive this?
You can't do this using Include. Include will bring back all related entities for the navigation property. You can either write two separate queries or write a join in your query.
Writing two queries:
var princess = context.Princesses.Find(id);
var unicorns = context.Unicorns.Where(u => u.PrincessId == id && u.UnicornName == "Blinky");
princess.Unicorns = unicorns.ToList();

problem with navigation properties in entity framework

When I execute this query I can navigate in TypeP property:
var items = from item in context.ProductosBodegas.Include("Product.TypeP")
select item;
But when I execute this query the TypeP Property is null:
var items = from item in context.ProductosBodegas.Include("Product.TypeP")
select item.Product;
Why is this?
Looks like Include only affects the directly returned object :
http://msdn.microsoft.com/en-us/library/bb896272.aspx
Otherwise you can call
item.TypePReference.Load()
But this can (and will) lead to perfornance issues (N+1 select), if used in a loop.
Another option would be to "reverse" your query, assuming relationship between Product and ProductosBodegas is bidirectional :
var items = context.Products
.Include("TypeP")
.Where(p => p.ProductosBodegas.Any( /* include condition here if needed */ ))
As far as I know Entity Framework ignores Includes as soon as you don't want to return full entities but only projections. See for instance the answer here. I am not sure if that is still valid for all types of projections but apparently it is still valid in your situation.
You can workaround this by adding the navigation property you want to have loaded into an anonymous type:
var items = from item in context.ProductosBodegas
select new {
Product = item.Product,
TypeP = item.Product.TypeP
};
(You don't need .Include here anymore.) After executing this query (by using .ToList() for instance) you can project to only the part of the anonymous type you want to have, like so:
var products = items.ToList().Select(x => x.Product);
The elements in this products collection have loaded the TypeP reference property now.
Edit:
Important note: Don't change the order of .ToList and .Select.... While this ...
var products = items.Select(x => x.Product).ToList();
... is also syntactically correct and also returns an enumeration of products, the TypeP reference will NOT be loaded in this case. The query for the anonymous type must be executed at first in the database and the anoynmous type collection loaded into memory. Then you can throw away the part of the anoynmous type you don't want to have by the .Select method.
you should load product first
var items = from item in context.ProductosBodegas.Include("Product").Include("Product.TypeP")
select item;

lazy loaded NHibernate collections into ViewModels?

I have my NHibernate mappings set to lazy loading = true.
In my CustomersViewModel I have something like:
foreach (Customer c in _customerRepository)
{
this.Customers.Add(new SingleCustomerViewModel(c));
}
This obviously kills all the lazy loading, since the customers are passed one by one.
How do I get my collections (including subcollections and sub-subcollections a.s.f.) of model-objects into the corresponding ObservableCollections of my ViewModels to bind to the UI?
This seems to be a common problem, but I found no answer, neither here nor on the Googles ...
I am not sure I completely understand the question .
But I was thinking why not change your getCustomers method to
IEnumerable<SingleCustomerViewModel> getCustomers(){
return from c in _customerRepository select SingleCustomerViewModel(c);
}
Since LINQ expressions are lazily evaluated you nhibernate collection wont be initialized until its actually bound to the UI .
This is a classic "SELECT N+1" problem: whichever query layer you are using for NHibernate offers you a way to eagerly load the child collections in your initial query to avoid this row-by-row query pattern.
With the LINQ provider, for example:
session.Query<Customer> ()
.FetchMany (c => c.Widgets) // eagerly load child collections
.ThenFetchMany (w => w.Frobbers); // now get grandchild collection
If you're using HQL, just add the fetch keyword to your joins.

Categories