2014-12-02 19 views
12

İki SQL işlevinde, ORM olarak Entity Framework kullanarak birleşim yapıyorum. Sorgu hata mesajını almak akdedilmiş alır zaman:LINQ sol dış birleştirme sorgusu hatası: OuterApply uygun anahtarlara sahip olmadı

The query attempted to call 'Outer Apply' over a nested query, 
but 'OuterApply' did not have the appropriate keys 

Bu benim sorgu:

var ingredientAllergenData = (from ings in db.fnListIngredientsFromItem(productId, (short)itemType, productId) 
          join ingAllergens in db.fnListAllergensFromItems(productId.ToString(CultureInfo.InvariantCulture), (short)itemType, currentLang) 
          on ings.id equals ingAllergens.ingredientId into ingAllergensData 
          from allergens in ingAllergensData.DefaultIfEmpty() 
          where ings.table == "tblIng" || ings.table == "" 
          select new {ings, allergens}).ToList(); 

Ben LINQPad aynı sorguyu yazdım ve sonuç döndüm, bu yüzden emin değilim konudur:

var ingredientAllergenData = (from ings in fnListIngredientsFromItem(1232, 0, 1232) 
          join ingAllergens in fnListAllergensFromItems("1232", 0, 1) 
          on ings.Id equals ingAllergens.IngredientId into ingAllergensData 
          from allergens in ingAllergensData.DefaultIfEmpty() 
          where ings.Table == "tblIng" || ings.Table == "" 
          select new {ings, allergens}).ToList(); 

LINQPad gelen yanıt: enter image description here

DÜZENLEME budur LINQPad üretilen SQL sorgusu: Ben de C# içine aynı numaraları hardcoding çalıştı ve yine aynı hata var

-- Region Parameters 
    DECLARE @p0 Int = 1232 
    DECLARE @p1 Int = 0 
    DECLARE @p2 Int = 1232 
    DECLARE @p3 VarChar(1000) = '1232' 
    DECLARE @p4 SmallInt = 0 
    DECLARE @p5 Int = 1 
    DECLARE @p6 VarChar(1000) = 'tblIng' 
    DECLARE @p7 VarChar(1000) = '' 
    -- EndRegion 
    SELECT [t0].[prodId] AS [ProdId], [t0].[id] AS [Id], [t0].[parent] AS [Parent], [t0].[name] AS [Name], [t0].[ing_gtin] AS [Ing_gtin], [t0].[ing_artsup] AS [Ing_artsup], [t0].[table] AS [Table], [t0].[quantity] AS [Quantity], [t2].[test], [t2].[prodId] AS [ProdId2], [t2].[ingredientId] AS [IngredientId], [t2].[allergenId] AS [AllergenId], [t2].[allergenName] AS [AllergenName], [t2].[level_of_containment] AS [Level_of_containment] 
    FROM [dbo].[fnListIngredientsFromItem](@p0, @p1, @p2) AS [t0] 
    LEFT OUTER JOIN (
     SELECT 1 AS [test], [t1].[prodId], [t1].[ingredientId], [t1].[allergenId], [t1].[allergenName], [t1].[level_of_containment] 
     FROM [dbo].[fnListAllergensFromItems](@p3, @p4, @p5) AS [t1] 
     ) AS [t2] ON [t0].[id] = ([t2].[ingredientId]) 
    WHERE ([t0].[table] = @p6) OR ([t0].[table] = @p7) 

.

+0

ve bunları değişkenlere atamak ve sorgudakileri kullanmak. LINQ, sorgunun dönüşümler bölümünü yapmaya çalışırken boğulmaya benziyor. – Becuzz

+0

Tam olarak aynı sorguyu Linqpad'de (değişkenleri kullanarak) denemelisiniz. –

+0

Önerileriniz için teşekkürler ama yaptığım ilk şey buydu. – Aleks

cevap

10

Sorun, Entity Framework'ün TVF sonuçlarının birincil anahtar sütunlarının sol birleştirme işlemini yapması gerektiğini bilmesi ve varsayılan oluşturulan EDMX dosyasının bu bilgileri içermemesidir. TVF sonuçlarını bir varlığa (karmaşık bir türe eşleme varsayılanı yerine) eşleyerek anahtar değeri bilgilerini ekleyebilirsiniz.

LINQPad'de aynı sorgunun çalışmasının nedeni, LINQPad'deki bir veritabanına bağlanmak için varsayılan Veri Bağlamı sürücüsünün LINQ to SQL'i (Entity Framework'ü değil) kullanmasıdır. Ancak, Entity Framework'de (sonuçta) çalışacak şekilde sorguyu alabildim.

yerel bir SQL Server veritabanı benzer tablo değerli işlevler kurmak:

CREATE FUNCTION fnListIngredientsFromItem(@prodId int, @itemType1 smallint, @parent int) 
RETURNS TABLE 
AS 
RETURN (
    select prodId = 1232, id = 1827, parent = 1232, name = 'Ossenhaaspunten', ing_gtin =089821, ing_artsup=141020, [table] = 'tblIng', quantity = '2 K' 
); 
go 
CREATE FUNCTION fnListAllergensFromItems(@prodIdString varchar(1000), @itemType2 smallint, @lang int) 
RETURNS TABLE 
AS 
RETURN (
    select prodId = '1232', ingredientId = 1827, allergenId = 11, allergenName = 'fish', level_of_containment = 2 
    union all 
    select prodId = '1232', ingredientId = 1827, allergenId = 16, allergenName = 'tree nuts', level_of_containment = 2 
    union all 
    select prodId = '1232', ingredientId = 1827, allergenId = 12, allergenName = 'crustacean and shellfish', level_of_containment = 2 
); 
go 

Ve Entity Framework 6.1.2 kullanarak bir test projesi oluşturdu ve Varlık Veri Modeli kullanılarak veritabanından bir EDMX dosyayı oluşturan biraz daha farklı bir hata ile sonuçlandı katılmak bir sol için alternatif bir ifade Running

System.NotSupportedException 
    HResult=-2146233067 
    Message=The query attempted to call 'OuterApply' over a nested query, but 'OuterApply' did not have the appropriate keys. 
    Source=EntityFramework 
    StackTrace: 
     at System.Data.Entity.Core.Query.PlanCompiler.NestPullup.ApplyOpJoinOp(Op op, Node n) 
     at System.Data.Entity.Core.Query.PlanCompiler.NestPullup.VisitApplyOp(ApplyBaseOp op, Node n) 
     at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.Visit(OuterApplyOp op, Node n) 
     ... 

:

Bu kurulumla Visual Studio 2013 yılında Tasarımcı, o sorgu çalıştırmak çalışırken aynı hatayı almaya başardı
var ingredientAllergenData = (db.fnListIngredientsFromItem(1323, (short)0, 1) 
    .GroupJoin(db.fnListAllergensFromItems("1232", 0, 1), 
     ing => ing.id, 
     allergen => allergen.ingredientId, 
     (ing, allergen) => new { ing, allergen } 
    ) 
).ToList(); 

İşte yeni istisnadan kesilmiş bir StackTrace geçerli:

System.NotSupportedException 
    HResult=-2146233067 
    Message=The nested query does not have the appropriate keys. 
    Source=EntityFramework 
    StackTrace: 
     at System.Data.Entity.Core.Query.PlanCompiler.NestPullup.ConvertToSingleStreamNest(Node nestNode, Dictionary`2 varRefReplacementMap, VarList flattenedOutputVarList, SimpleColumnMap[]& parentKeyColumnMaps) 
     at System.Data.Entity.Core.Query.PlanCompiler.NestPullup.Visit(PhysicalProjectOp op, Node n) 
     at System.Data.Entity.Core.Query.InternalTrees.PhysicalProjectOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n) 
     ... 

Varlık Framework açık kaynak, bu yüzden aslında bu istisnası atılır kaynak koduna bakabilirsiniz. Bu hataya götüren yolu açıklar

// Make sure that the driving node has keys defined. Otherwise we're in 
// trouble; we must be able to infer keys from the driving node. 
var drivingNode = nestNode.Child0; 
var drivingNodeKeys = Command.PullupKeys(drivingNode); 
if (drivingNodeKeys.NoKeys) 
{ 
    // ALMINEEV: In this case we used to wrap drivingNode into a projection that would also project Edm.NewGuid() thus giving us a synthetic key. 
    // This solution did not work however due to a bug in SQL Server that allowed pulling non-deterministic functions above joins and applies, thus 
    // producing incorrect results. SQL Server bug was filed in "sqlbuvsts01\Sql Server" database as #725272. 
    // The only known path how we can get a keyless drivingNode is if 
    // - drivingNode is over a TVF call 
    // - TVF is declared as Collection(Row) is SSDL (the only form of TVF definitions at the moment) 
    // - TVF is not mapped to entities 
    //  Note that if TVF is mapped to entities via function import mapping, and the user query is actually the call of the 
    //  function import, we infer keys for the TVF from the c-space entity keys and their mappings. 
    throw new NotSupportedException(Strings.ADP_KeysRequiredForNesting); 
} 

, bu yüzden her sorunu çözmek gerektiğini yolu kapalı olsun yapabilirsiniz: Bu snippet'indeki comments sorunu (https://entityframework.codeplex.com/SourceControl/latest#src/EntityFramework/Core/Query/PlanCompiler/NestPullup.cs) ne olduğunu açıklar. Bir tablo değerli fonksiyonun sonuçları üzerinde bu sola katılması gerektiğini varsayarsak, bir seçenek (belki de tek seçenek?) TVF'nin sonuçlarını bir birincil anahtara sahip olan bir varlıkla eşleştirmektir. Daha sonra, Entity Framework, TVF sonuçlarının bu varlığa eşlenmesini temel alan anahtar değerlerini bilecek ve eksik anahtarlarla ilgili bu hataları önlemek durumundayız.

Varsayılan

veritabanından bir EDMX dosyası oluşturma, bir TVF karmaşık tip eşleştirilir. https://msdn.microsoft.com/en-us/library/vstudio/ee534438%28v=vs.100%29.aspx adresinde nasıl değiştirileceğine dair talimatlar vardır.

Test projemde, model tasarımcısını Varlıklar oluşturmak için TVF'lerin çıktısıyla eşleşen bir şemaya ekledim, daha sonra model tarayıcıya gittim ve bir işlev koleksiyonunu döndürmek için işlev içe aktarma işlevini güncelledim Bu varlıklar (otomatik oluşturulan karmaşık türler yerine). Bu değişiklikleri yaptıktan sonra, aynı LINQ sorgusu hatasız koştu. İşte

var ingredientAllergenData = (from ings in db.fnListIngredientsFromItem(productId, (short)itemType, productId) 
          join ingAllergens in db.fnListAllergensFromItems(productId.ToString(CultureInfo.InvariantCulture), (short)itemType, currentLang) 
          on ings.id equals ingAllergens.ingredientId into ingAllergensData 
          from allergens in ingAllergensData.DefaultIfEmpty() 
          where ings.table == "tblIng" || ings.table == "" 
          select new {ings, allergens}).ToList(); 

sorgu verdi bana iz SQL geçerli:

SELECT 
    1 AS [C1], 
    [Extent1].[prodId] AS [prodId], 
    [Extent1].[id] AS [id], 
    [Extent1].[parent] AS [parent], 
    [Extent1].[name] AS [name], 
    [Extent1].[ing_gtin] AS [ing_gtin], 
    [Extent1].[ing_artsup] AS [ing_artsup], 
    [Extent1].[table] AS [table], 
    [Extent1].[quantity] AS [quantity], 
    [Extent2].[prodId] AS [prodId1], 
    [Extent2].[ingredientId] AS [ingredientId], 
    [Extent2].[allergenId] AS [allergenId], 
    [Extent2].[allergenName] AS [allergenName], 
    [Extent2].[level_of_containment] AS [level_of_containment] 
    FROM [dbo].[fnListIngredientsFromItem](@prodId, @itemType1, @parent) AS [Extent1] 
    LEFT OUTER JOIN [dbo].[fnListAllergensFromItems](@prodIdString, @itemType2, @lang) AS [Extent2] ON ([Extent1].[id] = [Extent2].[ingredientId]) OR (([Extent1].[id] IS NULL) AND ([Extent2].[ingredientId] IS NULL)) 
    WHERE [Extent1].[table] IN ('tblIng','') 
Ben sorgunun dışında (vb aramaları .ToString, kısa çevrim) çalışırken tüm dönüşümleri yaparak başlayacak
+0

"Aferin sevgilim Watson!" 'Db.fnListIngredientsFromItem (ProductID, (kısa) itemtype, productId) .OrderBy (x => x.ProdId)': Ben başka bir yolu istisna kurtulmak için eğer bir 'OrderBy' eklemektir merak ediyorum. Bkz. [This] (http://stackoverflow.com/a/27206566/861716). –

+0

Harika bir cevap, başka bir sorunu çözmek için bana fikir verdi. – rodpl

+0

Harika bir açıklama! Benim durumumda TVF'in sonuçlarını gruplandırıyordum ve daha sonra '' g.Count() == numSearchTerms || g.FirstOrDefault(). SearchTerm == null'. Bunu okuyarak, şunu denemek istedim: 'g.Count() == numSearchTerms || g.Any (x => x.SearchTerm == null) 'yerine ve çalıştı! – adam0101

İlgili konular