Entity Framework 4 - What is the syntax for joining 2 tables then paging them? - linq

I have the following linq-to-entities query with 2 joined tables that I would like to add pagination to:
IQueryable<ProductInventory> data = from inventory in objContext.ProductInventory
join variant in objContext.Variants
on inventory.VariantId equals variant.id
where inventory.ProductId == productId
where inventory.StoreId == storeId
orderby variant.SortOrder
select inventory;
I realize I need to use the .Join() extension method and then call .OrderBy().Skip().Take() to do this, I am just gettting tripped up on the syntax of Join() and can't seem to find any examples (either online or in books).
NOTE: The reason I am joining the tables is to do the sorting. If there is a better way to sort based on a value in a related table than join, please include it in your answer.
2 Possible Solutions
I guess this one is just a matter of readability, but both of these will work and are semantically identical.
1
IQueryable<ProductInventory> data = objContext.ProductInventory
.Where(y => y.ProductId == productId)
.Where(y => y.StoreId == storeId)
.Join(objContext.Variants,
pi => pi.VariantId,
v => v.id,
(pi, v) => new { Inventory = pi, Variant = v })
.OrderBy(y => y.Variant.SortOrder)
.Skip(skip)
.Take(take)
.Select(x => x.Inventory);
2
var query = from inventory in objContext.ProductInventory
where inventory.ProductId == productId
where inventory.StoreId == storeId
join variant in objContext.Variants
on inventory.VariantId equals variant.id
orderby variant.SortOrder
select inventory;
var paged = query.Skip(skip).Take(take);
Kudos to Khumesh and Pravin for helping with this. Thanks to the rest for contributing.

Define the join in your mapping, and then use it. You really don't get anything by using the Join method - instead, use the Include method. It's much nicer.
var data = objContext.ProductInventory.Include("Variant")
.Where(i => i.ProductId == productId && i.StoreId == storeId)
.OrderBy(j => j.Variant.SortOrder)
.Skip(x)
.Take(y);

Add following line to your query
var pagedQuery = data.Skip(PageIndex * PageSize).Take(PageSize);
The data variable is IQueryable, so you can put add skip & take method on it. And if you have relationship between Product & Variant, you donot really require to have join explicitly, you can refer the variant something like this
IQueryable<ProductInventory> data =
from inventory in objContext.ProductInventory
where inventory.ProductId == productId && inventory.StoreId == storeId
orderby inventory.variant.SortOrder
select new()
{
property1 = inventory.Variant.VariantId,
//rest of the properties go here
}
pagedQuery = data.Skip(PageIndex * PageSize).Take(PageSize);

My answer here based on the answer that is marked as true
but here I add a new best practice of the code above
var data= (from c in db.Categorie.AsQueryable().Join(db.CategoryMap,
cat=> cat.CategoryId, catmap => catmap.ChildCategoryId,
cat, catmap) => new { Category = cat, CategoryMap = catmap })
select (c => c.Category)
this is the best practice to use the Linq to entity because when you add AsQueryable() to your code; system will converts a generic System.Collections.Generic.IEnumerable to a generic System.Linq.IQueryable which is better for .Net engine to build this query at run time
thank you Mr. Khumesh Kumawat

You would simply use your Skip(itemsInPage * pageNo).Take(itemsInPage) to do paging.

Related

LINQTOSQL Help needed

I'm trying to add a column to the following LINQ expression. I want the column to contain a string concatenation of a text value in a many table called WasteItems. The join would be on "Waste.WasteId = WasteItem.WasteId". My problem is I need to display in a single dynamic column a string such as "EW (5); EX (3)" if there was 8 records in WasteItem and the column containing the 2 character string was called WasteItem.EWC. Hope that makes sense, there must be an efficient way since I realise LINQ is very powerfull. I'm new to it and not sure how to start or go about this:
return from waste in this._db.Wastes
where (from u in _db.UsersToSites.Where(p => p.UserId == userId && p.SystemTypeId == SystemType.W)
select u.SiteId)
.Contains(waste.SiteId)
orderby waste.Entered descending select waste;
THANKS IN ADVANCE
Something like this should do:
wastes.GroupJoin(db.WasteItems, w => w.WastId, wi => wi.WasteId, (w,wi) => new { w, wi })
.AsEnumerable()
.Select(x => new
{
x.w.Name,
Items = string.Join(", ", x.wi.GroupBy(wi => wi.EWC).Select(g => string.Format("{0} ({1})", g.Key, g.Count())))
})
Where wastes is the result from your query. The AsEnumerable() is necessary because Entity Framework can not handle string.Join, so that part must be dealt with in memory.
I could not check the syntax, obviously, but at least it may show you the way to go.

Linq, how to write regular query with aggregate function?

on my page, I need to show a list of tools.
var tools = _toolRepository.GetAll().Where(t => t.IsActive == true).OrderByDescending(t => t.PostDate).Take(50).ToList();
I also need to show how many Votes each tool got, I can write a separate query to do that, but that will double the number of queries.
is there a way to combine them together?
here's my database structure.
Tool
id
name
postDate
ToolVote
id
ToolId
UserId
How about:
var tools = _toolRepository.GetAll().Where(t => t.IsActive == true).OrderByDescending(t => t.PostDate).Take(50).Select(tool => new { Tool = tool, votes = ToolVotes.Where(tv => tv.ToolID == tool.ToolID)} ).ToList();
Assuming that you have 1 to * relationship you can use left join.
I can't test this query but it should look something like this
var query = (from tool in _toolRepository.GetAll().Where(t => t.IsActive == true).OrderByDescending(t => t.PostDate).Take(50)
join toolVote in toolVotes on tool.id equals toolVote.ToolId
into toolsTemp
from toolTemp in toolsTemp.DefaultIfEmpty()
select new { Tool = tool, Count = toolTemp == null ? 0 : toolTemp.Count})
.ToList();

complex orderby that links to another table

I have the following query to start with:
var query = from p in db.Products
from pc in p.NpProductCategories
where pc.CategoryId == categoryId
select p;
I'm applying some more filtering on it and in the end I want to sort the results:
if (orderBy == ProductSortingEnum.Name)
query = query.OrderBy(x => x.Name);
else
query = query.OrderBy(............);
My big problem (coming from not knowing linq too good) is the ELSE here. How can I sort results by a column that is not in the current result set? I would like to somehow link to another linq query in the orderby. The sorting I'm trying to achive is to link to NpProductVariants query using the ProductId to match between NpProductVariant and Products
and sort by the Price of the NpProductVariant
Assuming you have the relationship set up in the dbml...
For one to one (and many to one):
query = query.OrderBy(p => p.NpProductVariant.Price);
For one to many:
query = query.OrderBy(p => p.NpProductVariants.Select(v => v.Price).Max());
Also:
var query =
from p in db.Products
where p.NpProductCategories.Any(pc => pc.CategoryId == categoryId)
select p;
I think you can hook your Join to your query as long as it is returning the same thing. So maybe something like (I'm not 100 % sure since I haven't tried it):
query = from i1 in query
join i2 in query2 on i1.PropertyToJoin equals i2.PropertyToJoin
orderby i1.OrderProp1, i2.OrderProp2
select i1;
But I think it might be a good idea to check the generated sql so it is still effective.

linq sort many 2 many question

My main entity is called [Contract]. Contract has a many 2 many relationship w/ [Service].
When I query for a list of Contracts I grab the 1st Service available like this:
IQueryable<Contract> q = ctx.Contracts.Skip(startRow - 1).Take(pgSize);
q.Select(c =>
new ContractSearchResult()
{
ContractID = c.ContractID,
FirstService = c.Contract2Service.FirstOrDefault().Service.Name,
ServiceCount = c.Contract2Service.Count,
}
).ToList();
(When I display this list I show the FirstService if there's only 1. If > 1 I show "My1stService (3)" to show I'm seeing the 1st of 3 services) This works fine.
My question is this:
Is there any way to sort by FirstService? Or is this impossible? I haven't found a way of expressing this in linq and allow for paging.
Any help would be appreciated.
You need to OrderBy before you page.
var result = ctx.Contracts
.Select(c =>
new ContractSearchResult()
{
ContractID = c.ContractID,
FirstService = c.Contract2Service.FirstOrDefault().Service.Name,
ServiceCount = c.Contract2Service.Count,
})
.OrderBy(x => x.FirstService)
.Skip(startRow - 1)
.Take(pgSize)
.ToList();
Also note that FirstOrDefault can return null and you should check for that. If you know that it will (or should) never be null then use First instead.
q.OrderBy(c => c.Contract2Service.FirstOrDefault().Service.Name)
.Select(c =>
new ContractSearchResult()
{
ContractID = c.ContractID,
FirstService = c.Contract2Service.FirstOrDefault().Service.Name,
ServiceCount = c.Contract2Service.Count,
}
)
.ToList();
Unless I'm missing something in your question (which is possible), it should be as simple as calling OrderBy() after your call to Select()

How to write linq query based on EF?

Suppose I have three tables:
Person(pid, ...)
PersonAddress(pid, aid,...)
Address(aid, ...)
Then I want to get the person address like sql:
select a.* from address a join PersonAddress pa on a.addressID=pa.addressID
where pa.personID = myPersonID
Use Entity Framework to create Entity model, then want to write a linq equivalent as above sql.
I tried it in following way:
var addresses = this.GetAddress();
var personaddresses = this.GetPersonAddress();
var query = from ad in addresses
from pa in personaddresses
where ((ad.AddressID == pa.AddressID)&&(pa.PersonID==person.personID))
select ad;
but I got error. Or I try to start from:
var result = this.Context.Address;
var result = result.Join .... //how to write linq in this way?
How to write the linq?
This is untested but if you have all of your relationships setup and you create the model (I have used Model as the name for this) from this you should be able to use the following:
var values = this.Model.Address.Select(a => a.PersonAddress.Where(pa => pa.Id == myPersonID));
You almost never use join in LINQ to Entities.
Try:
var q = from p in Context.People
where p.PersonId == personId
from a in p.Addresses // presumes p.Addresses is 1..*
select a;
Assuming you have three entities: Person, PersonAddress and Address, here is a query that should meet your needs (this example assumes an Entity Framework context named context):
var values = context.PersonAddress.Where(pa => pa.Person.PersonId == myPersonId).Select(pa => pa.Address);
However, if the PersonAddress table exists as a pure many-to-many relationship table (i.e. contains only keys), you'd be better off setting up your Entity Framework model in such a way that the intermediate table isn't necessary, which would leave you with the much simpler:
var values = context.Person.Where(p => p.PersonId == myPersonId).Addresses;
Based on the additional feedback
Because you need to include the country table, you should originate your query from the Address table. In that case:
var values = context.Address.Where(a => a.PersonAddress.Where(pa => pa.Product.Id == myProductId).Count() > 0)
To include the Country table in the result:
var values = context.Address.Include("Country").Where(a => a.PersonAddress.Where(pa => pa.Product.Id == myProductId).Count() > 0)

Resources