Reference DataTable columns with Linq - linq

I am trying to join two datatables using linq
var invoices420 = dt420_.AsEnumerable();
var invoices430 = dt430_.AsEnumerable();
var query = from inv430 in invoices430
join inv420 in invoices420 on inv430.LinkDoc equals inv420.LinkDoc
orderby inv430.SID
select new
{
LinkDoc = inv430.LinkDoc,
TotalIn = Math.Round(inv430.Credit, 2),
TotalOut = ((inv420 == null) ? 0 : Math.Round(inv420.Debit, 2))
};
Joining does not seems to be a problem, but I am getting an error'System.Data.DataRow' does not contain a definition for 'LinkDoc' and no extension method 'LinkDoc' accepting a first argument of type 'System.Data.DataRow' could be found (are you missing a using directive or an assembly reference?).
What do I have to do to reference a column in DataTable for example inv430.LinkDoc without using inv430.Field("linkdoc")?
If I want to do a group by on result set I am thinking
var q2 = query
.GroupBy(item => item.LinkDoc);
return q2.ToArray();
Problem is that in q2 I dont get all the columns (linkdoc, totalin, totalout).
Original data is
dt420_
Linkdoc Credit
Invoice1 500
Invoice2 100
Invoice3 200
dt430_
LinkDoc Debit
Invoice1 100
Invoice1 100
Invoice2 200
Result would be
LinkDoc TotalIn(Credit) TotalOut(Debit)
Invoice1 500 200
Invoice2 100 200
Invoice3 200 0

You need to replace all places you called directly to properties like
inv430.LinkDoc
to
inv430["LinkDoc"]
inv430 is a DataRow so you need to use the indexer that gets a string.
EDIT:
Your join will bring wrong data (see my comment below). You need to use this code:
var group430 = from inv430 in invoices430
group inv430 by inv430["LinkDoc"].ToString().Trim() into g
select new
{
LinkDoc = g.Key.ToString().Trim(),
TotalOut = g.Sum(inv => Math.Round((decimal)inv["Debit"], 2))
};
var group420 = from inv420 in invoices420
group inv420 by inv420["LinkDoc"].ToString().Trim() into g
select new
{
LinkDoc = g.Key.ToString().Trim(),
TotalIn = g.Sum(inv => Math.Round((decimal)inv["Credit"], 2))
};
var result = from inv430 in group430
join inv420 in group420 on inv430.LinkDoc equals inv420.LinkDoc into inv
from inv420 in inv.DefaultIfEmpty()
select new
{
inv430.LinkDoc,
TotalOut = inv430.TotalOut,
TotalIn = inv420 != null ? inv420.TotalIn : 0
};

Related

how use multiple join in linq?

var abc1 = from dlist in db.DebtorTransactions.ToList()
join war in db.Warranties on dlist.ProductID equals war.Id
join ag in db.Agents on war.fldAgentID equals ag.pkfAgentID
join sr in db.SalesReps on war.fldSrId equals sr.pkfSrID
where dlist.TransTypeID == 1
select new
{
dlist.Amount,
dlist.TransTypeID,
name = ag.Name,
ag.pkfAgentID,
sr.pkfSrID,
salesnam = sr.Name
} into objabc
group objabc by new
{
objabc.TransTypeID,
objabc.name,
objabc.salesnam,
objabc.Amount
};
var amt1 = abc1.Sum(x => x.Key.Amount);
var abc2 = from dlist in db.DebtorTransactions.ToList()
join cjt in db.CarJackaTrackas on dlist.ProductID equals cjt.pkfCjtID
join ag in db.Agents on cjt.AgentID equals ag.pkfAgentID
join sr in db.SalesReps on cjt.SalesRepId equals sr.pkfSrID
where dlist.TransTypeID == 0
select new
{
dlist.Amount,
dlist.TransTypeID,
name = ag.Name,
ag.pkfAgentID,
sr.pkfSrID,
enter code here` salesnam = sr.Name
} into objabc
group objabc by new
{
objabc.TransTypeID,
objabc.name,
objabc.salesnam,
objabc.Amount
};
var amt2 = abc1.Sum(x => x.Key.Amount);
//var result1=
return View();
i am new to linq, this query is working but i need to get the sum of Amount where dlist.TransTypeID == 0 and where dlist.TransTypeID == 1 by just single query. may anybody help me? thanks in advance
Here's a trimmed down example of how you can do it. You can add the joins if they are necessary, but I'm not clear on why you need some of the extra join values.
var transTypeAmountSums = (from dlist in db.DebtorTransactions
group dlist by dlist.TransTypeId into g
where g.Key == 0 || g.Key == 1
select new
{
TransTypeId = g.Key,
AmountSum = g.Sum(d => d.Amount)
}).ToDictionary(k => k.TransTypeId, v => v.AmountSum);
int transTypeZeroSum = transTypeAmountSums[0];
int transTypeOneSum = transTypeAmountSums[1];
A couple of things to note:
I removed ToList(). Unless you want to bring ALL DebtorTransactions into memory then run a Linq operation on those results, you'll want to leave that out and let SQL take care of the aggregation (it's much better at it than C#).
I grouped by dlist.TransTypeId only. You can still group by more fields if you need that, but it was unclear in the example why they were needed so I just made a simplified example.

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);

How to join two datasource which return all rows and group by condition?

I have a problem when using LINQ to join two datasource. Two datasource created by a query like :
var A = (from....
group .... into grp
select new
{
Qty = grp.Count(),
Code = grp.Key.Code,
Name = grp.Key.Name
});
var B = (from....
group .... into grp
select new
{
Qty = grp.Count(),
Code = grp.Key.ContCode,
Name = grp.Key.ContName
});
Value of 'A' will be returned like this :
Qty-Code-Name
1-10A-Cont10
1-20B-Cont20
1-30C-Cont30
Value of 'B' will be returned like this :
Qty-Code-Name
1-10A-Cont10
1-20B-Cont20
1-30C-Cont30
1-40D-Cont40
1-50E-Cont50
I want to join A and B (or do somethings) and the result like this (which sum column 'Qty' if they have the same 'Code' and 'Name') :
Qty-Code-Name
2-10A-Cont10
2-20B-Cont20
2-30C-Cont30
1-40D-Cont40
1-50E-Cont50
How can I do it ? Please help me.
Thank you very much !
Concat the two datasources and than group by code and name.
Something like:
var q = from v in A.Concat(B)
group v by new {v.Code,v.Name } into g
select new
{
Qty = g.Sum(a => a.Qty),
CodeName = g.Key.Code,
Name = g.Key.Name
};

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
};

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