Linq Where clause with two conditions, not working - linq

I have three tables:
[Products]
Id
Title
Info
Price
[ProductCategories]
Id
Title
ParentId
[ProductsInCategories]
Id
ProductId
ProductCategoryId
I'm trying to read a selection of products in a certain category. The code I have doesn't concider the ProductCategoryId, and just loads all products from the Product table. Where's my mistake?:
The id-variable is a method argument. The value coming in is correct.
var ProdInCat = from p in _context.Products
from pc in _context.ProductsInCategories
.Where(x => p.Id == x.ProductId && x.ProductCategoryId == id)
.DefaultIfEmpty()
select new
{
p.Id,
p.Title,
p.Info,
p.Price
};

You can join products with a filtered list of products in categories based on your external (computed) id.
from p in _context.Products
join pc in _context.ProductsInCategories.Where(pic => pic.ProductCategoryId == id).DefaultIfEmpty()
on p.Id equals pc.ProductId
More about how can you achieve this at the following link: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/join-clause

Use Inner join expression
var ProdInCat = from p in _context.Products
from pc in _context.ProductsInCategories.Where(x => x.ProductCategoryId == id).DefaultIfEmpty()
on p.Id equals pc.ProductID
select new {
p.Id,
p.Title,
p.Info,
p.Price
};

Related

joining multiple tables, then grouping and suming in linq

How could I get this SQL query right in LINQ?
SQL query:
select c.Name, sum(t.value)
from Categories c
join Items i on c.Id = i.CategoryId
join Transactions t on t.ItemId = i.Id
where datepart(YEAR, t.CreatedTime) = 2016
and datepart(MONTH, t.CreatedTime) = 2
group by c.Name
I've tried to do the same in LINQ and got this:
var query = from cat in _context.Categories
join item in _context.Items on cat.Id equals item.CategoryId
join trans in _context.Transactions on item.Id equals trans.ItemId
where trans.CreatedTime.Month == e.RowIndex && trans.CreatedTime.Year == 2016
group new { cat.Name, trans.Value } by cat.Name into g
select new
{
Value = g.Sum(entry => entry.Value)
};
But it seems to be totally wrong, besides not giving me back the category names, it returns wrong values for the sums as well.
I have three tables:
Categories with columns Id and Name
Items with columns Id, Name, CategoryId, LastValue, IsIncome,
Transactions with columns Id, ItemId, Value, CreatedTime, IsIncome

LINQ Select Row with Max date per group

I want to return all the values from my "Categories" table and join that to my "CategorySelections" table to display all the categories and whether the specified user selected them or not.
A complication (which I don't know how to deal with in LINQ) is that the user could have changed his selection/deselection of a particular category over time...each change would have logged in the "CategorySelections" table with a date stamp.
I am after the last selection status.
The following SQL query does what I want:
SELECT cs.UserId, c.CategoryId, m.MaxDate, cs.IsSelected
FROM [myDB].[dbo].[Categories] c
LEFT JOIN [myDB].[dbo].[CategorySelections] cs
ON c.CategoryID = cs.CategoryID AND cs.UserID = 7
INNER JOIN
(
SELECT UserId, CategoryId, Max(CreatedOn) as MaxDate
FROM [myDB].[dbo].[CategorySelections]
GROUP BY UserId, CategoryId
) m
ON cs.UserID = m.UserID AND cs.CategoryID = m.CategoryID AND cs.CreatedOn = m.MaxDate
ORDER BY cs.CategoryI
I need some help getting this done in LINQ.
Below is my attempt, which returns all the selections instead of just the last per category.
var query = from c in db.Category
join cs in db.CategorySelection.Where(x => x.UserID == WebSecurity.CurrentUserId)
on c.CategoryID equals cs.CategoryID into JoinedCategory
from cs in JoinedCategory.DefaultIfEmpty()
select new Selection() { CategoryID = c.CategoryID, CategoryName = c.CategoryName ,IsSelected = cs != null ? cs.IsSelected : false }
I am working in MVC; the "new Selection()" refers to my Model
You may add WHERE statement:
where cs.CreatedOn == CategorySelections.Where(t => t.CategoryId == cs.CategoryId).Max(r => r.CreatedOn)

How to write this LINQ Query in a better way

I have one Linq Query. When I run the query, Only for 10 records its taking 13 seconds to extract the data to the model. I need to know the query which I wrote is good for performance or not. Please guide me what i am doing wrong.
Code
var stocktakelist = (from a in Db.Stocktakes
select new ExportStock
{
Id = a.Id,
ItemNo = a.ItemNo,
AdminId = (from admin in Db.AdminAccounts where admin.Id == a.Id select admin.Name).FirstOrDefault(),
CreatedOn = a.CreatedOn,
Status = (from items in Db.Items where items.ItemNo == a.ItemNo select items.ItemStatu.Description).FirstOrDefault(),
Title = (from tit in Db.BibContents where tit.BibId == (from bibs in Db.Items where bibs.ItemNo == a.ItemNo select bibs.BibId).FirstOrDefault() && tit.TagNo == "245" && tit.Sfld == "a" select tit.Value).FirstOrDefault() // This line of Query only makes the performance Issue
}
).ToList();
Thanks
The reason this is so slow is because it is running the 3 inner LINQ statements for every item in the outer LINQ statement.
Using LINQ joins will run only 4 queries and then link them together, which is faster.
To find out how to join, there are plenty of resources on the Internet depending on the type of LINQ you are using.
If you're retrieving this data from a SQL server, perhaps consider doing this intensive work in SQL - this is what SQL was designed for and it's much quicker than .NET. EDIT: As highlighted below, the work is done in SQL if using LINQ to SQL/Entities and using the correct join syntax.
I was trying to create the corresponding query with some joins for practice.
I cannot test it and i'm not 100% sure that this query will you get the result
you are hoping for but maybe at least it will give you a hint on how to write
joins with linq.
from a in Db.Stocktakes
join admin in Db.AdminAccounts
on a.Id equals admin.Id
into adminJoinData
from adminJoinRecord in adminJoinData.DefaultIfEmpty( )
join items in Db.Items
on a.ItemNo equals items.ItemNo
into itemsJoinData
from itemsJoinRecord in itemsJoinData.DefaultIfEmpty( )
join title in Db.BibContents
(
from subQuery in Db.BibContents
where subQuery.TagNo == "245"
where subQuery.Sfld == "a"
select subquery
)
on title.BibId equals itemsJoinRecord.BidId
into titleJoinData
from titleJoinRecord in titleJoinData.DefaultIfEmpty( )
select new ExportStock( )
{
Id = a.Id,
ItemNo = a.ItemNo,
AdminId = adminJoinRecord.Name,
CreatedOn = a.CreatedOn,
Status = itemsJoinRecord.ImemStatu.Description,
Title = titleJoinRecord.Value
}
As others have said, you should use Left Outer Joins in your LINQ just as you would if writing it in SQL.
Your query above will end up looking roughly like this once converted (this is untested, but gives the basic idea):
var a = from a in Db.Stocktakes
join admin in Db.AdminAccounts on admin.Id equals a.Id into tmpAdmin
from ad in tmpAdmin.DefaultIfEmpty()
join item in Db.Items on item.ItemNo equals a.ItemNo into tmpItem
from it in tmpItem.DefaultIfEmpty()
join title in Db.BibContents on bib.BibId equals items.BibId into tmpTitle
from ti in tmpTitle.DefaultIfEmpty()
where ti.TagNo == "245"
&& ti.Sfld == "a"
select new ExportStock
{
Id = a.Id,
ItemNo = a.ItemNo,
AdminId = ad == null ? default(int?) : ad.Id,
CreatedOn = a.CreatedOn,
Status = it == null ? default(string) : it.ItemStatus.Description,
Title = ti == null ? default(string) : ti.Value
};
Using lambda expressions your query will look like this:
Db.Stocktakes
.Join(Db.AdminAccounts, a => a.Id, b => b.Id, (a,b) => new { a, AdminId = b.Name })
.Join(Db.Items, a => a.ItemNo, b => b.ItemNo, (a,b) => new { a, Status = b.ItemStatus.Description, BidId = b.BibId })
.Join(Db.BibContents, a => a.BibId, b => b.BibId, (a,b) => new { a, Value = b.Value, TagNo = b.TagNo, Sfld = b.Sfld })
.Where(a => a.TagNo == "245" && a.Sfld == "a")
.Select(a =>
new ExportStock { Id = a.Id,
ItemNo = a.ItemNo,
AdminId = a.AdminId,
CreatedOn = a.CreatedOn,
Status = a.Status,
Title = a.Value
}
).ToList();

LINQ Query using 3 tables to get grouped data

So I am trying to get a list of cities and the name of the best sold product in each city. There are 3 tables and I cant seem to group them properly and get the count.
Here is what I have so far:
var result9 = (from p in shop.Tb_Purchases
join c in shop.Tb_PreferredCustomer on p.Cust_ID equals c.Cust_ID
join ap in shop.Tb_AvailableProduct on p.Prod_ID equals ap.Prod_ID
group ap by new { c.City, ap.Name } into g
select new { City = g.Key.City, Name = g.Key.Name, NumOf = g.Count() }).ToList();
and this gives me every product sold in each city and how many of them were sold, however i need only one city and the one product that was sold the most in it.
One solution is to group by just the city and then find each city's best product in a subquery.
var result9 = (from p in shop.Tb_Purchases
join c in shop.Tb_PreferredCustomer on p.Cust_ID equals c.Cust_ID
join ap in shop.Tb_AvailableProduct on p.Prod_ID equals ap.Prod_ID
group ap by c.City into g
let largestProductGroup = g.GroupBy(x => x.Name)
.OrderByDescending(x => x.Count())
.First()
select new
{
City = g.Key.City,
Name = largestProductGroup.Key.Name,
NumOf = largestProductGroup.Count()
}).ToList();

linq expression error:cant display column?

i am trying to join my linq query with another table. How can I display the name in the Customer table? I am getting an error: does not contain a definition for 'Name'?
from p in Purchases
join c in Customers on p.CustomerID equals c.ID
group p by p.Date.Year into SalesPerYear
select new {
customername= SalesPerYear.First().Name,
customerid= SalesPerYear.First().CustomerID,
totalsales= SalesPerYear.Sum(x=>x.Price)
}
You're grouping p (i.e. purchases) by date - so the customer details are no longer present.
Try this instead:
from p in Purchases
join c in Customers on p.CustomerID equals c.ID
group new { p, c } by p.Date.Year into SalesPerYear
select new {
CustomerName = SalesPerYear.First().c.Name,
CustomerId = SalesPerYear.First().p.CustomerID,
TotalSales = SalesPerYear.Sum(x => x.p.Price)
}
Or alternatively:
from p in Purchases
join c in Customers on p.CustomerID equals c.ID
group new { p.CustomerId, p.Price, c.CustomerName }
by p.Date.Year into SalesPerYear
select new {
SalesPerYear.First().CustomerName,
SalesPerYear.First().CustomerId
TotalSales = SalesPerYear.Sum(x => x.Price)
}
SalesPerYear is an IGrouping. You need to access Name, CustomerID etc off the Value property of the IGrouping.

Resources