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
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
Tam olarak aynı sorguyu Linqpad'de (değişkenleri kullanarak) denemelisiniz. –
Önerileriniz için teşekkürler ama yaptığım ilk şey buydu. – Aleks