How do you selectively join in tables with linq? - linq

Let's say you have these two tables:
CARS
ID CAR_MODEL
11 Mustang
22 Camaro
33 F-150
PARTS
ID CAR_ID PART_NAME
1 11 Steering Wheel
2 22 Steering Wheel
3 22 Headlights
You need to create a search form. How would you you make this work:
var query = db.CARS.include(u => u.PARTS);
if(model.CarPartName != "")
query = query.where(u => u.PARTS.PART_NAME == model.CarPartName); //this doesn't work
return query.ToList();
There is currently a foreign key relationship between PARTS.CAR_ID and CARS.ID but for some reason when I attempt u.PARTS. I can't append on a column name.
Any idea how to make this optional join work?

That wouldn't work that way, because a car can have many parts, thus u.PARTS is returning a collection. There are many solutions to this problem, you can start by Cars collection (as you did), or you can start by PARTS collection. If you started with PARTS collection, it would look like:
var query = db.PARTS.Include(p => p.Car);
if(model.CarPartName != "")
query = query.Where(u => u.PART_NAME == model.CarPartName);
return query.Select(p => p.Car).Distinct().ToList();
With Cars:
var query = db.CARS.include(u => u.PARTS);
if(model.CarPartName != "")
query = query.Where(u => u.PARTS.Any( up => up.PART_NAME == model.CarPartName));
return query.ToList();
Note: I added the first one, just because I wanted to show what is going on.

Related

LINQ to Entities GroupBy, OrderByDescending, FirstOrDefault when

I have a following problem with LINQ to Entities. I am selecting records from DB, group them and then order them by Id descending. Then i want to select First item but only if Quantity is != 0. I think because i have to use FirstOrDefault i am getting wrong results but am not sure how to correct it.
DB
Now this query will give me ID's 2 and 1 grouped and 4 "grouped". What i need is to select FIRST items only if Quantity is != 0 and am not sure how to get that.
data = DbContext.Items.Where(x.WarehouseId == 1)
.GroupBy(x => x.ItemCode, (key, g) => g.OrderByDescending(y => y.Id).FirstOrDefault())
.OrderBy(parameters.SortExpression)
.Skip(parameters.Start)
.Take(parameters.Length);
If WarehouseID is 1 i need to get only row with ID 4 back. Any help is appreciated.
Edit: First i need to groupBy ItemCode, then i will have two groups for my case above. That's 1 and 2 for first group, and 4 for second group. Then i order them by Id descending and i get (2, 1), (4). Then i need to select first from group but only if Quantity is != 0. If Quantity is zero i don't want to select anything from a group.
Image to clarify what i need. I'm stuck on last step Take FristFrom each group only if quantity is != 0
EDIT: This is, what you want to do:
var data = DbContext.Items
.Where(p => p.WarehouseId == 1)
.GroupBy(p => p.ItemCode, (key, items) => new { Key = key, Items = items, ItemsCountWithQuantityZero = items.Where(p => p.Quantity == 0).Count() })
.Where(p => p.ItemsCountWithQuantityZero == 0)
.Select(p => p.Items.OrderByDescending(q => q.Id).First());

How to get a list of the grouped values in linq groupby?

Linq newbie here, struggling with my first GroupBy query.
I have a list of objects of type KeywordInstance which represents a keyword, and the ID of the database record to which the keyword was applied.
Keyword RecordID
macrophages 1
macrophages 2
cell cycle 3
map kinase 2
cell cycle 1
What I want is a collection of all keywords, with a list of the RecordIDs to which each keyword was applied.
Keyword RecordIDs
macrophages 1, 2
cell cycle 1, 3
map kinase 2
I tried using Linq to get it into a new object. I only managed to get the distinct keywords.
var keywords = allWords
.GroupBy(w => w.keyword)
.Select(g => new {keyword = g.Key});
The problem is that I can't seem to get the values of g in any way. g is of the type IGrouping<String, KeywordInstance> and by documentation, it only has the property Key, but not even the property Value. All the examples I have seen on the Internet for groupby just tell me to select g itself, but the result of
var keywords = allWords
.GroupBy(w => w.keyword)
.Select(g => new {keyword = g.Key, RecordIDs = g});
is not what I want.
Any try to get something out of g fails with the error message System.Linq.IGropuing<string, UserQuery.KeywordInstance> does not have a definition for [whatever I tried].
What am I doing wrong here?
I think you are close to you solution.
var keywords = allWords
.GroupBy(w => w.keyword)
.Select(g => new
{
keyword = g.Key,
RecordIDs = g.Select(c => c.ID)
});
Just Select the records you need.
The reason you are seeing the Keyword-column as well as the ID-column, is becuase it's part of g
var keywords = allWords.GroupBy(w => w.keyword);
foreach (var itm in keywords)
{
var list = itm.ToList();
//list returns all of the original properties/values objects from allwords.
//itm.key returns w.keyword
}

LINQ: Drill down to value in query results

I am using LINQ for the first time and have the following query:
var query = from c in context.Call
join d in context.CallData on c.CallID = d.CallID
where c.CallID == 123
select new {
c.CallID,
c.CallResult,
d.FieldID,
d.FieldValue
};
This returns data similar to the following
c.CallID c.CallResult d.FieldID d.FieldValue
123 Sale 345 John
123 Sale 346 Doe
123 Sale 347 888-222-3333
How can drill down to the row containing d.FieldID = 346 to get the value of d.FieldValue (346 being the LastName field)? I would like to use this value as the new value of a local variable:
// I know this doesn't work, just showing what I would like to accomplish
string LastName = query.Select(a => a.FieldID = 346).FieldValue;
Conversely, how can I change the value of c.CallResult once I have these results? Since it is a join, can I simply change one row of data or do I have to do a separate query?
For the first question you can move on from your query variable:
var fieldValue = query.Where(x => x.FieldID == 346).Single().FieldValue;
As for your second question: you must apply a change to the original entity object:
var call = context.Find(123);
// or context.Calls.Single(c => c.CallId == 123) if this is not DbContext.
call.CallResult = newValue;
context.SaveChanges();
Do you mean:
string LastName = query
.Where(a => a.FieldID = 346)
.Select(a => a.FieldValue)
.FirstOrDefault();

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

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.

What's the LINQ to select the latest item from a number of versioned items?

I've got a class like the following:
public class Invoice
{
public int InvoiceId {get;set;}
public int VersionId {get;set;}
}
Each time an Invoice is modified, the VersionId gets incremented, but the InvoiceId remains the same. So given an IEnumerable<Invoice> which has the following results:
InvoiceId VersionId
1 1
1 2
1 3
2 1
2 2
How can I get just the results:
InvoiceId VersionId
1 3
2 2
I.e. I want just the Invoices from the results which have the latest VersionId. I can easily do this in T-SQL, but cannot for the life of me work out the correct LINQ syntax. I'm using Entity Framework 4 Code First.
Order by the VersionId, group them by InvoiceId, then take the first result of each group. Try this:
var query = list.OrderByDescending(i => i.VersionId)
.GroupBy(i => i.InvoiceId)
.Select(g => g.First());
EDIT: how about this approach using Max?
var query = list.GroupBy(i => i.InvoiceId)
.Select(g => g.Single(i => i.VersionId == g.Max(o => o.VersionId)));
Try using FirstOrDefault or SingleOrDefault in place of Single as well... it would give the same result although Single shows the intention better.
EDIT: I've tested both these queries with LINQ to Entities. They seem to work, so perhaps the issue is something else?
Option 1:
var latestInvoices = invoices.GroupBy(i => i.InvoiceId)
.Select(group => group.OrderByDescending(i => i.VersionId)
.FirstOrDefault());
EDIT: Changed 'Last' to 'FirstOrDefault', LINQ to Entities has issues with the 'Last' query operator.
Option 2:
var invoices = from invoice in dc.Invoices
group invoice by invoice.InvoiceId into invoiceGroup
let maxVersion = invoiceGroup.Max(i => i.VersionId)
from candidate in invoiceGroup
where candidate.VersionId == maxVersion
select candidate;
My version:
var h = from i in Invoices
group i.VersionId by i.InvoiceId into grouping
select new {InvoiceId = grouping.Key, VersionId = grouping.Max()};
Update
As was mentioned by Ahmad in the comments, the above query will return a projection. The version below will return a IQueryable<Invoice>. I use composition to build the query because I think it is more clear.
var maxVersions = from i in Invoices
group i.VersionId by i.InvoiceId into grouping
select new {InvoiceId = grouping.Key,
VersionId = grouping.Max()};
var latestInvoices = from i in Invoices
join m in maxVersions
on new {i.InvoiceId, i.VersionId} equals
new {m.InvoiceId, m.VersionId}
select i;

Resources