LINQ - Projection with grouping and Let local variable - linq

I'm trying to group my tickets by priority and figure out the average of time to resolve each ticket within those grouped priorities. I'm not sure how to go about getting the average in projection. How do I go about inserting the formula for the average in my projection, which is essentially number tickets devide by minElapse. It says minElapse is not in the current context. Being I'm grouping and using Let this is where I get confused on how to use the variable minElapse. Thanks
var results = from r in Tickets
where r.Team.Equals("ABC") && DateCreated >1330001100 && r.DateCreated<1350000000 //unix time
let minElapse = r.FinishDate - r.DateCreated
group r by r.priority into u
select new {
Priority = u.Key,
TicketCount = u.Count(),
AverageMin = u.Average( c => minElapse)
};

You could just make it not use a let:
var results = from r in Tickets
where r.Team.Equals("ABC") && r.DateCreated>1330001100 && r.DateCreated<1350000000
group r by r.priority into u
select new
{
Priority = u.Key,
TicketCount = u.Count(),
AverageMin = u.Average(x => x.FinishDate - x.DateCreated)
};

Related

LINQ using Group with Count and Where, easy SQL, harder in LINQ

I'm trying to display cities names where a count is greater than 1. I can do it easy in SQL and am close in LINQ but can't figure out how to use group and also get a count and display a name
var query = (from c in Consumer
group c
by new { c.City, size = c.City.Count() }
into results
select new { Name = results.Key.City })
.Where(a => size > 0);
The size part doesn't work
try this query:
var list= Consumer.GroupBy(s=>s.City)
.Select(s=>new {
City = s.Key,
size = s.Count(),
})
.Where(s=>s.size>0).ToList();

BLToolKit, Linq Query, SQL Not what I expected

I am using BLToolKit in a project of mine and I was trying to get this to work. What I don't like is that I am trying to average a bunch of temps down to the minute, but the select statement that is being generated groups by the minute but then selects the original time. I think I am doing the linq expression correctly (but then again, i am not getting the results i expect). (this is C#, if you care) Anyone know what is going wrong?
var test = (from r in db.SensorReadingRaws
where r.TimeLogged < DateTime.Now.AddMinutes(-2)
group r by new
{
Sensor = r.SensorNumber,
//group time down to the minute
Time = r.TimeLogged.AddSeconds(-1 * r.TimeLogged.Second).AddMilliseconds(-1 * r.TimeLogged.Millisecond)
} into grouped
select new SensorReading
{
SensorNumber = grouped.Key.Sensor,
TimeLogged = grouped.Key.Time,
Reading = (int)grouped.Average(x => x.Reading)
}).ToList();
textBox1.Text = db.LastQuery;
and the resulting query is this
SELECT
[r].[SensorNumber],
[r].[TimeLogged],
Avg([r].[Reading]) as [c1]
FROM
[SensorReadingRaw] [r]
WHERE
[r].[TimeLogged] < #p1
GROUP BY
[r].[SensorNumber],
DateAdd(Millisecond, Convert(Float, -DatePart(Millisecond, [r].[TimeLogged])), DateAdd(Second, Convert(Float, -DatePart(Second, [r].[TimeLogged])), [r].[TimeLogged])),
[r].[TimeLogged]
I discovered that
BLToolkit.Data.Linq.Sql.AsSql<T>(T obj)
can be used as a workaround for this case.
When applying this function to the required grouped key properties in select statement you get rid of grouping/selecting an original field.
It may look something like:
_queryStore.Leads().
GroupBy(x => new {
x.LeadDate.Hour,
x.LeadDate.Minute
}).
Select(x => new {
Hour = Sql.AsSql(x.Key.Hour),
Minute = Sql.AsSql(x.Key.Minute),
Count = x.Count()
});
and in your particular case:
var test = (from r in db.SensorReadingRaws
where r.TimeLogged < DateTime.Now.AddMinutes(-2)
group r by new
{
Sensor = r.SensorNumber,
//group time down to the minute
Time = r.TimeLogged.AddSeconds(-1 * r.TimeLogged.Second).AddMilliseconds(-1 * r.TimeLogged.Millisecond)
} into grouped
select new SensorReading
{
SensorNumber = grouped.Key.Sensor,
TimeLogged = Sql.AsSql(grouped.Key.Time),
Reading = (int)grouped.Average(x => x.Reading)
}).ToList();
I got same issue yesterday.
Today I found a workaround. The idea is to write 2 linq queries. First transforming the data and the second grouping the result:
var bandAndDate =
(from r in repo.Entities
select new {Band = r.Score / 33, r.StartTime.Date});
var examsByBandAndDay =
(from r in bandAndDate
group r by new {r.Band, r.Date } into g
select new { g.Key.Date, g.Key.Band, Count = g.Count() }).ToList();
Both this queries run one SQL that do the job:
SELECT
[t1].[c1] as [c11],
[t1].[c2] as [c21],
Count(*) as [c3]
FROM
(
SELECT
[r].[Score] / 33 as [c2],
Cast(Floor(Cast([r].[StartTime] as Float)) as DateTime) as [c1]
FROM
[Results] [r]
) [t1]
GROUP BY
[t1].[c2],
[t1].[c1]

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 get two sums from a linq query

Summary
I have a list of Transactions. Using Linq, I want to get a sum of the Cost and sum of the Quantity from this list in one query.
Grouping
My first thought is to use grouping - but I don't really have a key that I want to group on, I want just one group with the results from the whole list. So, I happen to have a property called "Parent" that will be the same for all of the transactions, so I'm using that to group on:
var totalCostQuery =
(from t in Transactions
where t.Status != GeneralStoreTransactionStatus.Inactive &&
(t.Type == GeneralStoreTransactionType.Purchase ||
t.Type == GeneralStoreTransactionType.Adjustment)
group t by t.Parent into g
select new
{
TotalCost = g.Sum(t => t.Cost.GetValueOrDefault()),
TotalQuantity = g.Sum(t => t.Quantity.GetValueOrDefault())
});
Grouping by t.Parent seems like it could be wrong. I really don't want to group at all, I just want the sum of t.Quantity and sum of t.Cost.
Is that the correct way to get a sum of two different properties or can it be done in a different way.
Assuming this is Linq to SQL or Entity Framework, you can do that:
var totalCostQuery =
(from t in Transactions
where t.Status != GeneralStoreTransactionStatus.Inactive &&
(t.Type == GeneralStoreTransactionType.Purchase ||
t.Type == GeneralStoreTransactionType.Adjustment)
group t by 1 into g
select new
{
TotalCost = g.Sum(t => t.Cost),
TotalQuantity = g.Sum(t => t.Quantity)
});
Note that you don't need to use GetValueOrDefault, null values will be ignored in the sum.
EDIT: not sure this works with Linq to NHibernate though...
Note that if you're using Linq to objects, the solution above won't be efficient, because it will enumerate each group twice (once for each sum). In that case you can use Aggregate instead:
var transactions =
from t in Transactions
where t.Status != GeneralStoreTransactionStatus.Inactive &&
(t.Type == GeneralStoreTransactionType.Purchase ||
t.Type == GeneralStoreTransactionType.Adjustment)
select t;
var total =
transactions.Aggregate(
new { TotalCost = 0.0, TotalQuantity = 0 },
(acc, t) =>
{
TotalCost = acc.TotalCost + t.Cost.GetValueOrDefault(),
TotalQuantity = acc.TotalQuantity + t.Quantity.GetValueOrDefault(),
});

How do I Count the number of parent groups using LINQ and child repo

I have a List of "Server Group" objects, which contain, "Monitored Servers" which in turn contain "Server Script Statuses". I have a repo of these script statuses. Currently I am trying to find the count for the number of Monitored servers. I have been messing with this for hours now and can't seem to get it. I keep getting back the count for the number of scripts within each Monitored Server, but I can't get the number of Monitored Servers under a Server Group. I'm new to LINQ, so sorry if this is something stupid. Thanks all!
//This is List of ScriptStatuses
var scriptsInCategory = scriptRepository.ScriptStatuses
.Where(p => p.MonitoredServer.ServerGroup.ServerCategory.Name == category);
var groupsList = new List<NodeSummaryListViewModel>();
foreach (var s in scriptsInCategory.GroupBy(p=>p.MonitoredServer.ServerGroup.Name))
{
NodeSummaryListViewModel viewModel = new NodeSummaryListViewModel
{
Name = s.First().MonitoredServer.ServerGroup.Name,
Description = s.First().MonitoredServer.ServerGroup.Description,
//I want NumServers to be the count of Monitored Servers under the server group
NumServers = (from z in scriptsInCategory
group z by z.MonitoredServer.ServerGroup into g
select new { ServerGroup = g.Key, MonSvrCount = g.Count() }).Count(),
NumOk = PopulateGroupCellData(CellState.Ok, s),
NumAttention = PopulateGroupCellData(CellState.Attention,s),
NumCritical = PopulateGroupCellData(CellState.Critical, s),
NumUnavailable = PopulateGroupCellData(CellState.Unavailable, s),
NumOffline = PopulateGroupCellData(CellState.Offline, s)
};
groupsList.Add(viewModel);
}
I think just
NumServers = s.Count(),
should work.
I'm not sure if I understand the question properly but does this make sense to you?
from g in groups select new{ g, monitoredServers = g.Servers.Count(s=>s.Scripts > 0)}
So you get a list of groups with the number of servers which are being monitored within that group.

Resources