C# EF Deep Lambda Distinct Count Query - c#

This is the query I am trying to do.
var commentActivity = project.ProjectDoc
.Select(c => c.Comment.Select(i => i.UserID))
.Distinct()
.Count();
What I want is the number of comments from distinct users on a specific project, but ANY ProjectDoc. This query "works" the result is just wrong. The model is like this, generically sketched.
Project
ProjectDoc
Comment
Update: I had to go one level deeper, based on the answer below I tried a few things that didn't work so I though I would post this as a reference. Note the two SelectMany methods.
var replyActivity = project.ProjectDoc
.SelectMany(c => c.Comment.SelectMany(r => r.CommentReply.Select(u => u.UserID)))
.Distinct()
.Count();

Use SelectMany instead of Select
project.ProjectDoc
.SelectMany(c => c.Comment.Select(i => i.UserID))
.Distinct()
.Count()

var data = (from con in project.ProjectDoc
select new
{
CommentCount=project.Comment.Count(x=>x.UserID==con.UserID)
}).ToList();
i think this will help you.

Related

LINQ to Entities does not recognize the method using LastOrDefault [duplicate]

In this query:
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderBy(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.Last());
}
I had to switch it to this for it to work
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.FirstOrDefault());
}
I couldn't even use p.First(), to mirror the first query.
Why are there such basic limitations in what's otherwise such a robust ORM system?
That limitation comes down to the fact that eventually it has to translate that query to SQL and SQL has a SELECT TOP (in T-SQL) but not a SELECT BOTTOM (no such thing).
There is an easy way around it though, just order descending and then do a First(), which is what you did.
EDIT:
Other providers will possibly have different implementations of SELECT TOP 1, on Oracle it would probably be something more like WHERE ROWNUM = 1
EDIT:
Another less efficient alternative - I DO NOT recommend this! - is to call .ToList() on your data before .Last(), which will immediately execute the LINQ To Entities Expression that has been built up to that point, and then your .Last() will work, because at that point the .Last() is effectively executed in the context of a LINQ to Objects Expression instead. (And as you pointed out, it could bring back thousands of records and waste loads of CPU materialising objects that will never get used)
Again, I would not recommend doing this second, but it does help illustrate the difference between where and when the LINQ expression is executed.
Instead of Last(), Try this:
model.OrderByDescending(o => o.Id).FirstOrDefault();
Replace Last() by a Linq selector OrderByDescending(x => x.ID).Take(1).Single()
Something like that would be works if you prefert do it in Linq :
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters.OrderBy(p => p.ServerStatus.ServerDateTime).GroupBy(p => p.RawName).Select(p => p.OrderByDescending(x => x.Id).Take(1).Single());
}
Yet another way get last element without OrderByDescending and load all entities:
dbSet
.Where(f => f.Id == dbSet.Max(f2 => f2.Id))
.FirstOrDefault();
That's because LINQ to Entities (and databases in general) does not support all the LINQ methods (see here for details: http://msdn.microsoft.com/en-us/library/bb738550.aspx)
What you need here is to order your data in such a way that the "last" record becomes "first" and then you can use FirstOrDefault. Note that databasese usually don't have such concepts as "first" and "last", it's not like the most recently inserted record will be "last" in the table.
This method can solve your problem
db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();
Adding a single function AsEnumerable() before Select function worked for me.
Example:
return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName).AsEnumerable()
.Select(p => p.FirstOrDefault());
Ref:
https://www.codeproject.com/Questions/1005274/LINQ-to-Entities-does-not-recognize-the-method-Sys

LastOrDefault vs FirstOrDefault [duplicate]

In this query:
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderBy(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.Last());
}
I had to switch it to this for it to work
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.FirstOrDefault());
}
I couldn't even use p.First(), to mirror the first query.
Why are there such basic limitations in what's otherwise such a robust ORM system?
That limitation comes down to the fact that eventually it has to translate that query to SQL and SQL has a SELECT TOP (in T-SQL) but not a SELECT BOTTOM (no such thing).
There is an easy way around it though, just order descending and then do a First(), which is what you did.
EDIT:
Other providers will possibly have different implementations of SELECT TOP 1, on Oracle it would probably be something more like WHERE ROWNUM = 1
EDIT:
Another less efficient alternative - I DO NOT recommend this! - is to call .ToList() on your data before .Last(), which will immediately execute the LINQ To Entities Expression that has been built up to that point, and then your .Last() will work, because at that point the .Last() is effectively executed in the context of a LINQ to Objects Expression instead. (And as you pointed out, it could bring back thousands of records and waste loads of CPU materialising objects that will never get used)
Again, I would not recommend doing this second, but it does help illustrate the difference between where and when the LINQ expression is executed.
Instead of Last(), Try this:
model.OrderByDescending(o => o.Id).FirstOrDefault();
Replace Last() by a Linq selector OrderByDescending(x => x.ID).Take(1).Single()
Something like that would be works if you prefert do it in Linq :
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters.OrderBy(p => p.ServerStatus.ServerDateTime).GroupBy(p => p.RawName).Select(p => p.OrderByDescending(x => x.Id).Take(1).Single());
}
Yet another way get last element without OrderByDescending and load all entities:
dbSet
.Where(f => f.Id == dbSet.Max(f2 => f2.Id))
.FirstOrDefault();
That's because LINQ to Entities (and databases in general) does not support all the LINQ methods (see here for details: http://msdn.microsoft.com/en-us/library/bb738550.aspx)
What you need here is to order your data in such a way that the "last" record becomes "first" and then you can use FirstOrDefault. Note that databasese usually don't have such concepts as "first" and "last", it's not like the most recently inserted record will be "last" in the table.
This method can solve your problem
db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();
Adding a single function AsEnumerable() before Select function worked for me.
Example:
return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName).AsEnumerable()
.Select(p => p.FirstOrDefault());
Ref:
https://www.codeproject.com/Questions/1005274/LINQ-to-Entities-does-not-recognize-the-method-Sys

Entity Framework - Select single record ordered by a property

I have a table and one of the properties of the table is TotalDue.I wish to first order it by TotalDue and then select the "top" record which in this case would be the record with the highest value.
homeVM.LastSaleAmount = (from i in salesService.GetSalesOrderHeaders()
.OrderByDescending(a => a.TotalDue).First();
This is what I've tried so far but I think .First() needs a parameter and I think I need a select as well but not really sure.
You can try with Take method, is like top, but in Linq world.
homeVM.LastSaleAmount = salesService.GetSalesOrderHeaders().OrderByDescending(a => a.TotalDue).Take(1);
https://msdn.microsoft.com/en-us/library/vstudio/bb503062%28v=vs.100%29.aspx
You're mixing method syntax and query syntax, and your use of query syntax isn't necessary and making this harder. Just remove it:
homeVM.LastSaleAmount = salesService.GetSalesOrderHeaders()
.OrderByDescending(a => a.TotalDue)
.Select(a => a.TotalDue)
.First();
You are trying to put an entire entity into LastSaleAmount. use .Select(a => a.TotalDue) like:
homeVM.LastSaleAmount = salesService.GetSalesOrderHeaders()
.OrderByDescending(a => a.TotalDue).Select(a => a.TotalDue).First();

Linq to SQL order by with Distinct

My Environment: ASP.net and C# in VS 2013 Express.
I have been through many similar SO articles trying to work this out. I am amateur with Linq to SQL queries and c# in general.
I'm trying to use Linq to SQL to get the top 5 most recent distinct values from a column, then add them to a list. My application is asp.net using c# and a .dbml file for data abstraction.
I've tried it many different ways. I either get non-distinct yet sorted list, or I get a distinct unsorted list. What I have so far is below
var Top5MFG = (from mfg in db.orders
where mfg.manufacturer.Length > 0 && mfg.customerid == "blahblahblahblahblah"<br />
select new {
manufacturer = mfg.manufacturer,
date = mfg.date_created
})
.Distinct()
.OrderByDescending(s => s.date);
I'm thinking my "Distinct" is looking at the "ID" column, and perhaps I need to tell it I want it to look at the "manufacturer" column, but I haven't worked out how / if it's possible to do that.
I could do this with ease by using a storedproc, but I'm really trying to do it with c# code directly if possible. This is my first post to SO, I hope I have put it together properly. Any help much appreciated.
Thanks
No the Distinct compares manufacturer and date pairs.If you want to get distinct records by manufacturer then I recommend DistinctBy method.It's in the MoreLINQ library.Since its a third library method it's not supported in linq to sql, you still can use it by fetching the records from DB and do the rest in memory
(from mfg in db.orders
where mfg.manufacturer.Length > 0 && mfg.customerid == "blahblahblahblahblah"
select new {
manufacturer = mfg.manufacturer,
date = mfg.date_created
})
.AsEnumerable()
.DistinctBy(x => x.manufacturer)
.OrderByDescending(s => s.date)
.Take(5);
I think you can use the GroupBy to do what you want.
var Top5MFG = db.orders
.Where (x => x.manufacturer.Length > 0 && x.customerid == "blahblahblahblahblah")
.GroupBy(mfg => mfg.manufacturer)
.Select(g => g.First())
.OrderByDescending(d => d.date_created );
.Take(5);
One way you can distinct by a certain field is to replace:
...
.Distinct()
...
with:
...
.GroupBy(x => x.manufacturer )
.Select(g => g.First())
...

Linq, unable to list only distinct results

I have three tables, car_type, car_manufacturer and car_model. When the user click on the particular vehicle type they want to browse, I'd like to show them a list of available manufacturers. The problem is the list of manufacturers is not distinct or unique. So if my db has three models from Mazda, Mazda will show up on the list 3 times. This is my controller:
public ActionResult Browse(string click_string)
{
var x = carDB.Models
.Include(b => b.Manufacturer)
.Include(a => a.VehicleType)
.Where(a => a.VehicleType.TypeName == click_string);
return View(x.ToList());
}
How can I write this to remove redundant listings? This is all new to me, so go easy on me.
You have to query for Manufacturers, not for Vehicles:
var x = carDB.Models.Where(a => a.VehicleType.TypeName == click_string)
.Select(a => a.Manufacturer)
.Distinct();
It usually works well to try and avoid Distinct altogether. You want manufacturers? Get manufacturers. And determine from there which ones you need: the ones that produce models that have click_string in their type name:
carDB.Manufacturers.Where(manufacturer => manufacturer.Models
.Any(model => model.VehicleType.TypeName == click_string))
You may want to include Models and/or VehicleType, that depends on what you want to show in the view.
First try doing a .Distinct() at the end of the query, if it does not work you might need to provide a custom comparer for the .Distinct()
You should be able to use .Distinct to return the distinct elements.
var x = carDB.Models
.Include(b => b.Manufacturer)
.Include(a => a.VehicleType)
.Where(a => a.VehicleType.TypeName == click_string)
.Distinct();
add distinct
var x = carDB.Models
.Include(b => b.Manufacturer)
.Include(a => a.VehicleType)
.Where(a => a.VehicleType.TypeName == click_string)
.Select(y => y)
.Distinct();
The .Select() might be a bit verbose but without trying it in my visual studio i put it in there for saftey

Categories