Dixin / EntityFramework.Functions

EntityFramework.Functions library implements Entity Framework code first support for stored procedures (with single result type, multiple result types, output parameter), table-valued functions (returning entity type, complex type), scalar-valued functions (composable, non-composable), aggregate functions, built-in functions, niladic functions, and model defined functions.
https://weblogs.asp.net/Dixin/EntityFramework.Functions
MIT License
79 stars 27 forks source link

Table valued functions are not composable #1

Closed SteveRuble closed 8 years ago

SteveRuble commented 8 years ago

Attempting to call a TVF from within a LINQ query results in an exception:

System.NotSupportedException: The specified method 
'System.Linq.IQueryable`1[EntityFramework.Functions.Tests.Examples.ContactInformation] ufnGetContactInformation(System.Nullable`1[System.Int32])' 
on the type 'EntityFramework.Functions.Tests.Examples.AdventureWorks' cannot 
be translated into a LINQ to Entities store expression because its return 
type does not match the return type of the function specified by its DbFunction attribute.

    at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.FunctionCallTranslator.ValidateReturnType(DbExpression result, TypeUsage actualReturnType, ExpressionConverter parent, MethodCallExpression call, Type clrReturnType, Boolean isElementOfCollection)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.FunctionCallTranslator.ValidateReturnType(DbExpression result, TypeUsage actualReturnType, ExpressionConverter parent, MethodCallExpression call, Type clrReturnType, Boolean isElementOfCollection)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.FunctionCallTranslator.TranslateFunctionCall(ExpressionConverter parent, MethodCallExpression call, DbFunctionAttribute functionAttribute)

The problem is that FunctionCallTranslator.TranslateFunctionCall looks up the function using the DbFunctionAttribute.NamespaceName property to get the namespace, but since the namespace is set to "CodeFirstDatabaseSchema" rather than the entity container name the function can't be found in CSpace and it gets looked up in SSpace instead (in Perspective.TryGetFunctionByName). The SSpace version of the function has the wrong return type, so FunctionCallTranslator.ValidateReturnType fails.

One fix is to change the constructor for EntityFramework.Functions.FunctionAttribute to something like

       public FunctionAttribute(FunctionType type, string name, string namespaceName = Function.CodeFirstDatabaseSchema)
            : base(namespaceName, name) 

and add documentation that the namespaceName parameter should only be set for TVF imports. That's a little smelly though. I've put together a small change that adds specific attributes for each FunctionType to make things a little more clear. I'll submit a pull request for that, but maybe you can figure out away to make everything work correctly without adding anything to the API. I did some experimentation in Function.AddFunction but I couldn't figure out anything that worked.