0

Linq 2 SQL を使用してサンプル アプリケーションに AdventureWorks db を実装しているときに、DBML で定義された継承マッピングで Linq 2 SQL が派生型の SQL ステートメントを生成する方法に問題がありました。

DBML に Person と PersonPhone (1 つの Person から多数の PersonPhone へ) という 2 つのエンティティがあり、継承マッピングは定義されていません (Person は後の例で継承基本クラスになります)。次に、次の Linq ステートメントを実行すると、トレースの実行中に次の SQL が期待どおりに生成されることに気付きます。

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]

その結果、SQL は単一のステートメントで生成されます。これは非効率的な SQL である可能性があるという事実を無視してください。私が指摘しているのは、単一のステートメントで実行されたということです。

しかし、DBML に継承マッピング情報を含め、StoreContact (Person から派生) という新しいエンティティ タイプを定義し、まったく同じクエリを実行すると、実際には以下に示すように異なる結果が得られます。その中で、2 つの SQL ステートメントが実際に実行されます。

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

これは、継承機能を実装する場合にDBへの重複呼び出しを行うためにLinq 2 SQLが必要であるという点で、アプリケーションの速度をかなり大幅に低下させるという点で、多少問題があります. OOP モデルにはコストがかかるようです。

この問題を回避して、SQL を単一のステートメントで実行する方法はありますか?

4

1 に答える 1

0

残念ながら、私が探している答えではありませんが、Entity Framework へのアップグレードは、継承を維持しながら問題を解決します。

次の EF 同等のクエリを実行すると:

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

次の SQL 結果が得られます。

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

SQL ステートメントは、1 つのクエリ内で結果を返します。

これが私が探している答えではない理由は、既存のアプリケーションの Linq から SQL、Entity Framework への移行はかなり大きなステップだからです。

于 2013-02-18T17:18:41.520 に答える