LINQ TO SQL V 1.0 Hickups

While reviewing LINQ TO SQL with a colleague recently I discovered yet another "hickup" in v 1.0 of LINQ TO SQL. The first couple can be found here.

The DBA's are going to love this. I believe that once they see this bit of information they will have a little more firepower against their cutting edge developers using LINQ TO SQL (Me included).

So what is this latest hickup? My colleague wanted to know how the code generated by LINQ TO SQL is executed against the server. I showed him in profiler that it uses parameterized queries (a good thing) and of course executes the stored procedures correctly the same way. He was satisfied with this.

Then he asked a question that I didn't even think of (most developers don't); what do the caching plans look like? I had no idea and had to have him remind me of the syntax. For those who don't know its:

select sql, * from master..syscacheobjects

To make it easier you can put a wildcard search (where sql like '%usp_individual_searchbylastname%') to only return the cache plans for your specific proc.

I have a simple form that has a "Last Name" text box on it. When I click the search button LINQ TO SQL executes my stored procedure "usp_individual_searchbylastname".

When I ran that search my cache plan looked pretty good.  Well they did the first time I ran the proc through LINQ TO SQL. The second, third, fourth, etc times I executed my procs through LINQ TO SQL it looked worse and worse.  Ultimately I searched 5 different last names and unfortunately got 5 different cache plans. Here is what I found in the syscacheobjects SQL column when I ran the SQL statement above:

(@p0 nvarchar(16),@RETURN_VALUE int output)
EXEC @RETURN_VALUE = [dbo].[usp_individual_searchbylastname] @lastname = @p0

(@p0 nvarchar(4),@RETURN_VALUE int output)
EXEC @RETURN_VALUE = [dbo].[usp_individual_searchbylastname] @lastname = @p0

(@p0 nvarchar(8),@RETURN_VALUE int output)
EXEC @RETURN_VALUE = [dbo].[usp_individual_searchbylastname] @lastname = @p0

(@p0 nvarchar(9),@RETURN_VALUE int output)
EXEC @RETURN_VALUE = [dbo].[usp_individual_searchbylastname] @lastname = @p0

(@p0 nvarchar(6),@RETURN_VALUE int output)
EXEC @RETURN_VALUE = [dbo].[usp_individual_searchbylastname] @lastname = @p0

Notice anything weird about the first parameter p0 (it's defined as @lastname nvarchar(50) in the stored procedure)? The size of the parameter is being determined NOT by the actual parameter definition, rather it's being determined by the length of the string value being passed to the LINQ TO SQL generated function. So instead of getting 1 good cache plan I'm getting five. What's worse is that this has the potential to have 50 different plans.

If that wasn't bad enough the same thing happened to my insert proc as well. I have a form that allows me to insert a record into the individual table. When I save the record the proc "usp_individual_insert" is executed. I inserted two records and like before got two different plans.

(@p0 nvarchar(2),@p1 nvarchar(4),@p2 nvarchar(1),@p3 nvarchar(5),@p4 nvarchar(4000),@p5 nvarchar(14),@p6 varchar(4),@p7 varchar(4),@p8 ntext,@p9 int,@p10 int output,@RETURN_VALUE int output)EXEC @RETURN_VALUE = [dbo].[usp_individual_insert] @Salutation = @p0, @FirstName = @p1, @MiddleName = @p2, @LastName = @p3, @Suffix = @p4, @EmailAddress = @p5, @PasswordHash = @p6, @PasswordSalt = @p7, @Demographics = @p8, @customerid = @p9, @individualid = @p10 OUTPUT

(@p0 nvarchar(2),@p1 nvarchar(5),@p2 nvarchar(7),@p3 nvarchar(5),@p4 nvarchar(4000),@p5 nvarchar(26),@p6 varchar(4),@p7 varchar(4),@p8 ntext,@p9 int,@p10 int output,@RETURN_VALUE int output)EXEC @RETURN_VALUE = [dbo].[usp_individual_insert] @Salutation = @p0, @FirstName = @p1, @MiddleName = @p2, @LastName = @p3, @Suffix = @p4, @EmailAddress = @p5, @PasswordHash = @p6, @PasswordSalt = @p7, @Demographics = @p8, @customerid = @p9, @individualid = @p10 OUTPUT

This has the potential to have just an ungodly number of cache plans. There are 8 different nvarchar or varchar parameters defined on this proc with lengths between 8 and 50. You do the math.

At this point if they don't fix this problem (and the stupid refresh problem) I'm going to say that I may have to substitute LINQ TO SQL with something else. My current project uses a facade class that interfaces with the LINQ TO SQL Datacontext class so it won't be that difficult to swap it out with something like subsonic. Although one of the guys who created subsonic is on the LINQ TO SQL team.

I'm hoping that the LINQ TO SQL team fixes these issues and does it soon.

UPDATE: SubSonic doesn't parameterize their queries therefore they do not generate syscacheobject records.
UPDATE #2: Somebody pointed out that "hickup" is mispelled. I told them it was on purpose to further empasize the problems I found. I guess it didn't come across as obvious. Next time I'll use cRaZy CaSe. Ok... Maybe not.


Technorati Tags: ,,.NET 3.5

posted @ Tuesday, February 19, 2008 6:51 PM

Print

Comments on this entry:

# re: LINQ TO SQL V 1.0 Hickups

Left by Hilton Giesenow at 2/26/2008 5:36 AM
Gravatar
Wow, this sounds like a serious problem. Is the same true if you don't use sprocs, I wonder? It probably is, considering what it's doing :-(. Can you check it out?

# re: LINQ TO SQL V 1.0 Hickups

Left by Hilton Giesenow at 2/26/2008 5:36 AM
Gravatar
Wow, this sounds like a serious problem. Is the same true if you don't use sprocs, I wonder? It probably is, considering what it's doing :-(. Can you check it out?

# re: LINQ TO SQL V 1.0 Hickups

Left by Chris at 2/26/2008 9:24 AM
Gravatar
Hilton,

It's the same when you don't use procs. It parameterizes all queries. Like I said, it's great they are parameterizing the queries because that will create the cache plan. However until they fix the issue it will create a huge number of plans.

Thanks for the comments!

Chris

# re: LINQ TO SQL V 1.0 Hickups

Left by Jon Kruger at 3/6/2008 9:28 PM
Gravatar
This obviously is an issue as you've mentioned, but how much of an issue is it? I'm not a database expert, but has anyone run any timing tests to see how much of a problem this is?

I'm not denying that it's not a problem to some extent, but I'm skeptical that this problem is reason enough to throw out LINQ to SQL altogether.

I'm using LINQ to SQL on a pretty big project against a 160GB database without any serious issues. That certainly doesn't mean that maybe they could do something to make it faster, like you mentioned.

I will say that the time it took us to develop this application was much less than when I was using Nhibernate. That has to count for something.

# re: LINQ TO SQL V 1.0 Hickups

Left by Chris at 3/6/2008 10:29 PM
Gravatar
To be honest I haven't thrown it out either.

It will cause your cache to grow like crazy which will probably have your DBA up in arms if performance problems start to occur.

The learning curve and time to development for LINQ TO SQL is, in my opinion, a lot better compared to NHibernaate. I like LTS because it's relatively easy to use.

However it does have it's "features" that I hope a service pack will fix in the near future.

Thank you for your comments Jon! Nice to see you over here!

# re: LINQ TO SQL V 1.0 Hickups

Left by Benjamin Eidelman at 3/14/2008 8:18 AM
Gravatar
This is really a major hikcup! :)... I suposse we should use string.PadRight(50) on the property before submitting! :D.

This is one of the reason I think is very important to distinguis Linq from Linq to SQL.

While the first it's the wonder feature in Net 3.5, that can only be criticized by language purists :), The second (LTS), IMHO seems to be a feature, (that couldnt be absent) and got rushed to get in this release.

# re: LINQ TO SQL V 1.0 Hickups

Left by Pill Store at 11/25/2008 3:50 PM
Gravatar
Very good job. Add your blog to bookmarks.

# re: LINQ TO SQL V 1.0 Hickups

Left by Chris Leonard at 5/21/2009 4:41 PM
Gravatar
Someone asked if anyone had done any measurement of the performance impact of this. The problem would not manifest itself as a performance issue right away. The problem is the size of the plan cache, and all of the extra optimizations and compilations. Parsing, optimizing, and compiling can be quite costly in a cumulative sense even though they seem trivial taken individually. It also has the potential to force useful plans that were difficult to compile to age out of cache while the system wastes time compiling zillions of potentially trivial plans that should really only occupy one slot in the plan cache.

So, bottom line, I have not tested the impact of this particular issue, but this is something we look for in our plan cache from time to time because the problem can be produced in other ways. We once had a stored procedure that was using EXEC (in such a way that it always got a dynamic compile). These calls were happening at least tens of thousands of times per hour. The pressure on the plan cache was palpable, and once we corrected the issue we did see system-wide performance improvement.

So I'd guess that this is an issue for high-load systems. Like all things in SQL Server though, the product is good enough that if you are small or under light load, you can mess things up really fairly badly before you see a performance problem - if that's the way you want to roll.

Hope this is helpful,
Chris

# re: LINQ TO SQL V 1.0 Hickups

Left by Custom Blog Design at 12/30/2009 3:54 PM
Gravatar
I must admit, great tips!

# re: LINQ TO SQL V 1.0 Hickups

Left by Rapid Share at 1/18/2010 6:29 AM
Gravatar
I have implemented a dynamic fetching strategy to work hand in hand with my Repository to achieve this. It also allows me to drop in a new one later if the strategy is not suiting my needs.

# re: LINQ TO SQL V 1.0 Hickups

Left by Casino gewinnen at 2/13/2010 1:04 AM
Gravatar
This is a tricky one because Microsoft can't assume you haven't made custom changes on the designer. I'm enjoying working with LINQ To SQL, but I'm not sure I'm all that happy with the designer approach at the moment. One way to get around this, by the way, is to drag another copy of the table onto the designer, copy-paste the changes over to the original entity, then delete the new duplicate table.

Your comment:



 (will not be displayed)


 
 
 
Please add 6 and 2 and type the answer here:
 

Live Comment Preview:

 
«March»
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910