Linq to SQL order by with Distinct - c#

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())
...

Related

How to sort something in LINQ based on many dates?

Hello this is a LINQ Query but it doesn't sort properly because four different dates are involved.
var EventReportRemarks = (from i in _context.pm_main_repz
.Include(a => a.PM_Evt_Cat)
.Include(b => b.department)
.Include(c => c.employees)
.Include(d => d.provncs)
where i.department.DepartmentName == "Finance"
orderby i.English_seen_by_executive_on descending
orderby i.Brief_seen_by_executive_on descending
orderby i.French_seen_by_executive_on descending
orderby i.Russian_seen_by_executive_on descending
select i).ToList();
All i want is that it should somehow combine the four dates and sort them in group not one by one.
For Example, at the moment it sorts all English Reports based on the date that executive has seen it, then Brief Report and So on.
But i want that it should check which one is seen first and so on. For example if the first report which is seen is French, then Brief, then English then Russian, so it should sort it accordingly.
Is it Possible??
You need to have them all in one column. The approach I would do, assuming that the value of the respective cells is null, when you don't want them to show up in the order by:
var EventReportRemarks = (from i in _context.pm_main_repz
.Include(a => a.PM_Evt_Cat)
.Include(b => b.department)
.Include(c => c.employees)
.Include(d => d.provncs)
where i.department.DepartmentName == "Finance"
select new
{
Date =
(
i.English_seen_by_executive_on != null ? i.English_seen_by_executive_on :
i.Brief_seen_by_executive_on != null ? i.Brief_seen_by_executive_on :
i.French_seen_by_executive_on != null ? i.French_seen_by_executive_on :
i.Russian_seen_by_executive_on
)
}).ToList().OrderBy(a => a.Date);
In the select clause you could add more columns if you whish.
Reference taken from here.
Why not just use .Min() or .Max() on the dates and then .OrderBy() or .OrderByDescending() based on that?
Logic is creating a new Enumerable (here, an array) with the 4 dates for the current line, and calculate the Max/Min of the 4 dates: this results in getting the latest/earliest of the 4. Then order the records based on this value.
var EventReportRemarks = (from i in _context.pm_main_repz
.Include(a => a.PM_Evt_Cat)
.Include(b => b.department)
.Include(c => c.employees)
.Include(d => d.provncs)
where i.department.DepartmentName == "Finance"
select i)
.OrderBy(i => new[]{
i.English_seen_by_executive_on,
i.Brief_seen_by_executive_on,
i.French_seen_by_executive_on,
i.Russian_seen_by_executive_on
}.Max())
.ToList();
Your problem is not a problem if you use method syntax for your LINQ query instead of query syntax.
var EventReportRemarks = _context.pm_main_repz
.Where(rep => rep.Department.DepartmentName == "Finance")
.OrderByDescending(rep => rep.English_seen_by_executive_on)
.ThenByDescending(rep => rep.Brief_seen_by_executive_on)
.ThenByDescending(rep => rep.French_seen_by_executive_on descending)
.ThenByDescending(rep => resp.Russian_seen_by_executive_on descending)
.Select(rep => ...);
Optimization
One of the slower parts of a database query is the transport of selected data from the DBMS to your local process. Hence it is wise to limit the transported data to values you actually plan to use.
You transport way more data than you need to.
For example. Every pm_main_repz (my, you do love to use easy identifiers for your items, don't you?), every pm_main_repz has zero or more Employees. Every Employees belongs to exactly one pm_main_repz using a foreign key like pm_main_repzId.
If you use include to transport pm_main_repz 4 with his 1000 Employees every Employee will have a pm_main_repzId with value 4. You'll transport this value 1001 times, while 1 time would have been enough
Always use Select to select data from the database and Select only the properties you actually plan to use. Only use Include if you plan to update the fetched objects
Consider using a proper Select where you only select the items that you actually plan to use:
.Select(rep => new
{
// only Select the rep properties you actually plan to use:
Id = rep.Id,
Name = rep.Name,
...
Employees = rep.Employees.Select(employee => new
{
// again: select only the properties you plan to use
Id = employee.Id,
Name = employee.Name,
// not needed: foreign key to pm_main_repz
// pm_main_repzId = rep.pm_main_repzId,
})
.ToList(),
Department = new
{
Id = rep.Department,
...
}
// etc for pm_evt_cat and provencs
});

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();

Ordering not working in Entity Framework query

I'm currently battling a linq query for my application using Entity Framework (6.1.3)
The query is as follows:
var productPeriods = (from pp in ctx.ProductPeriods
where pp.IsActive && pp.Product.IsBuyBackForProduct == null && !pp.Product.ProductAddOns.Any() && pp.PowerRegionID == powerRegionId
select new
{
ProductPeriod = pp,
Price = pp.Prices
.OrderByDescending(x => x.Created)
.GroupBy(x => x.FirmID)
.Select(pr => pr.FirstOrDefault())
.OrderByDescending(x => x.ProductPrice)
.FirstOrDefault()
}).ToList();
The purpose of the query is to find the latest price from the prices collection of a product period, grouped by the firm ID and then select the best price of the latest prices from each firm.
This works perfectly in Linqpad, but the first OrderByDescending(x => x.Created) doesn't work when used in context of Entity Framework.
Does anyone knows why? And perhaps have a solution for it? :-)
Thanks in advance!
Update
Thanks for all replies. I've tried the following:
select new {
ProductPeriod = p,
Price = p.Prices.GroupBy(x => x.FirmID).Select(pr => pr.OrderByDescending(x => x.Created).ThenByDescending(x => x.ProductPrice).FirstOrDefault())
}
But it seems like ThenByDescending(x => x.ProductPrice) gets ignored as well. The prices are not sorted correctly in the output. They're output like this:
Price: 0,22940, Created: 06-03-2015 10:15:09,
Price: 0,23150, Created: 06-03-2015 10:05:48
Price: 0,20040, Created: 06-03-2015 09:24:24
Update 2 (solution for now)
I came to the solution that the initial query just returns the latest prices from each firm. There's currently three firms, so the performance should be alright.
Later in my code, where I'm actually using the latest and best price, I simply do an .OrderByDescending(x => x.ProductPrice).FirstOrDefault() and check if it's not null.
I.e:
var productPeriods = (from pp in ctx.ProductPeriods
where pp.IsActive && pp.Product.IsBuyBackForProduct == null && !pp.Product.ProductAddOns.Any() && pp.PowerRegionID == powerRegionId
select new
{
ProductPeriod = pp,
Prices = pp.Prices.GroupBy(x => x.FirmID).Select(pr => pr.OrderByDescending(x => x.Created).FirstOrDefault())
}).ToList();
Later in my code:
var bestPriceOfToday = period.Prices.OrderByDescending(x => x.ProductPrice).FirstOrDefault()
The problem is the commands you are using. OrderBy and OrderByDescending do NOT add additional order by statements to the resulting query but instead they CREATE the order by statement and eliminate all orderby statements that existed before.
In order to use multiple orderby's you need to do the following:
OrderBy or OrderByDescending
ThenBy or ThenByDescending
the ThenBy statements can be used 1 or more times they just add additional order statements to the resulting query.
According to yours update, omnit select and type:
select new {
ProductPeriod = p,
Price = p.Prices.GroupBy(x => x.FirmID)
.OrderByDescending(x =>x.Created).ThenByDescending(x=>x.ProductPrice).FirstOrDefault()
}
That select was useless and could be the cause of problem

C# LINQ Lambda query with select, Where, Count and Distinct

I am brand new to linq and i need help with a query. I have 3 columns in a datatable. I need to get a count of unique values from col3, where col1 and Col2 contain certain values. here is the last piece of code i tried, but it is not working. can someone please help me with this?
thanks
AD = dt.AsEnumerable()
.Where(x => x.Field<string>("Col1").Equals("Value1")
|| x.Field<string>("Col2").Equals("Value2"))
.Select(s => s.Field<string>("Col3")
.Distinct().Count());
I'm missing a closing parenthesis at .Select(s => s.Field<string>("Col3"), try this:
AD = dt.AsEnumerable()
.Where(x => x.Field<string>("Col1").Equals("Value1")
|| x.Field<string>("Col2").Equals("Value2"))
.Select(s => s.Field<string>("Col3")) // <-- add this
.Distinct().Count(); // <-- remove this
var q1 = (from persons in _db.Table<Person>()
where persons.firstName==fname && persons.lastName==lname group
persons.age by persons.age ).Count();
I 've tested this on some "test" cases using sqlite-net library - and it seems to work.
Although there is already a solution - they are using different styles , so I decided to post it there to

C# EF Deep Lambda Distinct Count Query

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.

Categories