Linq Sql Query Error for Get value - linq

Here is my linq query for get Qty and Number from first collection Qty - Second Collection Qty and first collection Number - Second Collection Number, Some times First collection RM not contain second colection
var summary = (from r in firstCollection
join s in secondCollection
on new { r.RM, r.Size } equals new { s.RM, s.Size }
group new { r, s } by new { RM = r.RM, Size = s.Size, Qty = (r.Qty - s.Qty), Number = (r.Number - s.Number) }
into grp
select new
{
RM = grp.Key.RM,
RMsize = grp.Key.Size,
Qty = grp.Key.Qty,
Number = grp.Key.Number
}).ToList();
there is an error like
Additional information: A group by expression can only contain
non-constant scalars that are comparable by the server. The expression
with type 'Manufacturing.DataAccess.tbl_RawMaterial' is not
comparable.
How can i solve this ?

You can project to anonymous type first and then do a grouping. Try this:
var summary = (from r in firstCollection
join s in secondCollection
on new { r.RM, r.Size } equals new { s.RM, s.Size }
select new
{
RM = r.RM,
Size = s.Size,
Qty = (r.Qty - s.Qty),
Number = (r.Number - s.Number)
} into tmp
group tmp by new
{
RM,
Size,
Qty,
Number
} into grp
select new
{
RM = grp.Key.RM,
RMsize = grp.Key.Size,
Qty = grp.Key.Qty,
Number = grp.Key.Number
}).ToList();

Looks like the problem is RM member which I assume is some navigation property of type Manufacturing.DataAccess.tbl_RawMaterial. As the exception message states, you can only group by simple properties.
Let say your entity Manufacturing.DataAccess.tbl_RawMaterial primary key is called Id (you can replace it with the actual name). Then the query could be something like this
var summary =
(from r in firstCollection
join s in secondCollection
on new { r.RM.Id, r.Size } equals new { s.RM.Id, s.Size }
group new { r, s }
by new { Id = r.RM.Id, Size = s.Size, Qty = (r.Qty - s.Qty), Number = (r.Number - s.Number) }
into grp
select new
{
RM = grp.FirstOrDefault(e => e.r),
RMsize = grp.Key.Size,
Qty = grp.Key.Qty,
Number = grp.Key.Number
}).ToList();

Related

Problem with iterating over two lists simultaneously

I'm using ASP.NET Core 3.1. I have written some code like the following and now I want to get the result of these two queries that has the same size and iterate over each one of them and divide their element and store the result in a list. But now the problem is in my zip method, I can not specify exactly which attribute of each query I want to divide.
var mytotal = _context.Apiapp.GroupBy(o => new
{
Month = o.ApiRequestDate.Substring(4, 2),
Year = o.ApiRequestDate.Substring(0, 4)
}).Select(g => new
{
Month = g.Key.Month,
Year = g.Key.Year,
Total = g.Count()
}).OrderByDescending(a => a.Year).ThenByDescending(a => a.Month).ToList();
var numerator = from t1 in _context.Apiapp
join t2 in _context.ApiAppHistory on t1.Id equals t2.ApiApplicantId
join t3 in _context.EntityType on t2.LastReqStatus equals t3.Id
where t1.IsDeleted == false && t1.LastRequestStatus == t2.Id && t3.Name == "granted"
group new { Year = t1.ApiRequestDate.Substring(0, 4), Month = t1.ApiRequestDate.Substring(4, 2) }
by new { t2.LastReqStatus } into g
select new
{
Year = g.Max(n => n.Year),
Month = g.Max(n => n.Month),
GrantedCount = g.Count()
};
var GrantedReqStatus = numerator.ToList();
var GrantedAccessPercent = new List<Double>();
//-------------------------------------------------------
var res = mytotal.Zip(GrantedReqStatus, (total, GrantedCount) => new { Num = total, Denum = GrantedCount });
foreach(var r in res)
{
GrantedAccessPercent.Add(r.Num/r.Denum);
}
Here inside the body of foreach, r.Num and r.Denum is unknown! I appreciate of any help to fix the error.
The Num and DemNum in the Zip function represent objects for the first and second collection, that contains Month,Year and Total for the total object and Month,Year and GrantedCount for the grantedCount object.
You could use total.Total and grantedCount.GrantedCount to get numbers, like the following code:
var res = mytotal.Zip(GrantedReqStatus, (total, grantedCount) => new { Num = total.Total, Denum = grantedCount.GrantedCount });
foreach(var r in res)
{
GrantedAccessPercent.Add(Math.Round(r.Num / (double)r.DemNum, 2));
}
Note that, to divide int1/int2 you need to cast int2 to double, Will give the expected result, and you can use also Math.Round to specify numbers after comma.
I hope this help you fix the issue.

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 Sql Query - better solution (optimizing)

The following code works, but it's not a nice code. (low performance)
I have a dictionary with value and key.
First i go trough every webcodes who exist. Then i load all participants in a list (where webcode equals the actual webcode in the foreach). After that i add the data (parameter of the webcode and a count of participants to the dictionary).
Guid compID = Guid.Parse(wID);
ChartModel webcodes = new ChartModel();
webcodes.Title = "Webcodes Statistics";
webcodes.Data = new Dictionary<string, int>();
var webcodesData = db.t_Webcode;
foreach (var w in webcodesData)
{
var wData = db.t_Participant.Where(t => t.FK_Competition == compID && t.Webcode == w.Webcode);
if (wData.Count() != 0)
webcodes.Data.Add(w.Parameter, wData.Count());
}
ViewBag.Webcodes = webcodes;
TIA
You need something along these lines:
webcodes.Data = (from w in db.t_Webcode
join p in db.t_Participant on w.Webcode equals p.Webcode
where p.FK_Competition == compID
group w by w.Parameter into g
select new { g.Key, Count = g.Count() }).ToDictionary();
I can't test it but that is the type of query you need.
This will assume that you have relationships defined in your database and that your LINQ to SQL datacontext are aware of them. If not, you will need to join manually on t_Participants from tWebcode.
This should execute in 1 single SQL query, instead of 1 query per row in tWebcode.
var webcodesAndNoOfParticipants =
from webcode in db.tWebcode
// Define number of participants for this webcode
let numberOfParticipants = webcode.t_Participants.Count(participant => participant.FK_Competition == compID)
where numberOfParticipants > 0
select new {
WebcodeParameter = webcode.Parameter,
NoOfParticipants = numberOfParticipants
};
webcodes.Data = webcodesAndNoOfParticipants.ToDictionary(x => x.WebcodeParameter, x => x.NoOfParticipants);

Linq extract a count() value from a data object

I have divAssignments that has potential multiple rows by rNI, an official id, according to a compound key of Indictment and booking numbers.
rNI Booking Indictment
12345 954445 10 12345
12345 954445 10 12346
12345 954445 10 12347
So ID has a count of 3 for a single booking number for this rni.
I get lost attempting to generate a count and a group by booking Number:
var moreThen = from dA in divAssignments
select new { dA.rNI, IndictmentCount = dA.indictmentNumber.Count() };
Most of the examples are dealing with static int[] and don't seem to work in my case.
How do I get a group and then a count? If I could put in a having that would be fantastic.
from a t-sql POV I'd use this:
Select rni, bookingNumber, count(*) IndictmentCount
from divAssignments
group by rni, bookingNumber
having count(*) > 0
TIA
How about something like this:
var query = from item in divAssignments
group item by item.rNI into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
If you're interested in grouping by both the rNI and the booking number, I would change it to this:
var query = from item in divAssignements
group item by new { item.rNI, a.Booking } into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count
};
OR
var query = from item in divAssignments
group item by item into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
and implement IEquatable on the divAssignment object to support equality comparison. The other option if you'd like is to write an IEqualityComparer instance to do the composite key comparison. Your query could then look like:
var query =
divAssignments
.GroupBy(i => i, new MyCustomEqualityComparer())
.Select(i => new { Key = i.Key, Count = i.Count());
var query =
from dA in divAssignments
group dA by new { dA.rNI, dA.bookingNumber };
foreach(var grp in query)
{
Console.WriteLine("rNI={0}, bookingNumber={1} => Count={2}", grp.Key.rNI, grp.Key.bookingNumber, grp.Count());
}
If you use a Grouping operator in Linq you will get what you need. The code:
var count = from a in divAssignments
group a by new { a.rNI, a.Booking } into b
select b;
will return a collection of IGrouping objects. This will give you the Key (in my example this will be an anonymous type with an rNI and a Booking property) and a collection of the divAssignments that match the key.
Using Method syntax (much easier to read in my opinion):
First group the records, then select a new result for each group that contains the count.
var groups = divAssignments.GroupBy(d => new { d.rNI, d.Booking });
groups.Select(g=> new { g.Key.rNI, g.Key.Booking, IndictmentCount = g.Count() });

LINQ : Applying a filter in where clause but need to inspect the value of a SUM (subquery) - explained inside

I have the following linq and its working great but i need to be able to check a variable and depending on value only show a subset of the records. I will explain..
Here is the SQL
var test = from c in db.C
select new {
Period = c.M.Period,
Group = c.Code,
Code = c.ClientCode,
Name = c.ClientName,
Amount = (System.Int32)
((from m0 in db.M
where
m0.ClientCode == c.ClientCode
group m0 by new {
m0.ClientCode
} into g
select new {
Expr1 = (System.Int32)g.Sum(p => p.Amount)
}).First().Expr1)
}
This returns 6 records, The amount is the following in each record
100
200
300
400
500
600
I need to dynamically bolt on a where and check a variable called filter (in c#) and if filter is = 1 then return all records <300 and if filter is = 2 return all records >= 300 and if the variable is empty don't apply any filter and return all records.
Now where i am getting confused is that Amount isn't in the DB it is actually subquery.
Can anyone lend a hand?
I think it would help if you simplified your query to start with. You're using anonymous types for no particular reason (if there's only a single property, why bother?) and grouping by a value which you've already filtered by. In other words, your query is equivalent to:
var test = from c in db.C
select new {
Period = c.M.Period,
Group = c.Code,
Code = c.ClientCode,
Name = c.ClientName,
Amount = db.M
.Where(m0 => m0.ClientCode == c.ClientCode)
.Sum(m0 => m0.Amount)
};
Having a simpler query may make it easier to fix the rest of your problem. It may be as simple as:
var filtered = filter == 1 ? test.Where(t => t.Amount < 300)
: filter == 2 ? test.Where(t => t.Amount >= 300)
: test;
try this
var test = from c in db.C
select new {
Period = c.M.Period,
Group = c.Code,
Code = c.ClientCode,
Name = c.ClientName,
Amount = (System.Int32)
((from m0 in db.M
where
m0.ClientCode == c.ClientCode
group m0 by new {
m0.ClientCode
} into g
select new {
Expr1 = (System.Int32)g.Sum(p => p.Amount)
}).First().Expr1).Where(i =>
{
if (filter == 1)
i.Expr1 < 300;
else if (filter == 2)
i.Expr1 >= 300;
})
Basically I took Jon's answer and integrated the filter into the query:
int filter = 1;
Func<int, bool> isRelevant = (amount)=>{
switch(filter)
{
case 1: return amount < 300;
case 2: return amount > 300;
default: throw new ArgumentException();
}
};
var test = from c in db.C
let amount = db.M
.Where(m0 => m0.ClientCode == c.ClientCode)
.Sum(m0 => m0.Amount)
where isRelevant(amount)
select new
{
Period = c.M.Period,
Group = c.Code,
Code = c.ClientCode,
Name = c.ClientName,
Amount = amount
};

Resources