LINQ query from two related lists with specific criteria - c#

Using SQL Server and C#:
I have 3 tables: Employee (EmployeeId, JobDescription), CashAllowance (CashAllowanceId) and EmployeeCashAllowance (EmployeeId, CashAllowanceId, ValueTaken, DateAdded).
Employee has (EmployeeeId) as primary key, CashAllowance has (CashAllowanceId) as primary key, EmployeeCashAllowance has 2 foreign keys (EmployeeId and CashAllowanceId) related to the first 2 tables.
I need to get the list of (EmployeeCashAllowance) in a specific date + for specific CashAllowanceId + for employees having JobDescription = "Dev"
I need to achieve this in a LINQ query on lists filled from DB where list of all EmployeeCashAllowance is a property of the Employee object (each Employee object has List ListEmployeeCashAllowances as a property). What I wrote was this:
var sumValues = (from e in Employees
where (e.JobDescription == "Dev")
from c in e.ListEmployeeCashAllowances
where (c.EmployeeId == e.EmployeeId && c.CashAllowanceId == selectedCashAllowanceId && c.DateAdded == selectedDate)
select c).ToList();
But this is not working as I expected, it's returning all rows in Employee and EmployeeCashAllowance whatever the selected criteria is (even is JobDescription is not Dev and CashAllowanceId is not the selectedCashAllowanceId).
Where did I go wrong with this?

You better use join I suppose try something like this , I haven't tested so it must look like this
var sumValues = (from e in Employees
join c in EmployeeCashAllowances on e.EmployeeId equals c.EmployeeId
where ( c.CashAllowanceId == selectedCashAllowanceId && c.DateAdded == selectedDate && e.JobDescription == "Dev")
select c).ToList();

Not Tested, Just gave a try
var results = from e in Employees
from ec in e.EmployeeCashAllowances
where (e.EmployeeId == ec.EmployeeId && ec.CashAllowanceId == selectedCashAllowanceId && ec.DateAdded == selectedDate && e.JobDescription == "Dev")
select ec;

Well, I didn't expect this, but I had to change from the query to get what I want, with the restriction that I had use "Sum" and get sum of values in the list of EmployeeCashAllowance resulted:
Employees.SelectMany(e => e.ListEmployeeCashAllowances).Where(lc => lc.CashAllowanceId == selectedCashAllowanceId).Select(c => c.ValueTaken).Sum();
Note that if I don't use Sum the list returned will contain all items in all the ListEmployeeCashAllowances.

It becomes clear when you clean the formatting:
var sumValues =
from e in Employees
where
e.JobDescription == "Dev"
from c in e.ListEmployeeCashAllowances // Gotcha!!!
where
c.EmployeeId == e.EmployeeId && // Is that really necessary?
c.CashAllowanceId == selectedCashAllowanceId &&
c.DateAdded == selectedDate
select c;
Normally I use LINQ methods like:
var query = Employees
.Where(e => e.JobDescription == "Dev")
.SelectMany(e => e.ListEmployeeCashAllowances)
.Where(c =>
c.CashAllowanceId == selectedCashAllowanceId &&
c.DateAdded == selectedDate);
But 'e.ListEmployeeCashAllowances' still can be selecting all users...
A long shot, without knowing your environment, could be:
var query = Employees
.SelectMany(e => e.ListEmployeeCashAllowances)
.Where(c =>
c.JobDescription == "Dev" &&
c.CashAllowanceId == selectedCashAllowanceId &&
c.DateAdded == selectedDate);
Resolving what to do with 'query', you can simply do:
var sumValues = query.Sum(c => c.ValueTaken);

Related

How to get data from a Table if they exist in another Table C# LINQ

I have a two tables Customers, and ConnectedCustomers. That means customers can connect with each other.
My question is how can I get all Customers which are related to Chris (id = 3).
I should as a results get Bob and John..
I've tried something like this:
query = _context.Customers.Where(c =>_context.ConnectedCustomers.Any(cc => cc.Connected_Customer1.Equals(3) || cc.Connected_Customer2.Equals(3)));
But this is not working it returns too many rows..
Expected result for id 3 is BOB AND JOHN because they are connected
with id 3.
Thanks guys
Cheers
var answer = (from cc in _context.ConnectedCustomers
join c1 in _context.Customers on cc.Connected_Customer1 equals c1.id
join c2 in _context.Customers on cc.Connected_Customer2 equals c2.id
where c1.id == 3 || c2.id == 3
select c1.id == 3 ? c2 : c1
).ToList();
The problem in your query is you are querying from Customer table. When you are querying through Customer, what it's doing is getting all the possible combinations of Fk with Connected_Customer1 and Connected_Customer2 which satisfies the condition.
What you should be doing is query from ConnectedCustomers.
Try this:
var result = _context.ConnectedCustomers
.Where(x => x.Connected_Customer1.Equals(3) || x.Connected_Customer2.Equals(3))
.Select(x=>x.Customers)
.ToList();
You never use the id of the user to check (c), so you have to add a condition like this :
var query = _context.Customers.Where(customer => _context.ConnectedCustomers.Any(cc =>
cc.Connected_Customer1.Equals(customer.Id) && cc.Connected_Customer2.Equals(3) ||
cc.Connected_Customer1.Equals(3) && cc.Connected_Customer2.Equals(customer.Id))).ToList();
_context.Customers.Where(c => c.Id == _context.ConnectedCustomers.Where(cc => cc.Connected_Customer1 == 3).Select(cc => cc.Connected_Customer2))
Try that.

Linq where clause 2 condition order?

i tried this linq code for get questions
return (from i in db.Question
where i.Id == questionId || (i.RelatedId == questionId && i.IsAccept == isAccept)
select i)
.ToList();
I got 2 question but their order was wrong. Can I say to Linq get first where clause then second condition?
Note: First get i.Id == questionId then get second condition rows
Note2: Id and RelatedId is Guid. I am trying to do this without order by and single query
You can keep a variable which shows the returned row, satisfies which of your conditions. Then select the row together with this variable(here named as 'order') in an anonymous type. Finally, OrderBy your results based on your variable and Select your row.
return (from i in db.Question
where i.Id == questionId || (i.RelatedId == questionId && i.IsAccept == isAccept)
let order = i.Id == questionId ? 1 : 0
select new {i, order}).Orderby(a => a.order).Select(a => a.i)
.ToList();
For database queries you can use asynchronous queries and combine result later, which makes code little bid more comprehensible and asynchronous queries will be executed almost simultaneously.
var byIdTask =
db.Question.Where(question => question.Id == questionId).ToListAsync();
var relatedTask =
db.Question.Where(question => question.RelatedId = questionId)
.Where(question => question.IsAccept == isAccept)
.ToListAsync();
await Task.WhenAll(new[] byIdTask, relatedTask);
var allQuestions = byIdTask.Result.Concat(relatedTask.Result).ToList();
i tried this and looking work
List<Question> final = new List<Question>();
var result = (from i in db.Question where i.Id == questionId || (i.RelatedId == questionId && i.IsAccept == isAccept) select i).ToList();
final.Add(result.Where(a => a.Id == questionId).SingleOrDefault());
final.Add(result.Where(a => a.Id != questionId).SingleOrDefault());
im getting all with single query and adding them to list with conditions

IQueryable with distinct records and ordered

I've been trying to get an IQueryable with distinct and ordered values, but I've found that I can't apply a distinct after orderby or I'll lose the order.
The last query I tried was the following:
IQueryable<gbd_Pages> Listpagespages =
(from c in _db.gbd_Content
where c.IsActive == true && c.IsDeleted == false &&
c.gbd_Template_Fields.SortOrder == sortOrder
orderby c.Content ascending
select c.gbd_Pages);
With this I get repeated results.
The table I want returned is gbd_Pages which has a relation of 1 to many with gbd_Content.
With this i mean that gbd_Content will have a foreign key that will have the primary key of gbd_Pages.
I need to do a sortOrder by the table gbd_Template_Fields wich has a relation 1 to 1 with gbd_Content.
Is there a way for me to do this? I need it to be an IQueryable without converting to IEnumerable or list.
I believe what you need is something like this:
var query =
from p in _db.gbd_Pages
from pc in (from c in p.gbd_Content
where c.IsActive == true && c.IsDeleted == false &&
c.gbd_Template_Fields.SortOrder == sortOrder
orderby c.Content ascending
select c).Take(1)
orderby pc.Content ascending
select p;
So you start from one side of the relationship (to avoid the need of Distinct), then you select a single record from the many side matching the criteria and having the smaller value of the sorting field (using ordered subquery + Take(1)), and finally sort the result using the sort field value from that single child record.
I'm assuming you have inverse collection navigation property from gbd_Pages to gbd_Content. If you don't, replace the p.gbd_Content with _db.gbd_Content where c.[gbd_Content_FK] == p.[PK].
Your primary from needs to be the gbd_Pages table, rather than gbp_Content if those are the results that you want to return. I'll have to assume a foreign key here but you'd want to change to something like;
IQueryable<gbd_Pages> Listpagespages = _db.gbp_Pages
.Where(p => (from c in _db.gbd_Content
where c.IsActive == true
&& c.IsDeleted == false
select c.gbd_Pages.PrimaryKeyID)
.Any())
.Select(p => new
{
// select specific fields here...
p,
SortCol = _db.gbp_Content
.FirstOrDefault(c => c.PrimaryKeyID)
.Where(c => c.IsActive == true && c.IsDeleted == false &&
c.gbd_Template_Fields.SortOrder == sortOrder)
.Select(c => c.Content)
})
.OrderBy(v => c.SortCol);
Try this:
IQueryable<gbd_Pages> Listpagespages = _db.gbd_Content
.Select (c=>new { c.gbd_Pages })
.Where(c=>c.IsActive == true && c.IsDeleted == false &&c.gbd_Template_Fields.SortOrder == sortOrder)
.Distinct()
.OrderBy(c => c.Content)
.Select(c => c.gbd_Pages)

LINQ to Entity join query

I have the following setup:
Table ShoeAreas that has columns ShoeId and MaterialId.
Table Shoes that has columns ID and Status.
I have a method that takes one argument - materialId and the goal is to determine if there is a record in ShoeAreas with a MaterialId equal to the one passed like an argument. And if such a record (or records most probably) exist if they are relateed to shoe from Shoes withStatus` = Production.
I tried this :
return shoeService.All().
Join(shoeAreaService.All(),
s => s.ID,
sa => sa.ShoeId,
(s, sa) => (sa.MaterialId == matId)).
Any(s => (s.Status == (byte)EntityStatusProd.Production)));
But I get error on the Any.. line saying } expected and also this is my second Linq to Entity query that I write so I have doubts if it's syntax problem or the query is wrong itself.
You are returning IEnumerable<bool> from Join method (values of condition sa.MaterialId == matId). Create anonymous type which will hold both joined entities instead:
return shoeService.All()
.Join(shoeAreaService.All(),
s => s.ID,
sa => sa.ShoeId,
(s, sa) => new { s, sa }) // here
.Any(x => (x.sa.MaterialId == matId) &&
(x.s.Status == (byte)EntityStatusProd.Production)));
you can try this: (linq )
from shoe in Shoes
join shoeArea in ShoesArea on shoe.ID equals shoeArea.ShoeID
where shoeArea.MeterialID == matID && shoe.Status == (byte)EntityStatusProd.Production
select new {shoe.ID,shoe.Status};
return shoeService.All().Any(s => shoeAreaService.All()
.Any(sa => sa.MaterialId == matId
&& s.Id == sa.ShoeId)
&& s.Status == (byte)EntityStatusProd.Production);

Linq expression to get all items in a IList where one field is in another List

This collection contains the entire database of products:
IList<Products> allProducts;
This contains just the guids of all the products for this user:
IList<Guid> usersProducts;
Below is the psuedo code of what I need, i.e. all the product classes in a IList for a given user and the product is also of type == 1.
var filteredProducts = (p from allProducts
where p.Id in usersProducts && p.Type == 1).List();
I can't figure out how to do the SQL query "WHERE IN (..., ...,)
var filteredProducts = (from p in allProducts
where usersProducts.Contains(p.Id) && p.Type == 1
select p).ToList();
#Samich's answer is correct. It's just a personal preference, but I prefer the method syntax...
var filteredProducts = allProducts.Where(p => p.Type == 1 && userProducts.Contains(p.Id)).ToList();
Also, for performance reasons, I swapped the order of your conditionals. If p.Type doesn't equal 1, the Contains won't execute.
Sounds like you just want a join.
var filteredProducts = (from p in allProducts
join u in usersProducts on p.Id equals u.Id
where p.Type == 1
select p).ToList();

Categories