Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Whilst implementing the AdventureWorks db in a sample application using Linq 2 SQL i've ran into a problem with the way in which Linq 2 SQL generates SQL statements for derived types in the inheritance mapping defined in my DBML.

I have two entities in my DBML, Person and PersonPhone (one Person to many PersonPhones) WITHOUT any inheritance mapping defined (Person will be a inheritance base class in a later example). If I then run the following Linq statement I notice that whilst doing a trace the following SQL is generated as expected:

void Main()
{
this.DeferredLoadingEnabled = false;

DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Person>(n => n.PersonPhones);
this.LoadOptions = dlo;

Person person = this.Persons.SingleOrDefault(n => n.BusinessEntityID == 291);
person.PersonPhones.Dump(); 
}

DECLARE @p0 Int = 291
SELECT [t0].[BusinessEntityID], [t0].[PersonType], [t0].[NameStyle], [t0].[Title], [t0].[FirstName], [t0].[MiddleName], [t0].[LastName], [t0].[Suffix], [t0].[EmailPromotion], [t0].[AdditionalContactInfo], [t0].[Demographics], [t0].[rowguid], [t0].[ModifiedDate], [t1].[BusinessEntityID] AS [BusinessEntityID2], [t1].[PhoneNumber], [t1].[PhoneNumberTypeID], [t1].[ModifiedDate] AS [ModifiedDate2], (
    SELECT COUNT(*)
    FROM [Person].[PersonPhone] AS [t2]
    WHERE [t2].[BusinessEntityID] = [t0].[BusinessEntityID]
    ) AS [value]
FROM [Person].[Person] AS [t0]
LEFT OUTER JOIN [Person].[PersonPhone] AS [t1] ON [t1].[BusinessEntityID] = [t0].[BusinessEntityID]
WHERE [t0].[BusinessEntityID] = @p0
ORDER BY [t0].[BusinessEntityID], [t1].[PhoneNumber], [t1].[PhoneNumberTypeID]

The result is that the SQL is generated in a single statement. Please ignore the fact that this might be inefficient SQL, the point i'm making is that its ran in a single statement.

But if I include inheritance mapping information in my DBML and define a new entity type called StoreContact (derives from Person) and run the exact same query I actually get different results as shown below. In that TWO SQL statements are actually run.

DECLARE @p0 Int = 291
SELECT [t0].[PersonType], [t0].[BusinessEntityID], [t0].[NameStyle], [t0].[Title], [t0].[FirstName], [t0].[MiddleName], [t0].[LastName], [t0].[Suffix], [t0].[EmailPromotion], [t0].[AdditionalContactInfo], [t0].[Demographics], [t0].[rowguid], [t0].[ModifiedDate]
FROM [Person].[Person] AS [t0]
WHERE [t0].[BusinessEntityID] = @p0
GO

DECLARE @x1 Int = 291
SELECT [t0].[BusinessEntityID], [t0].[PhoneNumber], [t0].[PhoneNumberTypeID], [t0].[ModifiedDate]
FROM [Person].[PersonPhone] AS [t0]
WHERE [t0].[BusinessEntityID] = @x1

This is somewhat problematic in that this will reduce the speed of our applications rather substantially in that Linq 2 SQL is required to make duplicate calls to the DB in the case of implementing the inheritance feature. Looks like having an OOP model does come at a cost.

Is there a workaround for this problem to make the SQL run in a single statement?

share|improve this question
 
A point I should express is that in this example I'm using the SAME linq statement both before and after the inheritance mapping change. –  user978139 Feb 14 at 11:03
 
Why does the first SQL do counting? Does L2S insert this horrible aggregate or is that from your code? –  usr Feb 14 at 11:16
 
Because 'query' isn't ran against the db until the following line is executed: ac.PersonPhones.Count.Dump(), therefore it translates it into the most appropriate SQL statement based upon the linq statement. This is just an example of when it has performed the linq query in a single SQL statement in which I'm trying to achieve with inheritance, the fact that it might have generated inefficient SQL doesn't really matter because I can rewrite my Linq query to be more efficient for the purpose of doing a count. –  user978139 Feb 14 at 11:52
 
ac.PersonPhones is not Iqueryable. LINQ does not optimize this in any way. This statement cannot possibly influence the execution of query. Right now I have doubts that the code shown corresponds to the SQL shown. Can you review this? Maybe single-step through and observe which lines generate which SQL. –  usr Feb 14 at 11:54
 
I've double-checked and confirm that the Linq code provided in the example generates the stated SQL statement. I have checked this through using LinqPad which details the SQL queries executed and against SQL Server Profiler. The SQL which has been generated in the example makes sense, I wouldn't expect anything different based upon the linq query i have written. –  user978139 Feb 14 at 12:04
add comment

1 Answer

up vote 0 down vote accepted

Unfortunately not the answer I'm looking for but an upgrade to the Entity Framework would resolve the problem whilst maintaining the inheritance.

If I run the following EF equivalent query:

void Main()
{
    StoreContact person  =(StoreContact)this.People.Include("PersonPhones").SingleOrDefault(n => n.BusinessEntityID == 291);
    person.PersonPhones.Dump(); 
}

I get the following SQL result:

SELECT 
[Project2].[BusinessEntityID] AS [BusinessEntityID], 
[Project2].[C1] AS [C1], 
[Project2].[NameStyle] AS [NameStyle], 
[Project2].[Title] AS [Title], 
[Project2].[FirstName] AS [FirstName], 
[Project2].[MiddleName] AS [MiddleName], 
[Project2].[LastName] AS [LastName], 
[Project2].[Suffix] AS [Suffix], 
[Project2].[EmailPromotion] AS [EmailPromotion], 
[Project2].[AdditionalContactInfo] AS [AdditionalContactInfo], 
[Project2].[Demographics] AS [Demographics], 
[Project2].[rowguid] AS [rowguid], 
[Project2].[ModifiedDate] AS [ModifiedDate], 
[Project2].[C2] AS [C2], 
[Project2].[BusinessEntityID1] AS [BusinessEntityID1], 
[Project2].[PhoneNumber] AS [PhoneNumber], 
[Project2].[PhoneNumberTypeID] AS [PhoneNumberTypeID], 
[Project2].[ModifiedDate1] AS [ModifiedDate1]
FROM ( SELECT 
    [Limit1].[BusinessEntityID] AS [BusinessEntityID], 
    [Limit1].[NameStyle] AS [NameStyle], 
    [Limit1].[Title] AS [Title], 
    [Limit1].[FirstName] AS [FirstName], 
    [Limit1].[MiddleName] AS [MiddleName], 
    [Limit1].[LastName] AS [LastName], 
    [Limit1].[Suffix] AS [Suffix], 
    [Limit1].[EmailPromotion] AS [EmailPromotion], 
    [Limit1].[AdditionalContactInfo] AS [AdditionalContactInfo], 
    [Limit1].[Demographics] AS [Demographics], 
    [Limit1].[rowguid] AS [rowguid], 
    [Limit1].[ModifiedDate] AS [ModifiedDate], 
    [Limit1].[C1] AS [C1], 
    [Extent2].[BusinessEntityID] AS [BusinessEntityID1], 
    [Extent2].[PhoneNumber] AS [PhoneNumber], 
    [Extent2].[PhoneNumberTypeID] AS [PhoneNumberTypeID], 
    [Extent2].[ModifiedDate] AS [ModifiedDate1], 
    CASE WHEN ([Extent2].[BusinessEntityID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
    FROM   (SELECT TOP (2) 
        [Extent1].[BusinessEntityID] AS [BusinessEntityID], 
        [Extent1].[NameStyle] AS [NameStyle], 
        [Extent1].[Title] AS [Title], 
        [Extent1].[FirstName] AS [FirstName], 
        [Extent1].[MiddleName] AS [MiddleName], 
        [Extent1].[LastName] AS [LastName], 
        [Extent1].[Suffix] AS [Suffix], 
        [Extent1].[EmailPromotion] AS [EmailPromotion], 
        [Extent1].[AdditionalContactInfo] AS [AdditionalContactInfo], 
        [Extent1].[Demographics] AS [Demographics], 
        [Extent1].[rowguid] AS [rowguid], 
        [Extent1].[ModifiedDate] AS [ModifiedDate], 
        '0X0X' AS [C1]
        FROM [Person].[Person] AS [Extent1]
        WHERE 291 = [Extent1].[BusinessEntityID] ) AS [Limit1]
    LEFT OUTER JOIN [Person].[PersonPhone] AS [Extent2] ON [Limit1].[BusinessEntityID] = [Extent2].[BusinessEntityID]
)  AS [Project2]
ORDER BY [Project2].[BusinessEntityID] ASC, [Project2].[C2] ASC

The SQL statement returns the results within a single query.

The reason this isn't the answer i'm looking for is that for a transition from Linq to SQL to Entity Framework for an existing application is a rather big step.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.