Reputation: 1587
We have a REST service to get a member given an ID. The SQL runs fast in isolation (5ms) but when run from Linq (using Entity framework 6), it runs very slow (230ms).
I don't this question is a duplicate of this, this or this as it feels Linq/EntityFramework related.
Here are the stats: The time taken for a client to call member get is about 360ms The time taken to execute the Linq query from the C# code is about 230ms The time taken to execute the SQL on the SQL Server is about 228ms
SQL tracing in production is of similar performance (141ms to execute the SQL on the SQL server) so the numbers feel real.
I tried running the Linq query six times in a row to see if perhaps the cost of establishing a connection from the datacontext was a problem. Each of those Linq queries took the same amount of time to run.
If I use the same datacontext to run the SQL directly (ie: what Linq generates), the runtime (measured from C#) drops from 230ms to 19ms.
Running the SQL directly on the server (SQL Server management Studio) takes about 5ms.
C# code (all in the same routine, using the same datacontext, no using block) produces these numbers:
Linq original query run =227ms
Raw SQL query: 19ms
Linq run 0=228
Linq run 1=227
Linq run 2=229
Linq run 3=229
Linq run 4=232
The Linq query looks like this:
DateTime start = DateTime.Now;
var memberDetail = await (from member in DataContext.Members.AsNoTracking()
join memberName in DataContext.MemberNames.AsNoTracking() on member.UID equals memberName.MemberID into nameOutput
from mn in nameOutput.DefaultIfEmpty()
join memberProperty in DataContext.Properties.AsNoTracking() on member.PropertyID equals memberProperty.UID
join membershipCycle in DataContext.MembershipCycleHistories.AsNoTracking() on member.UID equals membershipCycle.MemberID into cycleOutput
from co in cycleOutput.DefaultIfEmpty()
where member.ReferenceNumber.Equals(memberNumber) &&
memberProperty.ExternalID.Equals(property, StringComparison.InvariantCultureIgnoreCase)
select new
{
member.UID,
member.Created,
member.LastUpdated,
PropertyName = memberProperty.ExternalID,
member.ReferenceNumber,
member.Active,
member.IsAwaitingSync,
member.Class,
mn.FirstName,
mn.LastName,
mn.PreferredName,
MembershipCreditBalance = co.MembershipCredits,
member.DOB
}
).FirstOrDefaultAsync();
System.Diagnostics.Trace.WriteLine(String.Format("linq run original={0}", (DateTime.Now - start).TotalMilliseconds));
And the connection string is:
Data Source=SQLServer123;Initial Catalog=DB123;Persist Security Info=True;User ID=User123;Password=PWD123;MultipleActiveResultSets=True
Upvotes: 0
Views: 3803
Reputation: 1587
After a fair bit more investigation, I found the problem. The database uses varchars for all of its strings. When Linq passes its parameters to SQL Server, it passes them as Unicode. SQL Server looks at the data type and figures it cannot use the varchar indexes, so falls back to linear scans.
By changing my database from varchar to nvarchar, my query speed went from 258ms to 3ms.
Upvotes: 2