NOTE : This is NOT a duplicate of this useful SO question, my problem is all about the TruncateTime inside the GroupBy clause. See explanations below :
I'd like to use DbFunctions.TruncateTime in a multiple GroupBy clause, but it doesn't seem to work in my ASP.NET MVC5 project.
Here is a first lambda I wrote, it gives me the total number of views per day for a set of data.
It gives me the expected result :
var qViews = dbContext.TABLE_C.Where(c => c.IdUser == 1234)
.Join(dbContext.TABLE_V.Where(v => v.Date > DbFunctions.AddMonths(DateTime.Now, -1)), c => c.Id, v => v.Id, (c, v) => new { c, v })
.GroupBy(x => DbFunctions.TruncateTime(x.v.MyDateTimeColumn))
.Select(g => new
{
Date = (DateTime)g.Key,
NbViews = g.Count(),
}).ToDictionary(p => p.Date, p => p.NbViews);
Result is something like that :
...
Date | Views
03/07/2018 | 15
03/08/2018 | 8
03/09/2018 | 23
Now, I'd like a more detailled result, with the number of views per day AND PER ITEM on the same set of data.
Here is what I'd like to write :
var qViews = dbContext.TABLE_C.Where(c => c.IdUser == 1234)
.Join(dbContext.TABLE_V.Where(v => v.Date > DbFunctions.AddMonths(DateTime.Now, -1)), c => c.Id, v => v.Id, (c, v) => new { c, v })
.GroupBy(x => new { DbFunctions.TruncateTime(x.v.MyDateTimeColumn), x.c.Id}) // Issue #1
.Select(g => new
{
Date = g.Key.Date, //Issue #2
NbViews = g.Count(),
}).ToDictionary(p => p.Date, p => p.NbViews);
And I expected something like that :
...
Date | Views | ID Item
03/07/2018 | 4 | 456789
03/07/2018 | 11 | 845674
03/08/2018 | 6 | 325987
03/08/2018 | 1 | 548965
03/08/2018 | 1 | 222695
03/09/2018 | 23 | 157896
So, this request have two issues (see comments above)
Issue #1 : It seems I can't GroupBy multiple columns, which one of them use DbFunctions. If I use .GroupBy(x => new { x.v.MyDateTimeColumn, x.c.Id }), code compiles, but doesn't give me the expected result, as I want to group by date, not date + time
Issue #2 : Date = g.Key.Date, seems wrong for the compiler. When I wrote g.Key, autocompletion only suggests me the Id column, but it doesn't see the truncated date.
Why can't I GroupBy multiple columns, with one of them is a truncated Date ?
Is there any workaround ?
You need to give your anonymous type's properties names if you want to use them later on:
.GroupBy(x => new
{ Date = DbFunctions.TruncateTime(x.v.MyDateTimeColumn),
Id = x.c.Id
})
Then you can project on that:
.Select(g => new
{
Date = g.Date,
NbViews = g.Count(),
})
And finally you cannot do this:
.ToDictionary(p => p.Date, p => p.NbViews);
because you will get this error:
An item with the same key has already been added.
Why? Because the Date is not unique since you just grouped by Date and Id so Date(s) will be duplicated. It is the same as this but this is a list of string:
var nums = new List<string> { "1", "1", "1", "2" };
nums.ToDictionary(x => x, x => x);
But, perhaps, you may want to do this:
var lu = nums.ToLookup(x => x, x => x);
And now you can look them up:
// Returns 3 items since there are 3 "1"s
IEnumerable<string> ones = lu["1"];
Related
select p.ProduitNom,v.VonduDate,p.ProduitPrix from Produits p,Vondus v
where p.ProduitId = v.ProduitId and p.CentreId=1
How to do this request in entity framework ?
You can do that as shown below.
Inner Join :
from p in db.Produits
join v in db.Vondus on p.ProduitId equals v.ProduitId
where p.CentreId=1
select new {
ProduitNom = p.ProduitNom,
VonduDate = v.VonduDate,
ProduitPrix = p.ProduitPrix
}
If you would like to learn,you can refer this : Queries in LINQ to Entities
You can use Join:
EDIT:
You should have a context to connect with Database first, or else, at least 2 lists:
List<Produits> Produits = new List<Produits>();
List<Vondus> Vondus = new List<Vondus>();
Then using below lambda expression:
var res = Produits.Join(Vondus, p => p.ProduitId, v => v.ProduitId,
(p, v) => new { p, v })
.Where(pv => pv.p.ProduitId == pv.v.ProduitId && pv.p.CentreId == 1)
.Select(pv => new { pv.p.ProduitNom, pv.v.VonduDate, pv.p.ProduitPrix)
.ToList();
The res will be a list containts of ProduitNom, VonduDate and ProduitPrix
This is what i am trying to achieve:
var result = (string)null;
foreach (var tagId in MultipleTags)
{
result = string.Join(",", db.Tags.Where(t => t.TagId == tid).Select(e => e.TagName));
}
This result variable should hold all the values in comma seperated form while MultipleTags contain sets of ids so it will iterate more than one time.
how should i write it. please suggest me.
I am doubtfull if this line is correctly written.
db.Tags.Where(t => t.TagId == tid).Select(e => e.TagName)
please suggest me something.
This does the trick:
//assuming that MultipleTags is an array of "ids"
var result = db.Tags.Where(t => MultipleTags.Contains(t.TagId)).Select(e => e.TagName).ToList().Aggregate((a, b) => a + "," + b);
Try this too
var result = db.Tags.Where(t => MultipleTags.Contains(t.TagId)).ToList().ForEach((item) =>
{
item.TagName = item.id + "," + item.TagName;
});
try using this
var result = (string)null;
result=string.Join(",", (from a in db.Tags join m in MultipleTags on a.TagId equals m select a.TagName));
I have a table with the following fields:
Id Name Score Event
(1 John 2 3)
(2 John 4 3)
(3 john 5 3)
and I would like to get the following result:
(John, "2 +4+5", 11(Total score))
I'm trying to combine the results to show the sum in a string format then show the sum result.
Can anyone please help me with this issue.
Thank you,
Franciso
You need to call the toList() method before using methods like string.Format or string.Join because they are not supported by Linq to SQL.
You should modify your code into something like this:
var group = (from Classificacao in _dbSet.classificacao
from Evento in _dbSet.evento
where Classificacao.class_id_Evento == id_evento
where Evento.id_evento == id_evento
group Classificacao by new
{
Classificacao.class_atletaNome,
Classificacao.class_numAtleta,
Classificacao.class_atletaEquipa,
Classificacao.class_paisAtleta,
Evento.even_name,
Evento.local.local_nome,
Evento.compTipo.CompTipo_name
} into g).ToList()
The idea is to materialize the query into a list before calling the methods.
var model = group.Select(g => new
{
g.Key.class_numAtleta,
g.Key.class_atletaEquipa,
g.Key.class_atletaNome,
g.Key.class_paisAtleta,
class_fianlAtleta = g.Sum(c => c.class_fianlAtleta.Value),
class_classGeral = g.Min(c => c.class_fianlAtleta.Value),
class_nome = g.Key.even_name,
local_nome = g.Key.local_nome,
CompTipo_name = g.Key.CompTipo_name,
class_40ponto = ((from u in g
where u.class_id_Evento == id_evento
group u by new
{
u.class_atletaNome,
u.class_fianlAtleta
}
into k
select new
{
//Score = string.Concat(k.Key.class_fianlAtleta.ToString(), " + ", g.Key.class_atletaNome)
Score = string.Format("{0}",k.Key.class_fianlAtleta)
})).Select(c=>c.Score).FirstOrDefault()
}).OrderBy(c => c.class_fianlAtleta).ThenBy(c => c.class_classGeral)).toList();
I can't check if the code is correct therefore I leave it to you, anyway once you have your List in memory you can apply the methods supported by Linq to entity.
Here is my code:
var model = (from Classificacao in _dbSet.classificacao
from Evento in _dbSet.evento
where Classificacao.class_id_Evento == id_evento
where Evento.id_evento == id_evento
group Classificacao by new
{
Classificacao.class_atletaNome,
Classificacao.class_numAtleta,
Classificacao.class_atletaEquipa,
Classificacao.class_paisAtleta,
Evento.even_name,
Evento.local.local_nome,
Evento.compTipo.CompTipo_name
} into g
select new
{
g.Key.class_numAtleta,
g.Key.class_atletaEquipa,
g.Key.class_atletaNome,
g.Key.class_paisAtleta,
class_fianlAtleta = g.Sum(c => c.class_fianlAtleta.Value),
class_classGeral = g.Min(c => c.class_fianlAtleta.Value),
class_nome = g.Key.even_name,
local_nome = g.Key.local_nome,
CompTipo_name = g.Key.CompTipo_name,
class_40ponto = ((from u in g
where u.class_id_Evento == id_evento
group u by new
{
u.class_atletaNome,
u.class_fianlAtleta
}
into k
select new
{
//Score = string.Concat(k.Key.class_fianlAtleta.ToString(), " + ", g.Key.class_atletaNome)
Score = string.Format("{0}",k.Key.class_fianlAtleta)
})).Select(c=>c.Score).FirstOrDefault()
}).OrderBy(c => c.class_fianlAtleta).ThenBy(c => c.class_classGeral);de here
You need something like this:
var result = table
.GroupBy(t => new { t.Name, t.Event })
.ToList()
.Select(t => new
{
Name = t.Key.Name,
JoinedScores = string.Join(" + ", t.Select(group => group.Score).ToArray()),
TotalScore = t.Sum(group => group.Score).ToString() + " (Total Score)"
}).ToList();
The first call to the ToList() method is needed because Linq to SQL will not support the calls to string.Join(). I'm not sure but probably you can replace it with a call to AsEnumerable().
I can use LINQ's Join with Lambda notations no problem, but I can't work out how one would then add a where condition.
var q = query.Join(context.CustomerIds,
x => x.CustomerId,
y => y.CustomerId,
(x, y) => new CustomerLookupResult()
{
dob = x.DateOfBirth.ToString(),
forenames = x.Forenames,
surname = x.Surname,
loyaltyNo = y.Identifier,
customerId = x.CustomerId
});
The table I'm joining the first to contains the loyaltyNo in its Identifier column, but also contains other information in the same column and so uses a second column IdentifierTypeCode to allow filtering.
So how do I now add .Where(x => x.IdentifierTypeCode == "LOYALTY") like I would in SQL. Appending this to end refers to the new object.
You could apply your Where before doing the join.
var q = customerLoyalties
.Where(x => x.IdentifierTypeCode == "LOYALTY")
.Join(customers,
x => x.CustomerId,
y => y.CustomerId,
(x, y) => new CustomerLookupResult()
{
CustomerId = y.CustomerId,
Name = y.Name,
IdentifierTypeCode = x.IdentifierTypeCode
});
You can also use this way to achieve that using Linq.
var match = from t1 in context.orders
join t2 in context.orderdetails on
new { t1.OrderID } equals
new { t2.OrderID }
join t3 in context.products on
new { t2.ProductID } equals
new { t3.ProductID }
where t3.ProductID == id
select t3;
return match.ToList();
The first parameter to the Join takes any IEnumerable, so you can apply the Where at that point, or earlier
var q = query.Join(context.CustomerIds.Where(x=>x.IdentifierTypeCode=="LOYALTY"),
x => x.CustomerId,
y => y.CustomerId,
(x, y) => new CustomerLookupResult()
{
dob = x.DateOfBirth.ToString(),
forenames = x.Forenames,
surname = x.Surname,
loyaltyNo = y.Identifier,
customerId = x.CustomerId
});
alternatively, if you don't like to put too much on one line:
var filteredLoyalties = context.CustomerIds.Where(x=>x.IdentifierTypeCode=="LOYALTY");
var q = query.Join(filteredLoyalties,
...
I expect a very simple solution, but I can't for the life of me figure this out...
I am trying to create the LINQ equivalent of this:
SELECT Group, COUNT(*) as GroupCount
FROM table1
WHERE Valid > 0
GROUP BY Group, Project
I have this so far:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => new { r.GROUP, r.Project})
.Select(r => new{ r.GROUP, GroupCount = r.count()};
What is wrong with my query? Visual studio throws and error stating that:
System.Linq.IGrouping' does not contain a definition for 'GROUP' and no extension method 'GROUP' accepting a first argument of type 'System.Linq.IGrouping' could be found (are you missing a using directive or an assembly reference?)
EDIT: Just a note: The above error is given for r.Group in the Select clause.
It also throws an error stating that the count extension method doesn't exist, but I've seen other examples done this way. Maybe I was looking at an example from an old version of LINQ?
EDIT2: Some example data
GroupName ProjectID Step Other Info...
-----------------------------------------------
GroupA | 1 | 1 | ..............
GroupA | 1 | 2 |..............
GroupA | 3 | 1 | ..............
GroupB | 4 | 1 | ..............
GroupB | 5 | 1 | ..............
GroupC | 6 | 1 |..............
Desired result:
GroupName Group Count
---------------------------
GroupA | 2
GroupB | 2
GroupC | 1
Well, you have following errors in the .Select statement:
.count should be .Count (C# is case sensitive)
r.GROUP should be r.Key or r.Key.GROUP (.GroupBy() returns an IGrouping<TKey, TElement>)
You forgot the last parenthesis in the Select method. (But maybe that was just a typo in the example)
Result:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => new { r.GROUP, r.Project })
.Select(r => new { r.Key.GROUP, GroupCount = r.Count() });
UPDATE:
After the comments and question update; it looks like you are only grouping by group, so that would turn in to something like this to yield the output you requested:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => new { r.GROUP }) // First, group only on GROUP
.Select(r => new
{
r.Key.GROUP,
// Second, group on unique ProjectId's
GroupCount = r.GroupBy(g => g.Project).Count()
});
Or simplified:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => r.GROUP) // First, group only on GROUP
.Select(r => new
{
Group = r.Key,
// Second, group on unique ProjectId's
GroupCount = r.GroupBy(g => g.Project).Count()
});
As you asked in the comments, yes, you could say that the values are concatenated in the GroupBy clause. So new { r.GROUP, r.Project } would group all pairs with the same value.
LINQ queries are case-sensitive. You're trying to access r.GROUP, which probably doesn't exist. Use r.Group instead. Also change r.count() to r.Count(). Here's the corrected code:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => new { r.Group, r.Project})
.Select(r => new{ r.Key, GroupCount = r.Count()};
When you're doing the Group By, you're creating a new anonymous type (not of the type you think it is). Thus, when you're doing the select statement, it has no idea what the heck GROUP is.
To mitigate this, follow the 2nd answer located here.