How to improve Linq-To-Sql code - c#

Using the StackExchange.Profiling.MiniProfiler class to profile an ASP.NET MVC application with Linq-To-Sql as ORM.
I'm trying to reduce one action to one SQL, so that I don't have any duplicates anymore.
So I changed my linq-to-sql code accordingly, but it didn't have any positive effect on the speed.
Then I checked the time that is needed for the SQL.
This shows the MiniProfiler:
When I fire up the exact same SQL in Management Studio it is super fast:
Here is the code:
from t in type
let tDoc = (from d in context.documents
where d.Key == t.No
&& d.RType == (int)RType.Art
&& d.AType == (int)AType.Doc
select d).FirstOrDefault(d => d.UseForThumb)
select new Time
{
Id = t.Id,
//... more simple mappings here
// then a complex one:
DocsCount = context.documents.Count(d =>
(d.Key == t.Id.ToString()
&& d.RType == (int)RType.Type
&& d.AType == (int)AType.Doc)
||
(d.Key == t.No
&& d.RType == (int)RType.Art
&& d.AType == (int)AType.Doc)),
// and another one
ThumbId = (tDoc != null && tDoc.FRKey.HasValue) ? tDoc.FRKey.Value : 0
};
What can be the reason for the huge difference? - Edit: There is no difference, I just misenterpreted SSMS :(
Anyway, my problem persits. What could I change to make it faster?
I read sometime that the mapping from Linq-To-Sql has a performance problem. Is there a way to workaround this?

I did some trial and error and changed the Linq-To-Sql code to this:
from t in types
let docs = context.documents.Where(d => (d.RKey == t.Id.ToString()
&& d.RType == (int)RType.Type
&& d.AType == (int)AType.Doc)
||
(d.RKey == t.No
&& d.RType == (int)RType.Art
&& d.AType == (int)AType.Doc))
let tDoc = docs.FirstOrDefault(d => d.RType == (int)RType.Art && d.UseForThumb)
let docsCount = docs.Count()
select new Time
{
Id = t.Id,
//... more simple mappings here
DocsCount = docsCount,
ThumbId = (tDoc != null && tDoc.FRKey.HasValue) ? tDoc.FRKey.Value : 0,
}
This made the query much, much faster.

Related

How to exclude from query's result in linq?

In the first picture I have the result of first query, the highlighted part indicates the rows that would be excluded by applying the filter on the second query, in the second I have the result of query select * from exlusion_table
I have to make a change to the first query to have it exclude the items retrieved from the second query
the first query:
var u = from a in cx.VW_LIST
where (a.PRJ == codPrj) && (a.DATE > date_ || a.DATE == null || date_ == null)
&& (x.Contains(a.CODE) || x.Count() == 0)
select a)
the second query:
var y = from esc in cx.EXCLUSION select esc
The first query should be modified to exclude all the rows that have the value fcode = the fcode of the second query (in the case in which the fscode of the second query = null) or that (fcode = fcode of the second query && fscode = fscode of the second query )
You can use Any(). ie:
var u = from a in cx.VW_LIST
where (a.PRJ == codPrj)
&& (a.DATE > date_ || a.DATE == null || date_ == null)
&& (x.Contains(a.CODE) || x.Count() == 0)
&& (!cx.EXCLUSION.Any( y => x.fscode == y.fscode && x.fcode == y.fcode ))
select a)
There are two approaches to this, one is to use ! and ANY() to filter out records found within the other list, this will compile into WHERE NOT EXISTS(_exclusion_) filter expression
var excluded = cx.EXCLUSION.AsQueryable();
var query = from vw in cx.VW_LIST
where vw.PRJ == codPrj
where vw.DATE == null || date_ == null || vw.DATE > date_
where !x.Any() || x.Contains(vw.CODE)
where !excluded.Any(esc => vw.fcode == esc.fcode
&& (esc.fscode == null || vw.fscode == esc.fscode))
select vw;
var results = query.ToList();
The tricky element is the null for fscode in the excluded table, that needs to act as wildcard match, or negate the fscode comparison.
It is not necessary to split the excluded query out into it's own query, we could have referenced to the cx.EXCLUSION table directly and it would have exactly the same effect, this shows you an encapsulation technique for building the LINQ query in a way that you could easily increase the complexity of the exclusion lookup without creating a mess of your overall query.
You may also find need to conditionally build the query, this is where fluent syntax provides a more modular approach:
bool filterExcludedRecords = true;
...
var excluded = cx.EXCLUSION.AsQueryable();
var query = cx.VW_LIST.Where(vw => vw.PRJ == codPrj)
.Where(vw => vw.DATE == null || date_ == null || vw.DATE > date_)
.Where(vw => !x.Any() || x.Contains(vw.CODE));
if(filterExcludedRecords)
query = query.Where(vw => !excluded.Any(esc => vw.fcode == esc.fcode
&& (esc.fscode == null || vw.fscode == esc.fscode)));
var results = query.ToList();
OUTER JOIN
Another method is to use a LFET OUTER JOIN where the exclusion match is not found:
var excluded = cx.EXCLUSION.AsQueryable();
var query = from vw in cx.VW_LIST
where vw.PRJ == codPrj
where vw.DATE == null || date_ == null || vw.DATE > date_
where !x.Any() || x.Contains(vw.CODE)
from esc in excluded.Where(e => vw.fcode == e.fcode
&& (e.fscode == null || vw.fscode == e.fscode))
.DefaultIfEmpty()
where esc.fscode == null
select vw;
var results = query.ToList();
The WHERE NOT EXISTS is often superior in terms of performance, OUTER JOIN may provide better response in an un-optimised table or when the number of rows in the exclusion list is significantly small and the number of rows in the main table is very large.
I include this query option for completeness, it is not well known that you can create simple outer joins by adding a new from clause to the query.

linq query with or and operator together

I need to run multiple conditions in linq query with or, and operator. First I need to compare with three conditions, which are separated by || operator, once one of the conditions is true I also need to see if check is true which I using && operator but I got all the result
so for example from following query, if ModuleLead == ContextSession.StaffID then record must have ClinicalSupervisorCheck == true
var query_b = (from b in activeAssessmentWithRemidiation
where b.AssessorID == ContextSession.StaffID
|| b.ModuleLead == ContextSession.StaffID
&& b.ClinicalSupervisorCheck == true
|| b.SeniorStaffID == ContextSession.StaffID
&& b.ModuleLeadCheck==true
select b).ToList();
the above query is running on following table records
It could be worth wrapping the AND conditions in parathensis, for example:
var query_b = (from b in activeAssessmentWithRemidiation
where b.AssessorID == ContextSession.StaffID ||
(b.ModuleLead == ContextSession.StaffID && b.ClinicalSupervisorCheck == true) ||
(b.SeniorStaffID == ContextSession.StaffID && b.ModuleLeadCheck==true)
select b).ToList();
Use parenthesis for that. Remember that operator && will be tested before || operators if you don't use parenthesis.
proper use of ( and )
var query_b = (from b in activeAssessmentWithRemidiation
where (((b.AssessorID == ContextSession.StaffID
|| b.ModuleLead == ContextSession.StaffID )
&& b.ClinicalSupervisorCheck == true)
||
(b.SeniorStaffID == ContextSession.StaffID
&& b.ModuleLeadCheck==true))
select b).ToList();

How to select both boolean values or one

I have a ternary operator as below for a LINQ query as shown
var sub = (SubordinationType == 1) ? (true&false) : false;
var query = from vw in dbContext.vw
where (vw.office == FieldOffice && vw.SubAgreement == sub)
select vw;
Here SubAgreement is a bit field in database I need to select both true and false(0,1) or false(0) based on the ternery how do i achieve this?
Any quick suggestions please.
I think I get your question. Your logic is:
If Subordination is 1, SubAgreement doesn't matter (true or false)
If Subordination is not 1, SubAgreement should be false
hence add another condition:
//..
where (vw.office == FieldOffice && (Subordination == 1 || !vw.SubAgreement))
Try something like:
var query = from vw in dbContext.vw
where vw.office == FieldOffice
select vw;
if (SubordinationType != 1)
{
query = query.Where(vw => vw.SubAgreement == false);
}
In LINQ it's very easy to add new where clauses that are in && with the other clauses (note that it's only easy to add if you want them to be in &&, the || case is much more complex! :-) )
You can make a condition that is true when SubordinationType is 1 or when SubAgreement is false:
var query = from vw in dbContext.vw
where (vw.office == FieldOffice && (SubordinationType == 1 || vw.SubAgreement == false))
select vw;

Optimizing A LINQ To Objects Query

I'm trying to optimize the below LINQ query to improve it's speed performance. The number of objects it's searching against could be in the tens of thousands.
var lQuery = from o in oEvents
where (o.oSalesEvent != null && o.oSalesEvent.OccurDate < oCalcMgr.OccurDate && (
(oCalcMgr.InclTransTypes == Definitions.TransactionTypes.SalesAll) ?
(o.oSalesEvent.EventStateID == ApprovedID || o.oSalesEvent.EventStateID == PendingID) :
o.oSalesEvent.EventStateID == ApprovedID)) &&
((oCalcMgr.InclTransTypes == Definitions.TransactionTypes.SalesAll) ?
(o.oSalesMan.oEmployment.EventStateID == ApprovedID || o.oSalesMan.oEmployment.EventStateID == PendingID) :
o.oSalesMan.oEmployment.EventStateID == ApprovedID)
select new { SaleAmount = o.SaleAmount.GetValueOrDefault(), CompanyID = o.oSalesEvent.CompanyID };
The query basically says, give me the sales amounts and company ids from all sale events that occurred prior to a certain date. The sale event's status and the salesman's employment status should either always be "approved" or they can be also "pending" if specified.
As you can see there's a date comparison and a couple of integer comparisons. Which integer comparison used is based on whether or not a property matches a certain Enum value.
I have some ideas of my own on ways to go about the optimization, but I want to hear others thoughts, who might have more insight into how LINQ would translate this query behind the scenes.
Thanks
It seems to me that your biggest challenge is that you're doing multiple criteria checks in your Linq statement that will take alot of time.
What about creating a new property in oEvents - Say "IsEligable" and set it's value within the Set statements of your other variables (much faster than constant re-querying of each variable in the Linq).
Then, by the time you get to this part of your code, you could update your Linq to be something along the lines of:
var lQuery = from o in oEvents
where (o.oSalesEvent != null && o.oSalesEvent.OccurDate < oCalcMgr.OccurDate && o.IsEligable == True)
select new { SaleAmount = o.SaleAmount.GetValueOrDefault(), CompanyID = o.oSalesEvent.CompanyID };
... I'm guessing that would speed up the execution, but just a thought...
This is as much to improve readability as to possibly speed it up, but give this a try:
var lQueryTemp = from o in oEvents
where (o.oSalesEvent != null && o.oSalesEvent.OccurDate < oCalcMgr.OccurDate)
if (oCalcMgr.InclTransTypes == Definitions.TransactionTypes.SalesAll)
{
lQueryTemp = from o in lQueryTemp
where (o.oSalesEvent.EventStateID == ApprovedID || o.oSalesEvent.EventStateID == PendingID) &&
(o.oSalesMan.oEmployment.EventStateID == ApprovedID || o.oSalesMan.oEmployment.EventStateID == PendingID);
}
else
{
lQueryTemp = from o in lQueryTemp
where (o.oSalesEvent.EventStateID == ApprovedID && o.oSalesMan.oEmployment.EventStateID == ApprovedID);
}
var lQuery = from o in lQueryTemp
select new { SaleAmount = o.SaleAmount.GetValueOrDefault(), CompanyID = o.oSalesEvent.CompanyID };
This might speed it up by pulling out both checks of oCalcMgr.InclTransTypes, which is effectively a constant for purposes of this query.

Linq conditional query on same table/object

Hi I'm having a problem with getting a conditional query to work. I want all projects where project.Parent either is null or if it has a parent then it shouldn't be voided or closed.
My example will NOT bring back any projects where project.Parent == null.
We are using linq-to-nhibernate
var projects = (from project in this.Session.Query<Project>()
where project.IsClosed == false
&& project.IsVoided == false
&& (project.Parent == null
|| (project.Parent.IsVoided == false
&& project.Parent.IsClosed == false))
select project).ToList();
That query won't work because inner joins are generated for the Parent property.
The easiest workaround is doing two queries and joining them client-side:
var projects = (from project in this.Session.Query<Project>()
where project.IsClosed == false
&& project.IsVoided == false
&& project.Parent == null
select project)
.AsEnumerable()
.Concat(
(from project in this.Session.Query<Project>()
where project.IsClosed == false
&& project.IsVoided == false
&& project.Parent.IsVoided == false
&& project.Parent.IsClosed == false
select project))
.ToList();
I'd suggest to fetch all project and to check what happens to projects that should be null. Without having any example data etc. I have to guess what is causing the problem. I'd say the project parents are initialized with some empty state.
Doing the join client-side is not required:
var projects = (from project in this.Session.Query<Project>()
where project.Parent == null || (project.IsClosed == false
&& project.IsVoided == false)
&& (project.Parent == null
|| (project.Parent.IsVoided == false
&& project.Parent.IsClosed == false))
select project).ToList();

Categories

Resources