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

What is a namespaceName re table valued functions? #15

Closed WillSullivan closed 7 years ago

WillSullivan commented 7 years ago

Apparently TVFs must have a namespace name. The problem I'm having is

What the heck does that even mean?

My function is dbo.SomeStupidTVFWhyAreYouHarassingMe. What's the namespace?

The doc example doesn't specify a namespace name.

https://weblogs.asp.net/Dixin/EntityFramework.Functions#Table-valued_function

Someone asked about this problem in the comments section of your blog post.

Yes I think you may miss something :)

The exception is thrown from https://github.com/Dixin/EntityFramework.Functions/blob/master/EntityFramework.Functions/FunctionAttribute.cs, line 52. It seems you have defined a table-valued function in MyEntities. EF requires a namespace name for table-valued function. So you have to specify it: [Function(FunctionType.TableValuedFunction, "functionName", "namespaceName", Schema = "dbo")] Or: [TableValuedFunction("functionName", "namespaceName", Schema = "dbo")]

... specify what? An empty string doesn't work. Neither does random text.

What am do?

WillSullivan commented 7 years ago

Switched it out for

[TableValuedFunction("SomeStupidTVFWhyAreYouHarassingMe", "Database", Schema = "dbo")]

and it appears to get past the initial issue. But it appears that this isn't compat with EF 6.1.3. When I add the convention

modelBuilder.Conventions.Add(new FunctionConvention<Database>());

it seems to muck up the schema of the type being returned by the TVF, when that type is already in use in my application.

To say it another way, I have a table of nodes that represent nodes in a tree. I'm competing in the Stupid Olympics event where I have to return immediate children of a node that have descendants with leaves of a given type. The TVF is part of this, returning the result of a CTE that gives me ancestors of a given node.

ANYHOW

While I already have a DbSet<Node> I figured I'd have the return result of the TVF be IQueryable<Node>. But now when I run, I have schema errors

Schema specified is not valid. Errors: (0,0) : error 0040: The Type Int32 is not qualified with a namespace or alias. Only primitive types can be used without qualification. (0,0) : error 0040: The Type String is not qualified with a namespace or alias. Only primitive types can be used without qualification. (0,0) : error 0040: The Type Int32 is not qualified with a namespace or alias. Only primitive types can be used without qualification. (0,0) : error 0040: The Type Int32 is not qualified with a namespace or alias. Only primitive types can be used without qualification.

A Node has three int properties and one string property, so I'm guessing the Node is borked.

I'll probably fork and investigate next week, and use a complex type in the meantime, but is there a way to do this without breaking stuff?

WillSullivan commented 7 years ago

Workaround is to create a ComplexType, but shouldn't type reuse be supported? I'm going to look into what is causing the issue...

Dixin commented 7 years ago

... specify what?

Specify the DbContext class name, which is required by EF. See the definition:

    public class TableValuedFunctionAttribute : FunctionAttribute
    {
        /// <summary>
        /// Marks a function as mapped to a Table Valued Function.
        /// </summary>
        /// <param name="name">The name of the Table Valued Function in the data store.</param>
        /// <param name="dbContextName">The name of the <see cref="DbContext"/> class.</param>
        public TableValuedFunctionAttribute(string name, string dbContextName)
            : base(FunctionType.TableValuedFunction, name, dbContextName)
        {
        }
    }

If you still have issue, can you share your code so I can look into?

WillSullivan commented 7 years ago

In the original that I linked to, it said "namespaceName" and not "dbContextName", which is where the confusion came from.

The other issue was fixed by not reusing the type returned by the TVF. I created two identical types--NodeAncestor and NodeDescendent, registered them and moved on. A valid workaround, but it would be nice if type reuse was supported.