EF Code first Eager loading and OrderBy problem - linq

I have two entities called Category and Product with 1:n relation.
I want to get a Category with its childs that childs be in order.
This is my linq:
_db.Categories.Where(c => c.CategoryID == catID)
.Include(c => c.Products.OrderBy(p => p.ProductID))
.SingleOrDefault();
This query enforce with the below exception because of orderby.
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

Eager loaded data cannot be ordered or filtered. That is linq-to-entities limitation and the only way how to order relations in the database is by using projection:
var data = _db.Polls
.Where(c => c.CategoryID == pollID)
.Select(c => new
{
Pool = c,
Products = c.Products.OrderBy(p => p.ProductID)
})
.SingelOrDefault();
You can project to anonymous or custom type but you cannot project to mapped type (for example Poll).
Another way is dividing this to two queries and use explicit loading:
var poll = _db.Polls.SingleOrDefault(c => c.CategoryID == pollID);
_db.Entry(poll).Collection(c => c.Products)
.Query()
.OrderBy(p =>.ProductID)
.Load();

Include has to reference a navigation property, which means you can't include an OrderBy(). Instead of this:
_db.Categories
.Where(c => c.CategoryID == catID)
.Include(c => c.Products.OrderBy(p => p.ProductID))
.SingleOrDefault();
...you'll have to use this:
_db.Categories
.Where(c => c.CategoryID == catID)
.Include(c => c.Products)
.SingleOrDefault();
...to access an ordered list of Products for each Category, you could add a property to Category like this:
class Category
{
public IEnumerable<Product> OrderedProducts
{
get { return this.Products.OrderBy(p => p.ProductID); }
}
}

Related

Anonymous types mismatch LINQ to SQL query?

List<branch_rating> ObjRat = new List<branch_rating>();
ObjRat=(_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new { rating = a.Sum(b => b.rating), Name = a.Key })
.OrderByDescending(a => a.rating)).ToList();
I tried by putting query in var variable and then adding in object.
Below picture shows the error.
I want to get data from branch_rating table having four columns, I also tried by using var and then adding list data one by one in object.
It will be better if you make a custom class like DOTopResturant which includes data members branch_rating,branch_id and then make a object of type DOTopResturant , add every item in variable q into that object.
List<branch_rating> ObjRat = new List<branch_rating>();
ObjRat = _Context.branch_rating.ToList();
var q = (_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new { branch_rating = a.Sum(b => b.rating), branch_id = a.Key })
.OrderByDescending(a => a.branch_rating)).ToList();
List<DOTopResturant> ObjTopRes = new List<DOTopResturant>();
foreach (var item in q)
{
ObjTopRes.Add(new DOTopResturant() { branch_id = Convert.ToInt32(item.branch_id), branch_rating = Convert.ToInt32(item.branch_rating) });
}
return ObjTopRes;
Just read and understand what the error describes :
your are trying to convert an anonymous type (with 2 properties Name and Rating) to another object type branch_rating.
You have 2 solutions :
work with the anonymous type (using var) :
var ObjRat=(_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new { Rating = a.Sum(b => b.rating), Name = a.Key })
.OrderByDescending(a => a.rating))
.ToList();
And then, in your code, just access to Rating and Name properties of your list members.
work with your type defined branch_rating, by using it in your Select, and use the appropriate constructor.
List<branch_rating> ObjRat = new List<branch_rating>();
ObjRat=(_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new branch_rating() { <Your branch_rating properties assignments here> })
.OrderByDescending(a => a.rating)).ToList();
If your branch_rating object does not have an empty constructor, call the correct constructor with good arguments in the Select.
Change select (third row in the picture) to:
.Select(a => new DOTopRestaurant {rating = a.Sum(b => b.rating), Name = a.Key})
Delete the declaration of ObjRat and use var:
var ObjRat=(_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new { rating = a.Sum(b => b.rating), Name = a.Key })
.OrderByDescending(a => a.rating))
.ToList();
Your list is a list of the anonymous type not of type branch_rating.
If you want to convert it to branch_rating use another Select to create a new branch_rating(...) passing the appropriate parameters to the constructor.
PS You should also look at .NET naming conventions.

where condition in include, how?

The code below is not working. I am getting exception "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."
What im doing wrong
public static MyEntity GetData(this IQueryable<MyEntity> source, int id, int year)
{
return source.Where(x => x.ID == id)
.Include(x => x.Childrens1)
.Include(x => x.Childrens2.where(y => y.Year == year))
.FirstOrDefault();
}
Where is not supported into Include expression, you only can use select, even nested. move where out of include
public static MyEntity GetData(this IQueryable<MyEntity> source, int id, int year)
{
return source
.Include(x => x.Childrens1)
.Include(x => x.Childrens2)
.Where(x => x.ID == id && x.Childrens2.Any(y => y.Year == year))
.FirstOrDefault();
}
Now, you also can unify where or filter inside FirstOrDefault

Is it possible to filter the results of an included entity in the following LINQ statement

I would like to filter the Users entity in this LINQ statement by a boolean property it contains. Is that possible?
var Subscriber = db.Subscribers
.Include(s => s.Users)
.Include(s => s.SubscriberNotes)
.Include(s => s.Orders)
.Include(s => s.Websites.Select(w => w.DomainNames))
.Single(s => s.SubscriberId == id);
Something like Where S.Users.isOwner = true
Since I do not have the original dataset available, I'll give you some code from the top of my head - I guess you could do something like this:
db.Subscribers.Include(s => s.Users.Where(user => user.IsOwner == true))
... and then continue with the remaining Include statements.

A fetch request must be a simple member access expression; '[100002]' is a SubQueryExpression instead

I get the following error:
"A fetch request must be a simple member access expression; '[100002]' is a SubQueryExpression instead. Parameter name: relatedObjectSelector"
Below is my query:
var query =
session.Query<Customer().Where(cus => cus.CustomerId == customerId)
.Fetch(c => c.CustomerType) // Parent CustomerType
.Fetch(c => c.CustomerOrders) // Child Collection of orders
.ThenFetch(co => co.Select(it => it.Item)).ToList(); // A parent of CustomerOrder
If I take the 'ThenFetch' out then I do not get that error. However, I do need to retrieve the Item object.
Any ideas?
Thanks
Can't you just apply the select on the resulting CustomerOrder?
var query =
session.Query<Customer().Where(cus => cus.CustomerId == customerId)
.Fetch(c => c.CustomerType)
.Fetch(c => c.CustomerOrder)
.Select(co => co.Item)
.ToList();

Linq extension methods - how to include not loaded objects?

I have something like this:
var threads = _forumsDb.ForumsAccounts
.Where(a => a.UserName == User.Identity.Name)
.SelectMany(u => u.Threads);
But those thread entities have posts entities associated with them, which are not loaded due to lazy loading. If i do something like this:
var threads = _forumsDb.ForumsAccounts
.Include("Posts")
.Where(a => a.UserName == User.Identity.Name)
.SelectMany(u => u.Threads);
It's apparently not working, and those thread entities still have null Posts entities. How can i do it correctly?
You can't apply Include for properties in a projection (Select or SelectMany). You need to extend the projection to include the posts as an additional property:
var threads = _forumsDb.ForumsAccounts
.Where(a => a.UserName == User.Identity.Name)
.SelectMany(u => new
{
Threads = u.Threads,
ThreadPosts = u.Threads.Select(t => t.Posts)
})
.AsEnumerable()
.SelectMany(a => a.Threads)
.ToList();
If the relationship between Thread and Post is one-to-many EF will fill the Posts collection of the threads.
The query would be much easier though if you have an inverse ForumAccount property on the Thread entity:
var threads = _forumsDb.Threads
.Include("Posts")
.Where(t => t.ForumAccount.UserName == User.Identity.Name)
.ToList();
In this case eager loading with Include will work.

Resources