linq groupjoin using lambda with a where clause - linq

I need to add a join using Lambda if I have a further parameter available that will also be used in a where clause.
My problem is I'm not sure of the exact format for adding a new object MemberTagLysts and then how the where clause should be created.
var tagList = from t in dc.Tags
join b in dc.Businesses on t.BusinessId equals b.BusinessId
where t.IsActive == true
where b.IsActive == true
orderby t.AdImage descending
select new TagItem
{
tagName = t.Name.Replace("\"", ""),
tagImage = tagImagePath + t.AdImage.Replace("\"", ""),
tagDescription = t.Description.Replace("\"", "")
};
if (!string.IsNullOrEmpty(lystId))
{
tagList = (IQueryable<TagItem>)tagList.GroupJoin(dc.MemberTagLysts, a => a.tagId, b => b.TagId, (a, b) => new { a, b });
}

I think you want to do something like this:
var tagList = from t in dc.Tags
join b in dc.Businesses on t.BusinessId equals b.BusinessId
where t.IsActive
where b.IsActive
orderby t.AdImage descending
select new TagItem
{
tagName = t.Name.Replace("\"", ""),
tagImage = tagImagePath + t.AdImage.Replace("\"", ""),
tagDescription = t.Description.Replace("\"", "")
};
if (!string.IsNullOrEmpty(lystId))
{
tagList = tagList
.GroupJoin(dc.MemberTagLysts.Where(l => l.lystId == lystId),
a => a.tagId,
b => b.TagId,
(a, b) => new { a, b }));
}
Conditionally expanding the query is good practice. Note that conditions like where t.IsActive == true are redundant, where t.IsActive is enough and arguable better readable with well-chosen property names (as you have).

Related

LINQ Aggregate results with Many to Many Relationship

I am currently working with this schema
This is how my LINQ currently looks
var regionResults = (
from p in _context.Projects
from pr in p.Regions
where (data.RegionId == null || pr.RegionId == data.RegionId)
group p by pr.RegionId into g
join q in _context.Regions on g.Key equals _context.Regions.First().Id
select new Models.ViewModels.ProjectBreakdownViewModel.Regions
{
RegionName = q.Name,
TotalCount = g.Count(),
RejectedCount = g.Count(e => e.SubmissionStatusId == 2),
DeniedCount = g.Count(e => e.SubmissionStatusId == 3)
});
this is what it is currently producing, albeit incorrect
This is what I need it to be...
I know the problem is with this line, essentially
join q in _context.Regions on g.Key equals _context.Regions.First().Id
I don't know how to do this without the use of .First(), there doesn't seem to be a way to do it. I'm close I just don't know how to finish this.
If you have an collection of ProjectRegions in you Region entity, you can do this:
var result= context.Regions
.Where(r=> data.RegionId == null || r.Id == data.RegionId)
.Select(r=> new
{
RegionName = r.Name,
TotalCount = r.ProjectRegions.Count(),
RejectedCount = r.ProjectRegions.Count(e => e.Project.SubmissionStatusId == 2),
DeniedCount = r.ProjectRegions.Count(e => e.Project.SubmissionStatusId == 3)
});
ProjectRegion entity should have two nav properties, Project and Region, use them to navigate and create the corresponding conditions

LinQ: ?: Operator in select query

I have a query like below. anyone have idea why the ?: parts are always return false values although there is item contains in LIST. Or anyone have better idea to write this query, feel free to reply here. thanks.
List EList = new List();
EList.Add(new EmployeeInfo(1, "a1", "b1"));
EList.Add(new EmployeeInfo(2, "a2", "b2"));
List OList = new List();
OList.Add(new EmployeeInfo(1, "a1", "b1"));
OList.Add(new EmployeeInfo(2, "a2", "b2"));
OList.Add(new EmployeeInfo(3, "aa2", "bb2"));
var results = (
from e in b
select new
{
Id = e.ID,
Name = e.Name,
Email = e.Email,
IS_Elist = (EList.Contains(e))?true:false,
IS_Olist = (OList.Contains(e)) ? true : false,
}
).ToList();
First you can simplify your query - expression ? : true : false is equivalent to just expression and we get the following.
var results = b.Select(e => new
{
Id = e.ID,
Name = e.Name,
Email = e.Email,
IS_Elist = EList.Contains(e),
IS_Olist = OList.Contains(e)
})
.ToList();
This should and will definitely yield true if an item from b is contained in EList or OList. If you always get false this means that EList and OList do not contain any items from b. Remember that you are probably doing a comparison of references and therefore
new EmployeeInfo(1, "a", "b") == new EmployeeInfo(1, "a", "b")
will usually yield false.
I guess you actually intended a comparison by ID.
var results = b.Select(e => new
{
Id = e.ID,
Name = e.Name,
Email = e.Email,
IS_Elist = EList.Any(x => x.ID == e.ID),
IS_Olist = OList.Any(x => x.ID == e.ID)
})
.ToList();
The alternative solution is to implement IEquatable<T> on EmployeeInfo or overriding Equals(), GetHashCode() and the equality operator. You should take care that all your implementations yield consistent results or you will end with a lot of confusion why for example Object.Equals() and the equality operator yield different results.
Your equality check may not be behaving as you expect for EmployeeInfo. To confirm, at the bottom of the snippet you posted above, try this and see if it returns true.
var is_olist = OList.Contains(new EmployeeInfo(1, "a1", "b1"));
If this returns false, you need to consider using something simpler but still unique to compare instances (such as the ID member), or else implement equals yourself in EmployeeInfo.
Implement Equals and the Equality == operator in your EmployeeInfo class.
Implementing the Equals Method
Guidelines for Implementing Equals and the Equality Operator (==)

How can I define a List to add results of a query in a loop?

I have an array filled with long type values and for each value in the array I need to implement a query. I used foreach loop as you can see from the code below:
var result;
foreach(long id in PrdIdArr)
{
var mainQuery = (from o in db.OPERATIONs
join u in db.UNITs on o.OP_UNIT_ID equals u.UNIT_ID into smt
from s in smt
join x in db.XIDs on s.UNIT_ID equals x.UNIT_ID
where o.OP_OT_CODE == OtCode
where x.IDTYP_CD == "BSN"
where s.START_PRD_ID == id
where o.OP_UPD_DATE >= _StartDate
where o.OP_UPD_DATE <= _EndDate
select new
{
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}).Take(_RowNumber);
//var result = mainQuery.ToList();
result.add(mainQuery.ToList());
}
data = this.Json(result);
data.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return data;
However, I have a problem in my code; I have to define a main list just before the foreach loop so that I could add results of each query to the that main list. my question is: How can I define this list as you can see at the beginning of my code? Thanks for the help...
How can I define this list as you can see at the beginning of my code?
Make
new {
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}
into a concrete type (say QueryResult, although something a little more specific than that), and then just declare
var result = new List<QueryResult>();
Also, you should consider turning
foreach(long id in PrdIdArr)
and
where s.START_PRD_ID == id
into
where PrdIdArr.Contains(s.Start_PRD_ID)
var result = new List<object>();
foreach(long id in PrdIdArr)
{
....
result.Add(mainQuery.ToList());
}
You could do this:
var result = PrdIdArr.Select(id =>
from o in db.OPERATIONs
join u in db.UNITs on o.OP_UNIT_ID equals u.UNIT_ID into smt
from s in smt
join x in db.XIDs on s.UNIT_ID equals x.UNIT_ID
where o.OP_OT_CODE == OtCode
where x.IDTYP_CD == "BSN"
where s.START_PRD_ID == id
where o.OP_UPD_DATE >= _StartDate
where o.OP_UPD_DATE <= _EndDate
select new
{
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}
.Take(_RowNumber)
.ToList()
).ToList();
I highly recommend performing some Extract Method refactorings, as the code is pretty complex and hard to understand/mange this way.
Just create the anonymous type outside with the same property names and the correct type
var result = Enumerable.Range(0, 0).Select(x => new
{
OP_ID = 1,
OP_UPD_DATE = DateTime.Now,
EXTERNAL_ID = 1,
OP_OS_CODE = 1,
OP_START = DateTIme.Now,
OP_ST_STATION = "",
START_PRD_ID = 1,
}).ToList();
And in your loop call AddRange
result.AddRange(mainQuery.ToList());

LINQ to Entity : Multiple join conditions

There are numerous post regarding LINQ and multiple joins.
I have however not found any solution to the join I'd like to make.
The SQL equivalent would be something like this:
SELECT * FROM table1 a
LEFT JOIN table2 b ON a.col1 = b.key1 AND
a.col2 = b.key2 AND
b.from_date <= now() AND
b.deleted = 0;
Here's one of the numerous linq queries I've attempted
var query = (from x in context.table1
join y in context.table2 on new {x.col1, x.col2} equals {b.key1, b.key2}
into result
from result......
How may I add the additonal conditions of the date and deleted flag?
If I use .Where conditions, then this is treated as a inner join, not a left join.
Another way could be like
var query = (from x in context.table1
join y in context.table2 on
new {
Key1 = x.col1,
Key2 = x.col2,
Key3 = true,
Key4 = true
}
equals
new {
Key1 = y.key1,
Key2 = y.key2,
Key3 = y.from_date< DateTime.Now,
Key4 = !y.deleted
}
into result
from r in result.DefaultIfEmpty()
select new {x.Something, r.Something}
LINQ supports both the join syntax and the older ANSI-82 WHERE syntax. Using the later, you could do what your looking for on an inner join with
var nowTime = DateTime.Now;
var query = from a in context.table1
from b in context.table2
where a.col1 == b.key1
&& a.col2 == b.key2
&& b.from_date < nowTime
&& b.deleted == false
select ???;
For an outer join, I prefer a syntax using a hybrid of where and select many. (Realize that the order in the LINQ query does not need to mimic what you would do in SQL and the order is more flexible.)
var nowTime = DateTime.Now;
var query = from b in context.table2
from a1 in a.Where(a2 =>
b.key1 = a.col &&
b.key2 = a.col2 &&
b.from_date < nowTime &&
b.deleted == false).DefaultIfEmpty()
select ???;
I had problem with naming of properties in anonymous object:
var subscriptions = context.EmailSubscription.Join(context.EmailQueue,
es => new { es.Id, 9 },
eq => new { eq.EmailSubscriptionId, eq.EmailTemplateId },
(es, eq) => new { es.Id, eq.Id }
).ToList();
Compiler was not happy so above answer helps me to figure out what was wrong and here is my working solution. It took me some time to find stupid mistake :) :
var subscriptions = context.EmailSubscription.Join(context.EmailQueue,
es => new { EmailSubscriptionId = es.Id, EmailTemplateId = 9 },
eq => new { eq.EmailSubscriptionId, eq.EmailTemplateId },
(es, eq) => new { es.Id, eq.Id }
).ToList();
Could you not just filter the 1st result set with a second query?
var query = (from x in context.table1
join y in context.table2 on new {x.col1, x.col2} equals {b.key1, b.key2}
into result
query = from x in query
where ...
Would that work?
In addition to #Muhammad Adeel Zahid answer, you could use also some several conditions like:
new
{
Key1 = ppl.PeopleId,
Key2 = true,
Key3 = true
}
equals
new
{
Key1 = y.PeopleId,
Key2 = !y.IsDeleted,
Key3 = (y.RelationshipType == 2 || y.RelationshipType == 4)
}

Update existing list values with values from another query

I have a linq statement which calls a stored proc and returns a list of items and descriptions.
Like so;
var q = from i in doh.usp_Report_PLC()
where i.QTYGood == 0
orderby i.PartNumber
select new Parts() { PartNumber = i.PartNumber, Description = i.Descritpion.TrimEnd() };
I then have another SQL statement which returns the quantities on order and delivery date for each of those items. The Parts class has two other properties to store these. How do I update the existing Parts list with the other two values so that there is one Parts list with all four values?
UPDATE
The following code now brings out results.
var a = from a1 in db.usp_Optos_DaysOnHand_Report_PLC()
where a1.QTYGood == 0
orderby a1.PartNumber
select new Parts() { PartNumber = a1.PartNumber, Description = a1.Descritpion.TrimEnd() };
var b = from b1 in db.POP10110s
join b2 in db.IV00101s on b1.ITEMNMBR equals b2.ITEMNMBR
//from b3 in j1.DefaultIfEmpty()
where b1.POLNESTA == 2 && b1.QTYCANCE == 0
group b1 by new { itemNumber = b2.ITMGEDSC } into g
select new Parts() { PartNumber = g.Key.itemNumber.TrimEnd(), QtyOnOrder = g.Sum(x => Convert.ToInt32(x.QTYORDER)), DeliveryDue = g.Max(x => x.REQDATE).ToShortDateString() };
var joinedList = a.Join(b,
usp => usp.PartNumber,
oss => oss.PartNumber,
(usp, oss) =>
new Parts
{
PartNumber = usp.PartNumber,
Description = usp.Description,
QtyOnOrder = oss.QtyOnOrder,
DeliveryDue = oss.DeliveryDue
});
return joinedList.ToList();
Assuming your "other SQL statement" returns PartNumber, Quantity and DeliveryDate, you can join the lists into one:
var joinedList = q.Join(OtherSQLStatement(),
usp => usp.PartNumber,
oss => oss.PartNumber,
(usp, oss) =>
new Parts
{
PartNumber = usp.PartNumber,
Description = usp.Description,
Quantity = oss.Quantity,
DeliveryDate = oss.DeliveryDate
}).ToList();
You can actually combine the queries and do this in one join and projection:
var joinedList = doh.usp_Report_PLC().
Where(i => i.QTYGood == 0).
OrderBy(i => i.PartNumber).
Join(OtherSQLStatement(),
i => i.PartNumber,
o => o.PartNumber,
(i, o) =>
new Parts
{
PartNumber = i.PartNumber,
Description = i.Description,
Quantity = o.Quantity,
DeliveryDate = o.DeliveryDate
}).ToList();
And again: I assume you have PartNumber in both returned collections to identify which item belongs to which.
Edit
In this case the LINQ Query syntax would probably be more readable:
var joinedList = from aElem in a
join bElem in b
on aElem.PartNumber equals bElem.PartNumber into joinedAB
from abElem in joinedAB.DefaultIfEmpty()
select new Part
{
PartNumber = aElem.PartNumber,
Description = aElem.Description,
DeliveryDue = abElem == null ? null : abElem.DeliveryDue,
QtyOnOrder = abElem == null ? null : abElem.QtyOnOrder
};
Your DeliveryDue and QtyOnOrder are probably nullable. If not, replace the nulls by your default values. E.g. if you don't have the element in b and want QtyOnOrder to be 0 in the resulting list, change the line to
QtyOnOrder = abElem == null ? 0 : abElem.QtyOnOrder

Resources