Select a range with Entity Framework - c#

I have an issue trying to maximize the performance of our listview that has pages.
I want the entity framework to do a select statement, but only return a range of the result (range = the items of one page of the listview).
I have searched google but didn't find any results on this. I only found that I can do a .ToList().GetRange(start index, end index), but then all items would be loaded in memory, and that is what I would like to avoid...
Can someone tell me if this can be done? (I don't want to use a stored procedure or view or something like that because our listview has to be reusable...)
Thanks!

You should be able to use .Take(x).ToList()
edit: sorry, try .Skip(startPosition).Take(numberOfItems).ToList()

And if you are not using lazy loading be sure to use Query() before Load() when applying filters to avoid loading the whole collection before applying filters :
context.Entry(blog)
.Collection(b => b.Posts)
.Query()
.Skip(startPosition)
.Take(numberOfItems)
.Load()
.ToList();
When using the Query method it is usually best to turn off lazy loading for the navigation property. This is because otherwise the entire collection may get loaded automatically by the lazy loading
mechanism either before or after the filtered query has been executed.
For more details : http://msdn.microsoft.com/en-us/data/jj574232.aspx

Related

Self join not including children (entity framework 4.0)

I am creating a sort of family tree in entity framework 4.0. I have come across an issue where the Entity Framework is only loading the immediate children. It does not load the children of the children even though i have an include specified.
For example, this is my query :-
public IQueryable<TreeMember> GetTreeMembers(int userId)
{
return this.ObjectContext.TreeMembers.Include("RelatedTreeMembers").Where(u => u.UserId == userId && u.RelatedTreeMemberId == null);
}
This would load the 1st level of children. But it does not load the children of the children. If i have to include children of the children, i have to write :-
public IQueryable<TreeMember> GetTreeMembers(int userId)
{
return this.ObjectContext.TreeMembers.Include("RelatedTreeMembers.RelatedTreeMembers").Where(u => u.UserId == userId && u.RelatedTreeMemberId == null);
}
This is quickly getting to be frustrating because i don't know how many times should i have to write this RelatedTreeMembers as a family tree can extend upto N level. How do i get past this issue? If my question is not clear please let me know.
Thanks in advance :)
That is how EF works. You want to define recursive (hierarchical) query which is not possible with eager loading in EF. You always have specify exactly which navigation properties you want to load - obviously in this scenario you can't because you don't know how deep is your recursion.
I like the idea #Magnus suggested with CTE but I would not use DB View. I would use stored procedure. The reason is that you already have entity TreeMember mapped to a table. If you define the view you will not be able to map it to the same entity type. You will need new entity for the view. If you use stored procedure you can map its result to already existing entity type.
Another way is to use lazy loading.
Write a view with a recursive CTE and than use that with Linq.
http://msdn.microsoft.com/en-us/library/ms186243.aspx

Eager loading a tree in NHibernate

I have a problem trying to load a tree, this is my case, I have an entity associated with itself (Hierarchic) with n levels; the question is, Can I load eagerly the entire tree using ICriteria or HQL?
Thanks in advance for any help.
Ariel
Yes... just set correct fetchmode.
i'll include example in a minute.
Example taken from here =>
IList cats = sess.CreateCriteria(typeof(Cat))
.Add( Expression.Like("Name", "Fritz%") )
.SetFetchMode("Mate", FetchMode.Eager)
.SetFetchMode("Kittens", FetchMode.Eager)
.List();
You can specify to eager load child of child too =>
.SetFetchMode("Kittens.BornOn", FetchMode.Eager)
In case you are using Linq to NHibernate, use Expand method =>
var feedItemQuery = from ad in session.Linq<FeedItem>().Expand("Ads")
where ad.Id == Id
select ad;
And i would recommend to use helper method that creates string from passed in lambda expression.
Quite likely that it's possible to tell Criteria to load whole tree. But i'm not aware about that and i prefer specifying what exactly i need (seems dangerous to load everything).
Does this helps?

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

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.

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.

applying filters 'like' clause on data returned via subsonic

i have a data access layer which returns data from stored procedures. If i bind this to a gridview control in asp.net 2.0, the users then have an option of filtering on that data select list where in they can choose the conditional clause of
like
=
or
and
Once the result is returned, I do not want to hit the Db again with the filters applied.
I have an option to use .net 3.5 if the need be. i looked at this:
http://weblogs.asp.net/jgaylord/archive/2006/05/31/Filter-A-GridView-After-The-Initial-Bind.aspx
and not sure of its efficiency.
Are you using a SqlDataSource control on the page and binding to that? if so, then you'll have to change gears and put in a manual binding step.
you might do something like storing the DataTable in ViewState or Session, then wrapping it in a DataView that contains your filter (or no filter for the intial page load). Then you just bind to the DataView.
SubSonic Collections have filtering capabilities built-in. You can also use LINQ filters on them. I don't have specific examples here for you, but you can basically set Where clauses on subsonic collections and run a particular method (can't remember which one as I've never used this functionality) which applies the filter to the current collection and returns a new one. From what I saw, the original collection is not modified, it just has metadata to filter it in memory.

Categories