Using the results linq query in another linq query - c#

Good day!
Tell me whether it is possible to use the result of a Linq query in another Linq query?
For example:
List<Member> memberList = db.Members.Where(m=>m.Year == 20013).ToList();
var result = (from members in memberList
from documents in Archive.Where(d=>d.MemberId = members.Id).DefaultIfEmpty()
select new ArchiveRecord
{
member = members,
documentId = documentsId
}).ToList();
At the same time the implementation of the second request falls with the error (There is already an open DataReader associated with this Command which must be closed first).
Please tell me where I made mistakes.
Thank you!

I don't know how to make nested linq query but i have solution for you.
Try this:
List<Member> memberList = db.Members.Where(m => m.Year == 20013).ToList();
var membersIds = memberList.Select(m => m.Id);
var result = (from documents in Archive.Where(d => membersIds.Contains(d.MemberId)).DefultIfEmpty()
select new ArchiveRecord
{
member = members,
documentId = documentsId
}).ToList();

Enable Multiple Active Result Sets ("MultipleActiveResultSets=True") in your connection string:
https://msdn.microsoft.com/en-us/library/h32h3abf(v=vs.110).aspx

I believe this should work (Single query):
var memberList = db.Members.Where(m=>m.Year == 20013);
var result = (from members in memberList
from documents in Archive.Where(d=>d.MemberId = members.Id).DefaultIfEmpty()
select new ArchiveRecord
{
member = members,
documentId = documentsId
}).ToList();
This definitely should work (Single query):
var result = (from members in db.Members
from documents in Archive.Where(d=>d.MemberId = members.Id).DefaultIfEmpty()
where members.year == 20013
select new ArchiveRecord
{
member = members,
documentId = documentsId
}).ToList();
and this (Two queries):
var membersIds = db.Members
.Where(m => m.Year == 20013)
.Select(m => m.Id)
.ToList();
var result = (from documents in Archive.Where(d => membersIds.Contains(d.MemberId)).DefultIfEmpty()
select new ArchiveRecord
{
member = members,
documentId = documentsId
}).ToList();
and this assuming you have created a navigation property on members to archive, and it is a 1-many relationship (Single query):
var result=db.Archives
.Where(a=>a.Member.Year==20013)
.Select(a=> new ArchiveRecord {
member=a.Member,
documentId=a.documentsId
}).ToList();

Related

C# LINQ to SQL - one to many relationship

I have troubles creating this query in LINQ:
USE Northwind
GO
SELECT emp.FirstName, emp.LastName, tr.TerritoryDescription, reg.RegionDescription
FROM Employees emp
INNER JOIN EmployeeTerritories empt ON empt.EmployeeID = emp.EmployeeID
INNER JOIN Territories tr ON tr.TerritoryID = empt.TerritoryID
INNER JOIN Region reg ON reg.RegionID = tr.RegionID
This is my current creation:
var query = await context
.Employees
.Select(x => new
{
x.FirstName,
x.LastName,
TerritoryId = x.EmployeeTerritories. //cannot access properties
})
.ToListAsync();
But i can't easily access EmployeeTerritories properties, since it's not 1:1 relationship. I accept both clues and full solution to this problem.
Edit
So this is what i currently have:
var query = await context
.Employees
.Select(x => new
{
x.FirstName,
x.LastName,
TerritoryDescription = x.EmployeeTerritories
.Select(et => et.Territory.TerritoryDescription)
.ToList(),
RegionDesicription = x.EmployeeTerritories
.Select(et => et.Territory.Region.RegionDescription)
.ToList()
})
.ToListAsync();
Is there a way to optimize it? RegionDescription is now a list that contains one element, but i don't know how to do it the better way.
Try something like this (assuming you have corresponding relations):
var query = await context
.Employees
.Select(x => new
{
x.Employee.FirstName,
x.Employee.LastName,
TerritoryDescription = x.EmployeeTerritories
.Select(et => et.Territory.TerritoryDescription)
.ToList(),
})
.ToListAsync();
UPD
To flatten in your particular case you can use solution posted by #dhrumil shah(it is more generic one) or try something like that, if you have EmployeeTerritories set up in your context :
var query = await context
.EmployeeTerritories
.Select(et => new
{
et.Employee.FirstName,
et.Employee.LastName,
et.Territory.TerritoryDescription,
et.Territory.Region.RegionDescription
})
.ToListAsync();
(from emp in context.Employees
join empt in context.EmployeeTerritories
on emp.EmployeeID equals empt.EmployeeID
join tr in context.EmployeeTerritories
on empt.TerritoryID equals tr.EmployeeID
join reg in context.Region
on reg.RegionID equals tr.RegionID
select new {
emp.FirstName,
emp.LastName,
tr.TerritoryDescription,
reg.RegionDescription
}).ToList();

Select by ID from a list

I have a table of products and a table of categories, I can select by the ID of the Category like this:
var result = db.tblProducts.Where(p => p.tblCategories.Any(c => c.ID == 1));
However, I want to be able to select based on a list of Categories:
var catIDs = new List<int>() { 1,2,3 };
var results = db.tblProducts.Where(r => r.tblCategories.Any(t => catIDs.Contains(t.ID)));
I get the following error:
LINQ to Entities does not recognize the method 'Boolean Contains(Int32)' method, and this method cannot be translated into a store expression.
Presumably because I am using Contains to compare entities to local variables. Is there a way to do this?
Try create Expression from values. F.e.:
static Expression MakeOrExpression<T, P>(Expression<Func<T, P>> whatToCompare, IEnumerable<P> values)
{
Expression result = Expression.Constant(true);
foreach (var value in values)
{
var comparison = Expression.Equal(whatToCompare, Expression.Constant(value));
result = Expression.Or(result, comparison);
}
return result;
}
How to use:
var results = db.tblProducts.Where(r => r.tblCategories.Any(MakeOrExpression(t => t.ID, catIDs)));
The method MakeOrExpression will create an expression t.ID == 1 || t.ID == 2 || t.ID == 3 for list { 1, 2, 3 } dynamically, and then EF will translate it to SQL condition.
Maybe you can use this:
var catIDs = new List<int>() { 1,2,3 };
var results = db.tblCategories
.Where(t => catIDs.Contains(t.ID))
.SelectMany(t => t.tblProducts)
.Distinct();
Try this:
var query=from p in db.tblProducts
from c in p.tblCategories
where catIDs.Contains(c.ID)
select p;
If at least one of the categories of the product is in the catIDs list, then the product will be seleted.
Another option could be start by the categories (I'm guessing you have a many to many relationship between Product and Category and you have a collections of products in your Category entity):
var query=db.tblCategories.Where(c => catIDs.Contains(c.ID)).SelectMany(c=>c.tblProducts).Distinct();
Try this code :
var catIDs = new List<int>() { 1,2,3 };
var results = db.tblProducts.Where(r => catIDs.Any(c => c == r.tblCategories.Id));

Linq ordering both parent and child related entities

I have a parent child model where I want to sort by the SortOrder column of both entities. I have the query working but it seems overly verbose and I was wondering if there was a simpler solution to this problem.
I am initially loading the results into an anonymous type (as you can not load complex types directly into the entity framework entities) then querying that type again to load into the entities. I know I could simplify this by implementing a DTO but was interested in a cleaner solution for this use case.
Model
Query
public List<Group> GetStaticMeasures(int businessUnitID)
{
var groups = (from g in ctx.Groups.Where(w => w.BusinessUnitID.Equals(businessUnitID)).OrderBy(o => o.SortOrder)
select new
{
ID = g.ID,
BusinessUnitID = g.BusinessUnitID,
Name = g.Name,
SortOrder = g.SortOrder,
Datapoints = (from d in ctx.Datapoints where g.ID.Equals(d.StaticGroupID) orderby d.SortOrder select d).ToList()
}).ToList();
var results = from g in groups
select new Group
{
ID = g.ID,
BusinessUnitID = g.BusinessUnitID,
Name = g.Name,
SortOrder = g.SortOrder,
Datapoints = g.Datapoints
};
return results.ToList();
}
How about:
public IEnumerable<Group> GetStaticMeasures(int businessUnitID)
{
var groups = ctx.Groups
.Include("Datapoints")
.Where(w => w.BusinessUnitID.Equals(businessUnitID))
.OrderBy(o => o.SortOrder);
foreach(var g in groups)
{
g.Datapoints = g.Datapoints.OrderBy(d => d.SortOrder).ToList();
yield return g;
}
}

How to select multiple fields (LINQ)

How to change the following linq query to select another field value Field<int>("data_entry"),i want to select multiple fields .
var a = DF_Utilities.GetAvailableTasks(empnum, 1).AsEnumerable().Where(
p => p.Field<int>("task_code") == int.Parse(drpTasks.SelectedValue)).Select(p => p.Field<int>("cand_num")).First();
p.Field<int>("cand_num"),Field<int>("data_entry")
instead of p.Field<int>("cand_num")
You can use anonymous type:
var a = DF_Utilities.
GetAvailableTasks(empnum, 1).
AsEnumerable().
Where(p => p.Field<int>("task_code") == int.Parse(drpTasks.SelectedValue)).
Select(p => new
{
candNum = p.Field<int>("cand_num"),
dataEntry = p.Field<int>("data_entry")
}).
First();

LINQ group by with multiple counts

I am having trouble doing multiple counts on a single table in a LINQ query. I am using NHibernate, LINQ to NHibernate and C#.
query is a populated list. I have a table that has a boolean called FullRef. I want to do a LINQ query to give a count of occurances of FullRef = false and FullRef = true on each TrackId. TrackId gets a new row for each time he gets a track.Source == "UserRef".
In the following query I get the correct number count (from the FullRefTrueCount) of FullRef = true, but it gives an unknown wrong number on the FullRefFalseCount.
var query2 = from track in query
where track.Source == "UserRef"
group track by new { TrackId = track.TrackId, FullRef = track.FullRef } into d
select new FullReferrer
{
Customer = d.Key.TrackId,
FullRefFalseCount = d.Where(x => x.FullRef == false).Count(),
FullRefTrueCount = d.Where(x => x.FullRef == true).Count()
};
Anyone have any idea on how to fix it? I am pretty certain the .Where() clause is ignored and the "group by" is screwing me over.
If I could somehow
group track by new { TrackId = track.TrackId, FullRefTrue = track.FullRef, FullRefFalse = !track.FullRef }"
it would work. Is there some way to do this?
you should group by trackId only, if you want results by trackId...
var query2 = query
.Where(m => m.Source == "UserRef")
.GroupBy(m => m.TrackId)
.Select(g => new FullReferrer {
Customer = g.Key,
FullRefFalseCount = g.Count(x => !x.FullRef),
FullRefTrueCount = g.Count(x => x.FullRef)
});

Categories