Performance Question between two Linq Queries in LinqPad and in Practice - linq

I have a query:
(from sr in ctx.STARS_Route
where sr.STARS_RouteStopDestination.Any(i => i.IsWorkingSet == true && i.STARS_DistrictRoute.DistrictId == districtId) == true
select sr.DistrictRouteNumber).Distinct();
In LinqPad the query was running at roughly 0.3 seconds. There are roughly 800K records in the STARS_RouteStopDestination table, but the average return is about 30-90 records.
In practice, the method returning the results from this query was taking 4+ seconds! It didn't make any sense.
The only thing I could think of is that the .Any clause was taking a lot of time, but LinqPad said the query was fast. I set up a test (please forgive the names):
using (STARSEntities ctx = new STARSEntities())
{
var Original = (from sr in ctx.STARS_Route
where sr.STARS_RouteStopDestination.Any(i => i.IsWorkingSet == true && i.STARS_DistrictRoute.DistrictId == districtId) == true
select sr.DistrictRouteNumber).Distinct();
var Entity = (from rsd in ctx.STARS_RouteStopDestination
where rsd.STARS_DistrictRoute.DistrictId == districtId
&& rsd.IsWorkingSet == true
select rsd.STARS_Route.DistrictRouteNumber).Distinct();
DateTime startOriginal = DateTime.Now;
routes = Original.ToList();
Debug.WriteLine("Original took: " + (DateTime.Now - startOriginal).ToString());
DateTime startEtity = DateTime.Now;
routes = Entity.ToList();
Debug.WriteLine("Entity took: " + (DateTime.Now - startEtity).ToString());
}
The output blew my mind:
Original took: 00:00:04.0270000
Entity took: 00:00:00.0200000
Why does the query with the .Any clause take so much longer to run, and why would LinqPad say that the Original query runs slightly faster than the Entity query against the same dataset?

You are simply acting upon two different objects that has two different strategies of obtaining the result. LINQPad generates a LINQ to SQL model and injects your code into a method with this model in scope.
Your STARSEntities class is of a different type and probably has a completely different interaction with the database.
LINQPad has its own view that displays the SQL being run. You should compare it to the SQL being executed by the EF model. This is displayed in the SQL Server Profiler when you execute.

Related

How to write good LINQ query for this?

I have a LINQ Query. When i run the query it takes so much of time to perform the operation. How can i modify the query to perform good. here is the LINQ query.
Query
var model = (from items in Db.Items
where items.ItemNo == DD.ItemNumber
select new ViewModel()
{
INo = items.ItemNo,
BTags = (from asd in Db.BibContents where asd.BibId == items.BibId && asd.TagNo == "245" && asd.Sfld == "a" select asd.Value).FirstOrDefault(),
Sid = (from stat in Db.ItemStatus1 where stat.Id == items.StatusId select stat.Description).FirstOrDefault(),
Option = DD.Option
}).ToList();
You should analyze the query in SQL Profiler and Query Analyzer. The problem is probably indexes on your tables.
First of all, while your query is always working with database objects, you'd better do all your job in SQL and bring only the results to LINQ.
This will probably improve your performance.
Make sure EF is not causing a Select N+1 problem.
http://blogs.microsoft.co.il/blogs/gilf/archive/2010/08/18/select-n-1-problem-how-to-decrease-your-orm-performance.aspx

Printing Month Name in Linq

Is there any Inbuilt function in Linq to Print the month Name while working with LINQPAD?
I want to print the month name in the following Scenario
var query = from e in Employees
let month=e.BirthDate.GetValueOrDefault()
let birthmonth=month.ToString("MMMM")
select birthmonth;
query.Dump();
When I run this it is throwing NotSupportedException.
how to print the month name in Linq to Sql?
Rather than using ToString, try string.Format. Something like:
var query = (from e in Employees
let month = e.BirthDate.GetValueOrDefault()
let birthmonth = string.Format("{0:MMMM}", month)
select birthmonth);
query.Dump();
This seems to work from my local testing, although it is not included as part of the SQL query.
Do it in two steps, one to get the months from the database, then another using Linq-To-Objects to perform the formatting.
var birthDates = Employees.Select(e => e.BirthDate).ToList();
var query = birthDates.Select(d => d != null ? d.ToString("MMMM") : "Null");
query.Dump();
Whatever ORM you are using can't convert the string formatting part of you query into SQL that works on your database. So, doing it in two steps and using ToList to evaluate inbetween overcomes that problem.

Wait for DomainContext.Load<t> from an entityquery with joins to complete (returning new type via 'select new')

My app consolidates data from other DBs for reporting purposes. We can't link the databases, so all the data processing has to be done in code - this is fine as we want to allow manual validation during the imports.
Certain users will be able to start an update through the Silverlight 4 front end.
I have 3 tables in database x that are fed from one EF4 Model (ModelX). I want to join those tables together, select specific columns and return the result as a new entity that exists in a different EF4 Model (ModelY). I'm using this query:
var myQuery = from i in DBx.table1 from it in DBx.table2 from h in DBx.table3 where (i.id==it.id && h.otherid == i.otherid) select new ModelYServer {Name = i.name,Thing = it.thing, Stuff = h.stuff};
The bit i'm stuck on, is how to execute that query, and wait until the Asynchronous call has completed. Normally, i'd use:
DomainContext.Load<T>(myQuery).Completed += (sender,args) =>
{List<T> myList = ((LoadOperation<T>)sender.Entities.ToList();};
but I can't pass myQuery (an IEnumerable) into the DomainContext.Load() as that expects an EntityQuery. The dataset is very large, and is taking up to 30 seconds to return, so I definitely need to wait before continuing.
So can anyone tell me how I can wait for the IEnumerable query to complete, or suggest a better way of doing this (there very likely is one).
Thanks
Mick
One simple way is just to force it to evaluate by calling ToList:
var query = from i in DBx.table1
join it in DBx.table2 on i.id equals it.id
join h in DBx.table3 on i.otherid equals h.otherid
select new ModelYServer {
Name = i.name,
Thing = it.thing,
Stuff = h.stuff
};
// This will block until the results have been fetched
var results = query.ToList();
// Now use results...
(I've changed your where clause into joins on the earlier tables, as that's what you were effectively doing and this is more idiomatic, IMO.)

SQLiteException and SQLite error near "(": syntax error with Subsonic ActiveRecord

I ran into an interesting error with the following LiNQ query using LiNQPad and when using Subsonic 3.0.x w/ActiveRecord within my project and wanted to share the error and resolution for anyone else who runs into it.
The linq statement below is meant to group entries in the tblSystemsValues collection into their appropriate system and then extract the system with the highest ID.
from ksf in KeySafetyFunction where ksf.Unit == 2 && ksf.Condition_ID == 1
join sys in tblSystems on ksf.ID equals sys.KeySafetyFunction
join xval in (from t in tblSystemsValues
group t by t.tblSystems_ID into groupedT
select new
{
sysId = groupedT.Key,
MaxID = groupedT.Max(g=>g.ID),
MaxText = groupedT.First(gt2 => gt2.ID ==
groupedT.Max(g=>g.ID)).TextValue,
MaxChecked = groupedT.First(gt2 => gt2.ID ==
groupedT.Max(g=>g.ID)).Checked
}) on sys.ID equals xval.sysId
select new {KSFDesc=ksf.Description, sys.Description, xval.MaxText, xval.MaxChecked}
On its own, the subquery for grouping into groupedT works perfectly and the query to match up KeySafetyFunctions with their System in tblSystems also works perfectly on its own.
However, when trying to run the completed query in linqpad or within my project I kept running into a SQLiteException SQLite Error Near "("
First I tried splitting the queries up within my project because I knew that I could just run a foreach loop over the results if necessary. However, I continued to receive the same exception!
I eventually separated the query into three separate parts before I realized that it was the lazy execution of the queries that was killing me. It then became clear that adding the .ToList() specifier after the myProtectedSystem query below was the key to avoiding the lazy execution after combining and optimizing the query and being able to get my results despite the problems I encountered with the SQLite driver.
// determine the max Text/Checked values for each system in tblSystemsValue
var myProtectedValue = from t in tblSystemsValue.All()
group t by t.tblSystems_ID into groupedT
select new {
sysId = groupedT.Key,
MaxID = groupedT.Max(g => g.ID),
MaxText = groupedT.First(gt2 => gt2.ID ==groupedT.Max(g => g.ID)).TextValue,
MaxChecked = groupedT.First(gt2 => gt2.ID ==groupedT.Max(g => g.ID)).Checked};
// get the system description information and filter by Unit/Condition ID
var myProtectedSystem = (from ksf in KeySafetyFunction.All()
where ksf.Unit == 2 && ksf.Condition_ID == 1
join sys in tblSystem.All() on ksf.ID equals sys.KeySafetyFunction
select new {KSFDesc = ksf.Description, sys.Description, sys.ID}).ToList();
// finally join everything together AFTER forcing execution with .ToList()
var joined = from protectedSys in myProtectedSystem
join protectedVal in myProtectedValue on protectedSys.ID equals protectedVal.sysId
select new {protectedSys.KSFDesc, protectedSys.Description, protectedVal.MaxChecked, protectedVal.MaxText};
// print the gratifying debug results
foreach(var protectedItem in joined)
{
System.Diagnostics.Debug.WriteLine(protectedItem.Description + ", " + protectedItem.KSFDesc + ", " + protectedItem.MaxText + ", " + protectedItem.MaxChecked);
}
Avoid lazy evaluation by forcing an early execution with .ToList() on one of the components of the final query. The results will go into memory so try to make sure you are choosing a small set of data and don't force an unbounded query or gigantic query into a list.

How to write dynamic Linq2Sql compiled queries?

I'm having performance issues with Linq2Sql compared to raw ADO.NET which has led me down the path of compiled queries. I have got this far so far
public static readonly Func<MyDataContext, WebServices.Search.Parameters, IQueryable<image>>
Compiled_SelectImagesLinq =
CompiledQuery.Compile<MyDataContext, WebServices.Search.Parameters, IQueryable<image>>(
(dc, parameters) => from i in dc.images
join l in dc.links on i.image_id equals l.image_id
join r in dc.resolutions on i.image_id equals r.image_id
where i.image_enabled == true && i.image_rating >= parameters.MinRating
&& i.image_rating <= parameters.MaxRating
select i
);
However I can't figure out how to add the extra optional parameters to the query as I currently do
if (parameters.Country != null)
{
query = query.Where(x => x.image_country_id == parameters.Country);
}
if (parameters.ComponentId != null)
{
query = query.Where(x => x.links.Any(l => l.link_component_id == parameters.ComponentId));
}
etc, etc
I tried writing another function which does
var query = Compiled_SelectImagesLinq(parameters);
and then adding the extra parameters to the query and returning
return query.Distinct().Take(parameters.Results);
Bit this doesn't seem right and returns no results
Have a look at this article. It may not do what you need (especially since you are compiling your queries), but anytime someone mentions Dynamic and Linq in the same sentence, I refer them to this article:
Dynamic LINQ: Using the LINQ Dynamic Query Library
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
You'd have to benchmark your specific query, but often queries must be used 10-20 times before compiled query performance improvements equal the overhead. Also, how are you adding parameters to the where clause?
Additionally, dynamic compiled queries seems a bit of a mismatch. The Dynamic LINQ query library will do what you need but I don't think you'll get the compiled query performance improvement you want.

Resources