Counting records in C# using LINQ - c#

I have a simple SQL query:
Select ID, COUNT(ID) as Selections, OptionName, SUM(Units) as Units
FROM tbl_Results
GROUP BY ID, OptionName
The results I got were:
'1' '4' 'Approved' '40'
'2' '1' 'Rejected' '19'
'3' '2' 'Not Decided' '12'
I have to encrypt the data in the database, and as such am unable to sum the data in relational form. To get around this I decrypt the data in the application layer, and want to use LINQ to query it there. I need the following results:
'1' 'Approved' '10'
'3' 'Not Deceided' '6'
'2' 'Rejected' '19'
'1' 'Approved' '15'
'1' 'Approved' '5'
'3' 'Not Deceided' '6'
'1' 'Approved' '10'
I put these results into class and create a strongly typed list:
public class results
{
public int ID {get;set;}
public string OptionName {get;set;}
public int Unit {get;set;}
}
I almost have the LINQ query to bring back the results like the SQL query about:
var q = from r in Results
group p.Unit by p.ID
int g
select new {ID = g.Key,
Selections = g.Count(),
Units = g.Sum()};
How do I ensure my LINQ query also give me the Option Name?
If I created a class called Statistics to hold my results how would I modify the LINQ query to give me List<Statistics> result set?
public class results
{
public int ID {get;set;}
public int NumberOfSelections { get; set; }
public string OptionName {get;set;}
public int UnitTotal {get;set;}
}

You're currently just grouping by the ID. Instead, as per the original query, you need to group by the option name too. That way each group's key will contain both the ID and the option name. Here's just that change:
var q = from r in Results
group r.Unit by new { p.ID, p.OptionName } into g
select new { ID = g.Key.ID,
OptionName = g.Key.OptionName
Selections = g.Count(),
UnitTotal = g.Sum() };
Now, it wasn't immediately obvious to me that you were selecting just the "unit" part for the group item. I missed "group p.Unit by" in the query expression. My bad... but others may do likewise. Here's an alternative, which makes the group contain the items, and then sums the units in the projection:
var q = from r in Results
group r by new { p.ID, p.OptionName } into g
select new { ID = g.Key.ID,
OptionName = g.Key.OptionName
Selections = g.Count(),
UnitTotal = g.Sum(x => x.Unit) };

This query groups by id and option name thus making it available:
var q = from r in Results
group p.Unit by new { ID = p.ID, OptionName = p.OptionName } into g
select new {
ID = g.Key.ID,
OptionName = g.Key.OptionName,
Selections = g.Count(),
Units = g.Sum()
};
To return your custom result class use:
var q = from r in Results
group p.Unit by new { ID = p.ID, OptionName = p.OptionName } into g
select new results() {
ID = g.Key.ID,
OptionName = g.Key.OptionName,
NumberOfSelections = g.Count(),
UnitTotal = g.Sum()
};

Related

LINQ Join and Sum Query returns duplicate Results

I am running this query in LINQ which joins tables and returns the sum of sales per District ID.
var resultsalescity = (from so in _companysales
join en in _entity
on so.Id equals en.Id
group so by so.Id into totaledorders
from tot in totaledorders
join ad in _address
on tot.Id equals ad.EntityId
select new Salescities {
Totaledvalue = totaledorders.Sum(s = >s.TotalCost),
District = ad.DistrictId.ToString(),
}).ToList();
return resultsalescity;
I get duplicates values returns:
[{"totaledvalue":3855.00,"district":"568"},{"totaledvalue":3855.00,"district":"568"}}
.....
How can I drop the duplicates on the district ID to return only one value for each district?
I'm getting 4 duplicates District ID in results with different sales values
It's correct. Because It's grouped by Id. The actual result of 4 duplicates District ID would be different _companysales.Id. So you might be looking for something like this
var groupedByIdAndDistrictData = (from so in _companysalesRepo.QueryNoTracking
join en in _entityRepo.QueryNoTracking on so.PharmacyId equals en.Id
join ad in _addressRepo.QueryNoTracking on so.PharmacyId equals ad.EntityId
group so by new {
so.Id,
ad.DistrictId
}
into totaledeorders
select new Salescities {
Totaledvalue = totaledeorders.Sum(s = >s.TotalCost),
District = totaledeorders.Key.DistrictId.ToString()
}).ToList();
var resultsalescity = groupedByIdAndDistrictData.GroupBy(p = >p.District)
.Select(g = >new Salescities {
Totaledvalue = g.Sum(s = >s.Totaledvalue),
District = g.Key
}).ToList();
return resultsalescity;

Linq Query Join to Subquery with In clause

I have a query from an old application that I am attempting to convert to an Entity Framework Core application. The relationship I am working with is a simple one to many, where one Order can have many OrderEvents. The old query looks something like this:
select Order.Id, mostRecent.*
from Order
left join OrderEvent mostRecent
on mostRecent.Id in (select top(1) Id
from OrderEvent
where OrderEvent.OrdId = Order.Id
and OrderEvent.PostDateTime is not null
order by OrderEvent.PostDateTime desc)
where Order.SomeColumn = 'some value'
I am struggling with figuring out how to write this query in LINQ. It doesnt seem that one can use anything other than equals when doing using join, so I first attempted something like:
var test = (from order in _context.Ord.AsNoTracking()
join mostRecentQuery in _context.OrdEvt.AsNoTracking()
on (from orderEvent in _context.OrdEvt.AsNoTracking()
where orderEvent.PostDateTime != null && orderEvent.OrdId == order.Id
orderby orderEvent.PostDateTime descending
select orderEvent.Id).FirstOrDefault()
equals mostRecentQuery.Id
into mostRecentResults
from mostRecent in mostRecentResults.DefaultIfEmpty()
select new
{
OrderId = order.Id,
OrderEvent = mostRecent
}).ToList();
Whatever sql this spit out, it appears to be so slow that I cant even run it connected to Sql Server. I am however able to run this query when using Sqlite and it generates the following sql:
SELECT 'big list of fields....'
FROM "Ord" AS "order"
LEFT JOIN "OrdEvt" AS "mostRecentQuery" ON COALESCE((
SELECT "orderEvent0"."Id"
FROM "OrdEvt" AS "orderEvent0"
WHERE "orderEvent0"."PostDateTime" IS NOT NULL AND ("orderEvent0"."OrdId" = "order"."Id")
ORDER BY "orderEvent0"."PostDateTime" DESC
LIMIT 1
), X'00000000000000000000000000000000') = "mostRecentQuery"."Id"
This is close do what I am going for, but Im not sure why the COALESCE function is being used.
Is it possible to represent the query I am trying to convert, in Linq query syntax?
I'm not sure what your problem is, because you left out why it doesn't compile.
Bute here and simple example whith a join and choosing one event for an order:
public class Order
{
public int OrderId { get; set; }
public string OrderName { get; set; }
}
public class OrderEvent
{
public int OrderId { get; set; }
public string EventName { get; set; }
public DateTime Date { get; set; }
}
public static void Main(string[] args) // Here our Exe-Args are delivered to our program..
{
List<Order> orders = new List<Order>()
{
new Order() { OrderId = 1, OrderName = "One" }
};
List<OrderEvent> orderEvents = new List<OrderEvent>()
{
new OrderEvent() { OrderId = 1, EventName = "EventTwo", Date = DateTime.Now.AddHours(-1) },
new OrderEvent() { OrderId = 1, EventName = "EventOne", Date = DateTime.Now },
new OrderEvent() { OrderId = 1, EventName = "EventThree", Date = DateTime.Now.AddHours(-2) },
new OrderEvent() { OrderId = 2, EventName = "EventX", Date = DateTime.Now },
};
var tmp = orders.GroupJoin( // Join something to our orders
orderEvents, // join all events
o => o.OrderId, // key of our order
e => e.OrderId, // foreign-key to order in our event
(o, es) => // lambda-expression "selector". One order "o" and multiple events "es"
es.OrderByDescending(e => e.Date) // We order our events
.Select(s => new // we build a new object
{
OrderId = o.OrderId,
OrderName = o.OrderName,
RecentEvent = s.EventName
}).First()); // we choose the object with biggest date (first after orderDesc)
foreach (var item in tmp)
{
Console.WriteLine(item.OrderId + ", " + item.OrderName + ", " + item.RecentEvent);
}
tmp = from o in orders
join e in orderEvents on o.OrderId equals e.OrderId into es
select new
{
OrderId = o.OrderId,
OrderName = o.OrderName,
RecentEvent = (from x in es orderby x.Date descending select x.EventName).First()
};
foreach (var item in tmp)
{
Console.WriteLine(item.OrderId + ", " + item.OrderName + ", " + item.RecentEvent);
}
}

retrieve last value from table in linq Extention method and bind with DataGridView

I am working on windows Form App. I want to retrieve last record from database and bind it with datagridview, I can get all value from this
var query2 = _context.Products.Join(_context.ProductInfos, c => c.Id, a => a.ProductId, (a, c) => new
{
Id = a.Id,
ItemName = a.ItemName,
CategoryName = a.Category.CategoryName,
UnitName = a.Unit.UnitName,
UnitValue = a.UnitSize,
Quantity = a.Quantity,
CostPrice = c.PurchasePrice,
SalePrice = c.SalePrice,
EntryDate = c.EntryDate,
ExpireDate = c.ExpireDate
}).toList();
StockListGrid.DataSource = query2;
but i only want the last inserted value, i use
var query2 = _context.Products.Join(_context.ProductInfos, c => c.Id, a => a.ProductId, (a, c) => new
{
Id = a.Id,
ItemName = a.ItemName,
CategoryName = a.Category.CategoryName,
UnitName = a.Unit.UnitName,
UnitValue = a.UnitSize,
Quantity = a.Quantity,
CostPrice = c.PurchasePrice,
SalePrice = c.SalePrice,
EntryDate = c.EntryDate,
ExpireDate = c.ExpireDate
}).ToList().LastOrDefault();
StockListGrid.DataSource = query2;
but this time i get no value.Please tell me how can i retrieve last inserted value?
Try using OrderByDescending
var query2 = _context.Products.Join(_context.ProductInfos, c => c.Id, a => a.ProductId, (a, c) => new
{
Id = a.Id,
ItemName = a.ItemName,
CategoryName = a.Category.CategoryName,
UnitName = a.Unit.UnitName,
UnitValue = a.UnitSize,
Quantity = a.Quantity,
CostPrice = c.PurchasePrice,
SalePrice = c.SalePrice,
EntryDate = c.EntryDate,
ExpireDate = c.ExpireDate
}).OrderByDescending(x => x.Id).First();
Or Max
var query2 = _context.Products.Join(_context.ProductInfos, c => c.Id, a => a.ProductId, (a, c) => new
{
Id = a.Id,
ItemName = a.ItemName,
CategoryName = a.Category.CategoryName,
UnitName = a.Unit.UnitName,
UnitValue = a.UnitSize,
Quantity = a.Quantity,
CostPrice = c.PurchasePrice,
SalePrice = c.SalePrice,
EntryDate = c.EntryDate,
ExpireDate = c.ExpireDate
}).Max(x => x.Id);
The type of your first query is a List<...>, all elements are of the same anonymous type Anonymous1. The type of your later query is one object of class Anonymous1. What does your StockListGrid.DataSource expect? A list or one single object?
ToList transports all elements from your Database management system (DBMS) to your local memory, after which you decide that you only want the last element
I see two problems
Why transport all elements if you only want the last one?
Is the last element of your joinresult defined? Is it the one with the highest Id? Or maybe the one with alphabetically last ItemName? Is it the one with the highest SalePrice?
Unfortunately Entity Framework does not support LastOrDefault. See Supported and Unsupported LINQ methods (linq to entities)
The trick around this would be sorting in ascending order by the property you consider last and then take the FirstOrDefault. Keep in mind that (depending on your sort property) there might be several items with the same sort value, so a second sort is needed
var result = dbContext.Products.Join(dbContext.ProductInfos, ...)
.OrderByDescending(joinResult => joinResult.SalePrice) // depending on your definition of Last
.ThenByDescending(joinResult => joinResult.Id) // in case there are two with same price
.FirstOrDefault();
This is much more efficient, because only one element is transported from your DBMS to your local memory.
Note that the result is only one element, not a List. If you want assign a List with only this last element to your DataSource
.ThenByDescending(joinResult => joinResult.Id)
.Take(1)
.ToList();
Again, only one anonymous object is transported

Entity Framework Linq to Object Mapping

I have one problem.
Database Shema
==================
|parts |
==================
| partId |textId |
==================
========================
texts |
========================
|TextId|LanguageId|text|
========================
============================
languages |
============================
|LanguageId|LanguageIsoCode|
============================
I want to map this result to the following object
public long PartId { get; set; }
public Dictionary<string,string> Name { get; set; }
eg.
{
PartId: 32020
Name: {["en": "Blah", "es": "Blah2"]}
}
this is what I have tried, but Im running into TimeOut with this query.
var query = (from p in _context.epc_parts
select new //select first dynamic object
{
PartId = p.PartID,
Code = p.PartName,
Name = (from trans in _context.epc_texts
join lang in _context.epc_languages on trans.LanguageID equals lang.LanguageID
where p.TextID == trans.TextID
select new
{
LanguageId = lang.shortname.ToLower(),
Caption = trans.Caption
})
}).AsEnumerable().Select(x => new SearchPartModel //transform it here when we can use dictionary
{
Code = x.Code,
PartId = x.PartId,
Name = x.Name.ToDictionary(t => t.LanguageId, t => t.Caption)
});
The parts table has about 60k rows for every row there are 7 translations. Navigation properties cant be used because the Shema doesn't use foreign keys and the model is generated from db.
I have solved it with this query. For the whole query it took about 20s to load everything what is ok for this purpose. Im using group by first.
(from p in _context.epc_parts
join trans in _context.epc_texts on p.TextID equals trans.TextID
join lang in _context.epc_languages on trans.LanguageID equals lang.LanguageID
select new
{
PartId = p.PartID,
Code = p.PartName,
Caption = trans.Caption,
LanguageId = lang.shortname.ToLower()
}).AsEnumerable().GroupBy(x => x.PartId).Select(g => new SearchPartModel
{
Code = g.Select(x => x.Code).FirstOrDefault(),
PartId = g.Key,
Name = g.Select(x => new
{
x.LanguageId,
x.Caption
}).Distinct().ToDictionary(y => y.LanguageId, y => y.Caption)
});
from p in _context.epc_parts
join trans in _context.epc_texts on p.TextID equals trans.TextID
join lang in _context.epc_languages on trans.LanguageID equals lang.LanguageID
select new {
PartId = p.PartID,
Code = p.PartName,
Name = new
{
LanguageId = lang.shortname.ToLower(),
Caption = trans.Caption
}
}
That should be better, otherwise in your current implementation for each part you're querying the texts table

linq combine 2 tables into one list

I have 2 tables in db - one is EmploymentRecords one is EmploymentVerificationRecords
I want to query both tables and return one List
I have a Model (simplified example):
Public Class Record
{
int ID {get; set;}
string name {get; set;}
bool IsVerification {get; set;}
}
I want to have some type of query in LINQ like :
var records = from a in _context.EmploymentRecords
join b in _context.EmploymentVerificationRecords on a.id equals b.id
where a.UserID = 1
select new Record() { .ID = a.id, .name = a.name, .IsVerification = false}
// I also want to select a new Record() for each b found
see - I also want a new Record() added to the results for each record found in the second table , for these results IsVerification would be True
You can select everything from DB as it is selected now (but I would rather use join/into to do that) and then flatten results into one big collection using LINQ to Objects.
Following should do the trick:
var records
= (from a in _context.EmploymentRecords
join b in _context.EmploymentVerificationRecords on a.id equals b.id into bc
where a.UserID = 1
select new {
a = new Record() { ID = a.id, name = a.name, IsVerification = false},
b = bc.Select(x => new Record() { ID = x.ID, name = b.name, IsVerification = true })
}).AsEnumerable()
.SelectMany(x => (new [] { x.a }).Concat(x.b));

Categories