score:14
the problem is that entity framework needs to know what the primary key columns of the tvf results are to do a left join, and the default generated edmx file does not contain that information. you can add the key value information by mapping the tvf results to an entity (instead of the default of mapping to a complex type).
the reason the same query works in linqpad is that the default data context driver for connecting to a database in linqpad uses linq to sql (not entity framework). but i was able to get the query to run in entity framework (eventually).
i set up a local sql server database similar table-valued functions:
create function fnlistingredientsfromitem(@prodid int, @itemtype1 smallint, @parent int)
returns table
as
return (
select prodid = 1232, id = 1827, parent = 1232, name = 'ossenhaaspunten', ing_gtin = 3003210089821, 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
and i created a test project using entity framework 6.1.2 and generated an edmx file from the database using the entity data model designer in visual studio 2013. with this setup, i was able to get the same error when trying to run that query:
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)
...
running an alternate expression for a left join resulted in a slightly different error:
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();
here is a truncated stacktrace from the new exception:
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)
...
entity framework is open source, so we can actually look at the source code where this exception is thrown. the comments in this snippet explains what the problem is (https://entityframework.codeplex.com/sourcecontrol/latest#src/entityframework/core/query/plancompiler/nestpullup.cs):
// 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);
}
that explains the path that leads to that error, so anything we can do to get off that path should fix the problem. assuming we have to do that left join on the results of a table-valued function, one option (maybe the only option?) is to map the results of the tvf to an entity that has a primary key. then entity framework will know the key values of the tvf results based on the mapping to that entity, and we should avoid these errors related to missing keys.
by default when generating an edmx file from the database, a tvf is mapped to a complex type. there are instructions for how to change it at https://msdn.microsoft.com/en-us/library/vstudio/ee534438%28v=vs.100%29.aspx.
in my test project, i added an empty table with a schema that matched the output of the tvfs to get the model designer to generate entities, then i went to the model browser and updated the function imports to return a collection of these entities (instead of the auto-generated complex types). after making these changes, that same linq query ran without errors.
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();
here is the trace sql that the query gave me:
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','')
Source: stackoverflow.com
Related Query
- LINQ left outer join query error: OuterApply did not have the appropriate keys
- Left outer join using LINQ -- understanding the code
- The nested query does not have the appropriate keys
- Linq query of left outer join not properly working
- LINQ Left join on my than one field? I have the SQL working but can't convert it to a linq query
- Linq join query with left outer join throwing object reference not found
- Null value in the result of a left outer join linq causes error
- Left outer join linq query object reference not set to an instance of an object
- Not counting null values from a linq LEFT OUTER JOIN query
- Using multiple LINQ statements with into , for the DefaultIfEmpty() of the left outer join not working
- why .ToList().Distinct() throws error but not the .Distinct().ToList() with linq query
- How to do a left outer join in Entity Framework without using the query syntax?
- Linq Left Outer Join - DefaultIfEmpty Error
- Unable to convert SQL Query to LINQ Query for Left Outer Join
- How to write a LEFT OUTER JOIN where the right side is null in LINQ to SQL?
- Join in LINQ Query Syntax: moving right side to the left
- Linq left outer join not working using DefaultIfEmpty
- Outer join with linq query in EF Core 3 not working
- How to add left outer join to grouped and summed LINQ query
- Linq - Using a left join on to a table which may not have records
- Left outer join using LINQ Query Syntax EF Core C#
- Linq to SQL Left Outer Join Not
- Outer Join is not working in Linq Query: The method 'Join' cannot follow the method 'SelectMany' or is not supported
- Linq expression multiple left outer join error
- LINQ Left outer join - Object reference not set to an instance of an object
- Getting "Could not find an implementation of the query pattern for source type 'ExcelQueryable<T>'. " Error
- LINQ join query with multiple fields using left outer join
- Not getting LEFT JOIN query in LINQ to pick up empty rows
- Left Join on Linq query when also using the Where clause on joint table
- Name doesn't exist in the current context LEFT OUTER JOIN in LINQ
More Query from same tag
- How to check if the "id" is null or empty then default values are 0 or empty string?
- LINQ - ToString("N2") fails without exception?
- How to sort a Dictionary<string, List<int>> by a certain element in values list?
- How to group by Games and select each game's list of Genres
- Adding a where/order by clause to an IQueryable
- Add an existing xml element to a xml document
- Linq Query To Combine 2 Lists
- How to convert group by with join from SQL Server to Linq?
- Converting a Dictionary to a List with formatting
- Get all data from database for Administrator role but not for other roles, using same query
- XAttribute default value if not existing on XElement
- C# implement raven db full text search by the part of word
- PowerShell equivalent of LINQ Any()?
- Switching DB Context in LINQ Entity Framework asp.net MVC 5
- Filter a specific record from xml file to gridview asp.net
- LINQ Using inline subquery with min() in a left join where clause
- How can I use LINQ to select a property and also other items of a list at the same level?
- An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext
- Access count of related records of an entity Entity Framework
- Concatenating two fields in LINQ select
- Writing an anonymous method for a method in IEnumerable
- Copy list objects from one list to another list based on properties
- How to use LINQ to group by multiple conditions in a C# List?
- LINQ to Entities does not recognize the method. Guid causing trouble?
- Having an issue with a null exception on WPF DataGrid when trying to display data from a database with Linq
- How to ignore a specific with a LINQ where clause?
- Understanding .AsEnumerable() in LINQ to SQL
- can wcf service be loosely decoupled
- C# .First() vs [0]
- LinQ to get the latest group of records satisfying a condition