.net linq entity framework groupby - c#

I have a db table with columns like
DaysInTrade, PercentGain
i want a sum of PercentGain grouped by DaysInTrade
so:
DaysInTrade,PercentGain
1, 5
1,6
2,4
Would give me:
DaysInTrade, Sum
1, 11
2,4
I was not able to get intellisense working in the "select new" part unless I used something like Sum or Select. the DIT column below shows up blank no matter if I do ts.DaysInTrade, or gts.DaysInTrade
var partialQuery = (from ts in context.TradeSnapshots
group ts by ts.DaysInTrade
into gts
select
new
{
profit = gts.Sum(s => s.PercentGain),
DIT = gts.Select(ts=>ts.DaysInTrade)
});
dataGridViewTradeSnapshots.DataSource = partialQuery.ToList();

You want to use the key on the grouping. See the documentation on IGrouping<TKey,TElement>
var partialQuery = (from ts in context.TradeSnapshots
group ts by ts.DaysInTrade
into gts
select
new
{
profit = gts.Sum(s => s.PercentGain),
DIT = gts.Key
});
dataGridViewTradeSnapshots.DataSource = partialQuery.ToList();

Related

linq and DTO property interpolation

In the below query I order by "Category1":
var ExampleValues = (from values in ExampleAllRecorsds
group values by new { values.Category1, values.Date.Value.Month } into x
orderby x.Key.Category1
select new ExamplesDto()
{
ExampleId = exampleId,
Row = x.Key.Category1,
Month = x.Key.Month,
}).ToList();
I have other properties that I want to order my query by, depending on another variable. It could be ordering by Category2, Category3, etc.
What I want to do is change the ordering of this query between my CategoryX properties without needing to wholesale repeat the query in a switch statement.
For example, this is how I would think to do it with a switch. Notice that this is repeating a lot of code.
switch(categoryVar){
case "Category1":
var ExampleValues = (from values in ExampleAllRecorsds
group values by new { values.Category1, values.Date.Value.Month } into x
orderby x.Key.Category1 // the only change
select new ExamplesDto()
{
ExampleId = exampleId,
Row = x.Key.Category1,
Month = x.Key.Month,
}).ToList();
break;
case "Category2":
var ExampleValues = (from values in ExampleAllRecorsds
group values by new { values.Category1, values.Date.Value.Month } into x
orderby x.Key.Category2 // the only change
select new ExamplesDto()
{
ExampleId = exampleId,
Row = x.Key.Category2,
Month = x.Key.Month,
}).ToList();
break
...
}
How do I not repeat myself?
If you don't mind using an extra library and are on .net Core you could try:
System.Linq.Dynamic.Core
I'm pretty sure there is a System.linq.dynamic for Framework as well, you can check nuget for both

Group by LINQ vs group by SQL

I have the following query in SQL which I would like to convert to LINQ statement.
select AreaId, BrandId, MilestoneId, DocumentCategoryId
from Document
group by AreaId, Brandid, MilestoneId, DocumentCategoryId
I have tried, e.g.,
var docs =
from d in documents
group d by new
{
d.Area,
d.Brand,
d.MilestoneId,
d.DocumentCategoryId
} into gcs
select new Group()
{
Area = gcs.Key.Area,
Brand = gcs.Key.Brand,
MilestoneId = gcs.Key.MilestoneId,
DocumentCategoryId = gcs.Key.DocumentCategoryId,
};
And
var docs = documents
.GroupBy(d => new Group
{
Area = d.Area,
Brand = d.Brand,
MilestoneId = d.MilestoneId,
DocumentCategoryId = d.DocumentCategoryId,
})
but the result in SQL returns 88 rows (the aim), in query syntax 78 rows and in LINQ 270 (total number).
I would like a LINQ statement for to return 88 rows.
I expect the final version is essentially switching to LINQ-to-Objects - i.e. populating a Group object per row, then grouping in .NET terms, which will mean one row per group as I doubt your Group implements the right patterns for equality. To get LINQ to treat the last version correctly, you probably need to add an anonymous type into the mix (LINQ understands this should behave like a tuple):
var docs = documents
.GroupBy(d => new
{
Area = d.Area,
Brand = d.Brand,
MilestoneId = d.MilestoneId,
DocumentCategoryId = d.DocumentCategoryId,
}).Select(grp => new Group {
Area = grp.Key.Area,
Brand = grp.Key.Brand,
MilestoneId = grp.Key.MilestoneId,
DocumentCategoryId = grp.Key.DocumentCategoryId,
});
As for the 88 vs 78: have you looked at what SQL is being issued? That should tell you what it is doing differently.

get all the latest entries from table linq

I have two tables like below: (date format : mm/dd/yyyy)
Parameter master table:
Id Parameter
1 ST
2 GP
3 Interest
4 CC
And Second TaxValue table :
Date ParameterId Value
1/1/2017 1 4
2/1/2017 1 4.5
1/15/2017 2 15
3/20/2017 2 20
3/21/2017 3 18
4/28/2017 3 20
1/1/2017 4 10
I want to write a linq query to get all the latest parameters values from the date specified.
Desired Result: (if I want to get latest entries for today)
Date Parameter Value
2/1/2017 ST 4.5
3/20/2017 GP 20
4/28/2017 Interest 20
1/1/2017 CC 10
Please help!!
User OrderByDescending
var result = from n in table
group n by n.Value into g
select g.OrderByDescending(t=>t.Date).FirstOrDefault();
You can do a join on the latest TaxValue record like this:
var qry = (from pm in parameterMaster
from tax in taxValue.Where(t => t.ParameterId == pm.Id)
.OrderByDescending(t => t.Date)
.Take(1)
.DefaultIfEmpty()
select new {
date = tax.Date,
parameter = pm.Name,
value = tax.Value
});
var results = qry.ToList();
You can use this query: (I tested it and it worked properly)
using (testEntities db = new testEntities())
{
var res = from element in
(from p in db.Parameters
join t in db.TaxValues on p.Id equals t.ParameterId
select new { Date = t.Date, Parameter = p.Parameter1, Value = t.Value, ParameterId = t.ParameterId }).ToList()
group element by element.ParameterId
into groups
select groups.OrderBy(p => p.Value).Last();
}
Result

Group By with multiple column

I have data in a table as below
RowId | User | Date
--------------------------
1 A 2015-11-11 08:50:48.243
2 A 2015-11-11 08:51:01.433
3 B 2015-11-11 08:51:05.210
Trying to get the data as below:
User, Date, Count
A 2015-11-11 2
B 2015-11-11 1
Select User,Date,Count(User) from Table1
Group By User,Date
It is returning me 3 rows because of time involved in Date field.
How to get this in SQL and Linq.
Please suggest me.
EDITING:
I am able to get it in SQL
Select User,Cast(Date as Date),Count(User) from Table1
Group By User,Cast(Date as Date)
EDITING:
adding linq query
var details = db.table1.GroupBy( r => new { r.RowId,r.User,r.Date})
.Select(g => new {Name = g.Key, Count = g.Count()}).ToList();
For Linq Query just do the following: (you need to import using System.Data.Entity.SqlServer namespace.
Execute this linq query all calculations are done on the server database. Notice that Table1s represents the DbSet for Table1 and context is your DbContext instance.
var query = from item in context.Table1s
group item by new
{
item.User,
Year = SqlFunctions.DatePart("yyyy", item.Date),
Month = SqlFunctions.DatePart("mm", item.Date),
Day = SqlFunctions.DatePart("dd", item.Date)
} into g
select new { g.Key.User, g.Key.Year, g.Key.Month, g.Key.Day, Count = g.Count() };
Then create the final result like this:
var result = query.ToList().Select(p =>
new
{
p.User,
Date = new DateTime(p.Year.Value, p.Month.Value, p.Day.Value),
p.Count
}).ToList();
Other solution is to create a SQL View that will be used by DbContext to retrive the data you want. The SQL View body must be the SQL your wrote in your question.
EDIT 2 : DbFunctions
Like Cetin Basoz pointed in comments we can use System.Data.Entity.DbFunctions as well. And the code is more cleaner than using SqlFunctions. This will work only with EF 6 and greater. The version using SqlFunctions work with EF 4 and greater.
var query = from item in context.Table1s
group item by new
{
item.User,
Date = DbFunctions.TruncateTime(item.Date)
} into g
select new { g.Key.User, g.Key.Date, Count = g.Count() };
EDIT 1 : this is specific for Cetin Basoz's answer :
As we all know using AsEnumerable is not efficient for doing what is needed.
The second solution he gives us which is :
var grouped = from d in db.MyTable
group d by new {
User = d.User,
Date=d.Date.HasValue ? d.Date.Value.Date : (DateTime?)null} into g
select new {User=g.Key.User, Date=g.Key.Date, Count=g.Count()};
This solution just not work because of this :
The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
If the time is the problem, you can first convert it:
select User, CAST(dateColumn AS DATE) as dateConverted
into #tempTable
from myTable
then using a window function or a group by:
select *,
count(user) over (partition by date) as userCount
from #tempTable
This should work in SQL server, don't know about Linq
edit: If the date part is the problem, just select into from your table to a table with the casted date. Then you won't have this problem in Linq.
var grouped = from d in db.MyTable.AsEnumerable()
group d by new {
User = d.User,
Date=d.Date.HasValue ? d.Date.Value.Date : (DateTime?)null} into g
select new {User=g.Key.User, Date=g.Key.Date, Count=g.Count()};
Sooner or later, someone would say that this is not server side grouping and would suffer from performance and they would be right. Without Enumerable it is serverside but at the cost of another call per group, so here is another way:
public class MyResult
{
public string User {get;set;}
public DateTime? Date {get;set;}
public int Count {get;set;}
}
var grouped = db.ExecuteQuery<MyResult>(#"select [User],
Cast([Date] as Date) as [Date],
Count(*) as [Count]
from myTable
group by [user], Cast([Date] as Date)");
EDIT: I don't know why I thought the other way before, this would just work serverside and do it, AsEnumerable() was not needed:
var grouped = from d in db.MyTable
group d by new {
User = d.User,
Date=d.Date.HasValue ? d.Date.Value.Date : (DateTime?)null} into g
select new {User=g.Key.User, Date=g.Key.Date, Count=g.Count()};

LINQ group by month question

I'm new to LINQ to SQL and I would like to know how to achieve something like this in LINQ:
Month Hires Terminations
Jan 5 7
Feb 8 8
Marc 8 5
I've got this so far, and I think there is something wrong with it but I'm not sure:
from term1 in HRSystemDB.Terminations
group term1 by new { term1.TerminationDate.Month, term1.TerminationDate.Year } into grpTerm
select new HiresVsTerminationsQuery
{
Date = Criteria.Period,
TerminationsCount = grpTerm.Count(term => term.TerminationDate.Month == Criteria.Period.Value.Month),
HiresCount = (from emp in HRSystemDB.Persons.OfType<Employee>()
group emp by new { emp.HireDate.Month, emp.HireDate.Year } into grpEmp
select grpEmp).Count(e => e.Key.Month == Criteria.Period.Value.Month)
});
Thanks in advance.
I'm not quite sure where does the Criteria.Period value come from in your sample query.
However I think you're trying to read both hires and terminations for all available months (and then you can easily filter it). Your query could go wrong if the first table (Termination) didn't include any records for some specified month (say May). Then the select clause wouldn't be called with "May" as the parameter at all and even if you had some data in the second table (representing Hires), then you wouldn't be able to find it.
This can be elegantly solved using the Concat method (see MSDN samples). You could select all termniations and all hires (into a data structure of some type) and then group all the data by month:
var terms = from t in HRSystemDB.Terminations
select new { Month = t.TerminationDate.Month,
Year = term1.TerminationDate.Year,
IsHire = false };
var hires = from emp in HRSystemDB.Persons.OfType<Employee>()
select new { Month = emp.HireDate.Month,
Year = emp.HireDate.Year
IsHire = true };
// Now we can merge the two inputs into one
var summary = terms.Concat(hires);
// And group the data using month or year
var res = from s in summary
group s by new { s.Year, s.Month } into g
select new { Period = g.Key,
Hires = g.Count(info => info.IsHire),
Terminations = g.Count(info => !info.IsHire) }
When looking at the code now, I'm pretty sure there is some shorter way to write this. On the other hand, this code should be quite readable, which is a benefit. Also note that it doesn't matter that we split the code into a couple of sub-queries. Thanks to lazy evalutation of LINQ to SQL, this should be executed as a single query.
I don't know if it shorter but you can also try this version to see if it works better with your server. I don't know exactly how these two answers turn into SQL statements. One might be better based on your indexs and such.
var terms =
from t in Terminations
group t by new {t.Month, t.Year} into g
select new {g.Key, Count = g.Count()};
var hires =
from p in Persons
group p by new {p.Month, p.Year} into g
select new {g.Key, Count = g.Count()};
var summary =
from t in terms
join h in hires on t.Key equals h.Key
select new {t.Key.Month, t.Key.Year,
Hires = h.Count, Terms = t.Count};

Categories

Resources