Join + Group + Aggregate in LINQ - c#

I have the following query, which I put into a DataGrid:
var balancePerAccount = from bal in data.balanceDetails
join acc in data.userPaymentDetails on bal.userName equals acc.userName
where bal.paymentID == 0
group bal by bal.userName into g
where g.Sum(p => p.rebateBeforeService) >= 20
select new
{
Username = g.Key,
Amount = g.Sum(p => p.rebateBeforeService),
};
dgPaymentsPending.DataSource = balancePerAccount;
dgPaymentsPending.DataBind();
What I would like to do is add something inside acc in to the select. For example I would like to add PaymentProvider = acc.PaymentProvider. I tried flipping this every way I could think of, including getting First() from the group and trying to access it from there, but I can't seem to find a way to access it. I might just be missing something really simple, but I have been looking around Google for some time and I can't seem to find a comparable example. Anyone have an idea?

Extend grouping source using anonymous type: new { bal, acc } and then use First
var balancePerAccount = from bal in data.balanceDetails
join acc in data.userPaymentDetails on bal.userName equals acc.userName
where bal.paymentID == 0
group new { bal, acc } by bal.userName into g
where g.Sum(p => p.bal.rebateBeforeService) >= 20
select new
{
Username = g.Key,
PaymentProvider = g.FirstOrDefault().acc.PaymentProvider,
Amount = g.Sum(p => p.bal.rebateBeforeService),
};

Related

Linq query simplified

i have tried to manipulate some data using linq query. but i'm not sure the way i did was the best approach.
var makes = (from m in
(from pv in vehicleViews
let make = HttpUtility.ParseQueryString(new Uri("http://www.someurl.com" + pv.Url).Query).Get("Criteria.Make")
select new
{
Name = make,
Y = pv.TotalViews,
})
group m by m.Name into g
let total = g.Sum(x => x.Y)
select new
{
Name = g.Key,
Y = total
}).OrderByDescending(x => x.Y);
is there anyway to simplified this query ?
Thanks
You do have a couple of excess projections, an unnecessary nested select before the grouping, and a use of method syntax that isn't strictly necessary. I beleive the following is equivalent to your query:
from pv in vehicleViews
let make = HttpUtility
.ParseQueryString(new Uri("http://www.someurl.com" + pv.Url).Query)
.Get("Criteria.Make")
group pv by make into g
let total = g.Sum(pv => pv.TotalViews)
orderby total descending
select new { Name = g.Key, Y = total };
The let make = line isn't strictly necessary (you could just group by that value directly) but it does make it clearer what you are calculating.

.Sum() in lambda expressions

I'm new to lambda expressions. I'm trying to use the .Sum() method to a result from a db search, I want to sum all the values from the Importe column, I'm selecting the values using an ID from another table, but the Json send me back the entire list with every value, it's not doing the sum. Or maybe I don't know how to apply it?
Thank you
public JsonResult IngresaCuentas(string codigo)
{
ContextoAngeles db = new ContextoAngeles();
var suma = (from data in db.Cuentas
where data.Codigo == codigo
select (from n in db.MovimientosPolizas
where data.Id == n.IdCuenta
group n by new { n.Importe} into g
let sumaTotal = (g.Sum(n => n.Importe))
select new
{
Total: sumaTotal
})).ToList();
return Json(suma, JsonRequestBehavior.AllowGet);
}
I'm getting this in the Console:
[[{"Total":0},{"Total":20},{"Total":150},{"Total":330},{"Total":56.2},{"Total":240},{"Total":1750},{"Total":70.07},{"Total":480},{"Total":540},{"Total":95},{"Total":200},{"Total":108},{"Total":108.8},{"Total":880},{"Total":111.98},{"Total":115},{"Total":240},{"Total":125},{"Total":129.98},{"Total":780},{"Total":131.42},{"Total":134.59},{"Total":1260},{"Total":141.65},{"Total":145}]] (and a lot more..)
A friend helped me to resolve this issue and a Join was the answer. I think i didn't explain my problem clear enough, I was trying to do that but in a different way more like SQL syntax, if anyone knows a good place to learn about lambda expressions would be great.
Thank all of you! Here the solution.
var dato = db.Cuentas.Where(x => x.Codigo == codigo)
.Join(db.MovimientosPolizas, cuentas => cuentas.Id, movimientos => movimientos.IdCuenta, (cuenta, movimiento) => new { sumImporte = movimiento.Importe, cuenta = cuenta.Nombre })
.Sum(x => x.sumImporte);
When you are using select new { Total: sumaTotal }, that's not just sending back the int, that's sending you an object of an anonymous type with a Total field. I don't think that that's what you're going after.
What I think you should be doing is something like this:
var suma = (from data in db.Cuentas
where data.Codigo == codigo
select (from n in db.MovimientosPolizas
where data.Id == n.IdCuenta
group n by new { n.Importe} into g
let sumaTotal = (g.Sum(n => n.Importe))
select sumaTotal)).ToList();
Or, if what you're going for is to select the sum of every Importe that you've queried:
var suma = (from data in db.Cuentas
where data.Codigo == codigo
select (from n in db.MovimientosPolizas
where data.Id == n.IdCuenta
group n by new { n.Importe} into g
let sumaTotal = (g.Sum(n => n.Importe))
select sumaTotal)).Sum();

LINQ to CRM One to Many results without flattening

I'm trying to use LINQ to CRM to get some data in a parent/child format. It's a One to Many and I don't want the Child records flattening with the Parent. Firstly is what I'm asking possible?
I do not have navigation properties and there will be many parents returned, in turn each parent will have many children.
I'm trying to avoid multiple queries, just fetching it in 1.
So I'm after:
So the parent has many Children and the children have some other lookup properties in other tables 1 to 1.
Parent
--Child + other info
--Child + other info
Parent
--Child + other info
Parent
--Child + other info
--Child + other info
--Child + other info
etc
I've tried getting the Details as a IQueryable first, then using that joined on the header:
var detailsOnly = (from det in db.details
join inc in db.Incidents on det.detOtherId equals inc.incidentid into incidentsLo
from subInc in incidentsLo.DefaultIfEmpty()
join cli in db.Accounts on subInc.accountid equals cli.accountid into accountsLo
from subCli in accountsLo.DefaultIfEmpty()
select new
{
det,
AccName = subAcc.name,
AccRef = subAcc.accountnumber,
IncidentTicketNumber = subInc.ticketnumber,
IncidentKeyDescription = subInc.title,
IncidentMainContact = subInc.maincontactname
});
var query = from head in db.headers
where head.IsDone == isdone & head.type == typeId & head.accountid == AccountId
select new MyHeader(head)
{
MyDetails = detailsOnly.Where(md => md.det.detOtherId == head.headOtherId)
.Select(d => new MyDetail(d.det)
{
AccName = d.AccName,
AccRef = d.AccRef,
IncidentTicketNumber = d.IncidentTicketNumber,
IncidentKeyDescription = d.IncidentKeyDescription,
IncidentMainContact = d.IncidentMainContact
}).ToList()
};
var result = query.ToList();
It doesn't look like this is getting a real answer, so the best I can say is to execute the details collecting part, group it, then select the other parts from that.
var detailsOnly = (from det in db.details
join inc in db.Incidents on det.detOtherId equals inc.incidentid into incidentsLo
from subInc in incidentsLo.DefaultIfEmpty()
join cli in db.Accounts on subInc.accountid equals cli.accountid into accountsLo
from subCli in accountsLo.DefaultIfEmpty()
select new MyDetail(det)
{
AccName = subAcc.name,
AccRef = subAcc.accountnumber,
IncidentTicketNumber = subInc.ticketnumber,
IncidentKeyDescription = subInc.title,
IncidentMainContact = subInc.maincontactname
}).ToArray();
var groupedDetails = detailsOnly.GroupBy(c => c.det.detOtherId);
var query = db.headers.Where(head => head.IsDone == isdone
&& head.type == typeId
&& head.accountid == AccountId);
var result = query.Join(groupedDetails, c => c.headOtherId, c => c.Key, (a, b) => new MyHeader(a) { MyDetails = b })).ToList();
I could be way off on that, but you see the gist. I don't think it's the best way, but I don't have any experience with LINQ-to-CRM so I don't know. Something like this should work, though. Better than nothing. There might also be syntax errors, because this hasn't been tested.

Loop linq query n times by updating property name

I have this query
var query = from v in this._venueRepository.Table
join s in this._storeRepository.Table on v.VenueID equals s.VenueID
join w in this._workstationRepository.Table on s.StoreID equals w.StoreID
join t in this._tillSummaryRepository.Table on w.WorkstationID equals t.TillOpID
group new { v.DiscItemName_1, t.DiscItem_1, t.QDiscItem_1 } by new { v.DiscItemName_1 } into g
select new { Discount = g.Key, Amount = g.Sum(p => p.DiscItem_1), Qty = g.Sum(p => p.QDiscItem_1) };
I would like to execute this query (probably asynchronously) but each time I execute this query I would like to update the parameter "_1" -> "_2" -> "_3" etc for example
var query = from v in this._venueRepository.Table
join s in this._storeRepository.Table on v.VenueID equals s.VenueID
join w in this._workstationRepository.Table on s.StoreID equals w.StoreID
join t in this._tillSummaryRepository.Table on w.WorkstationID equals t.TillOpID
group new { v.DiscItemName_2, t.DiscItem_2, t.QDiscItem_2 } by new { v.DiscItemName_2 } into g
select new { Discount = g.Key, Amount = g.Sum(p => p.DiscItem_2), Qty = g.Sum(p => p.QDiscItem_2) };
etc, etc Any ideas how I can do this? Perhaps reflection?
One way of doing this is of course reflection as you mention. A sample based on your example is below:
var t = ._venueRepository.Table.FirstOrDefault().GetType();
for(int iterationCount = 1; iterationCount < MAX_ITERATIONS ; iterationCount++)
{
PropertyInfo itemNameProperty = t.GetProperty(String.Format("DiscItemName_{0}", iterationCount));
PropertyInfo discItermProperty = t.GetProperty(String.Format("DiscItem_{0}", iterationCount));
//Repeat the above for all properties.
var query = from v in this._venueRepository.Table
join s in this._storeRepository.Table on v.VenueID equals s.VenueID
join w in this._workstationRepository.Table on s.StoreID equals w.StoreID
join t in this._tillSummaryRepository.Table on w.WorkstationID equals t.TillOpID
//Repeat the below for other properties
group new { itemNameProperty.GetValue(v), dicItemProperty.GetValue(v) , qDiscProperty.GetValue(v) } by new { itemNameProperty.GetValue(v) } into g
//Similarly do for the select new.
select new { Discount = g.Key, Amount = g.Sum(p => p.DiscItem_1), Qty = g.Sum(p => p.QDiscItem_1) };
//Other code here.
}
If you are using the Entity framework, the above query will not work. The reason this will not work is that the Entity Framework will try to translate your query to SQL, and will fail at the reflection parts. What you have to do is split your query in two. Do the filter and join in one step and retrieving it to a List and then using reflection to create the anonymous types in another query. You will be using more memory however for this operation and you are losing benefits from using Linq to Entity.

Show 3 result from each element in group by

I am working on a system for handling meter reading.
I want to produce a output where the system displays all the meters belonging to the customer and for each meter, the three last readings.
So far, I have to followering code:
var lastMeterReading = from meeters in metermodel.Meeters
join reading in metermodel.Readings on meeters.MeterNumber equals reading.MeterNumber
where (maalers.CustNo == 6085574)
orderby reading.Date descending
group meeters by new { meeters.MeterNumber, reading.Consumption, reading.Date } into result
select new
{
Consumption = result.Key.Consumption, No = result.Key.MeterNumber, Date = result.Key.Date
};
Now, it shows all the meters belonging to the customer. If I put a .take(3), it only shows the first 3 results.
Thx!
Daniel
I think what you need is to put the .Take(3) in the right place.
In you case you probably did result.Take(3) but this means take the first three groups (with all their elements).
Below is an attempt to show what I mean, however, I suppose you will need to fix it in the last part, as I don't have data to test it on, and as such I'm not sure if what I'm trying to access is accessible at that point. But I hope you get what I mean.
var lastMeterReading = (from meeters in metermodel.Meeters
join reading in metermodel.Readings on meeters.MeterNumber equals reading.MeterNumber
where (maalers.CustNo == 6085574)
orderby reading.Date descending
group meeters by new { meeters.MeterNumber, reading.Consumption, reading.Date } into result
from m in result
select new {Key = m.Key, Info = result.OrderByDescending(r => r.Date).Take(3)})
.Select(r => new
{ Consumption = r.Consumption, No = r.MeterNumber, Date = r.Date });
Try this:
var lastMeterReading = from meeters in metermodel.Meeters
join reading in metermodel.Readings on meeters.MeterNumber equals reading.MeterNumber
where (maalers.CustNo == 6085574)
orderby reading.Date descending
group meeters by new { meeters.MeterNumber, reading.Consumption, reading.Date } into result
from m in result.Take(3)
select new
{
Consumption = m.Consumption, No = m.MeterNumber, Date = m.Date
};
You only want to group by MeterNumber. The way you're doing the grouping right now, you'll get a new group for every unique MeterNumber-Consumption-Date combination.
You can also simplify your query using LINQ's GroupJoin operator. In query syntax you use the "join..on..into" pattern:
from meter in meterModel.Meters
where (meter.CustNo == 6085574)
join reading in meterModel.Readings
on meter.MeterNumber equals reading.MeterNumber
into meterGroup
select meterGroup.OrderByDescending(r => r.Date).Take(3);
Or using dot notation:
meterModel.Meters
.Where(x => x.CustNo == 6085574)
.GroupJoin(
meterModel.Readings,
meter => meter.MeterNumber,
reading => reading.MeterNumber,
(meter,readings) => readings.OrderByDescending(r => r.Date).Take(3))
;

Categories

Resources