are model-defined functions still supported in EF6

2019-02-18 17:37发布

model-defined functions are discussed here:

are these supported by EF6.1.2?

I'm stepping through the Edm/DbModel stuff and I can't for the life of me work out where the <Function> element in the csdl is supposed to be parsed, because it's not making it into the EdmModel (EdmModel.AddItem(EdmFunction) isn't getting called)

ExpressionConverter.FindFunction looks in EdmModel._functions, and _functions is only added to by EdmModel.AddItem(EdmFunction) and that's only called by the extension method EdmModelExtensions.AddFunction(), and I cannot find anywhere in the EntityFramework source code that calls that function. I must be missing something simple...

more: I gave up on defining the Function in the edmx and now I'm creating my EdmFunction programatically and adding it in a custom IConceptualModelConvention.Apply() method:

    class CustomFunctionConvention : IConceptualModelConvention<EdmModel>
    {
        public void Apply(EdmModel item, DbModel model)
        {
            var functionPayload = new EdmFunctionPayload () {
                CommandText = "CAST (strValue AS int)",
                Parameters = new [] {
                    FunctionParameter.Create("strValue", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String).GetEdmPrimitiveType(), ParameterMode.In),
                },
                ReturnParameters = new [] {
                    FunctionParameter.Create("ReturnValue", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32).GetEdmPrimitiveType(), ParameterMode.ReturnValue),
                },
                IsComposable = true,
            };

            var function = EdmFunction.Create("ParseInt", "MyNamespace", DataSpace.CSpace, functionPayload, null);
            model.ConceptualModel.AddItem(function);
        }
    }

but now I'm getting a bunch of schema errors in EdmItemCollection.LoadItems() :

Schema specified is not valid. Errors:
(0,0) : error 0005: The 'Aggregate' attribute is not allowed.
(0,0) : error 0005: The 'BuiltIn' attribute is not allowed.
(0,0) : error 0005: The 'NiladicFunction' attribute is not allowed.
(0,0) : error 0005: The 'IsComposable' attribute is not allowed.
(0,0) : error 0005: The 'ParameterTypeSemantics' attribute is not allowed.
(0,0) : error 0005: The 'Schema' attribute is not allowed.
(0,0) : error 0005: The 'Mode' attribute is not allowed.

1条回答
做自己的国王
2楼-- · 2019-02-18 18:23

It seems, model defined functions are usable with code first. Here is a version for your ParseInt example :

namespace EfTestModelFunctions
{
    public class CustomFunctionConvention : IConceptualModelConvention<EdmModel>
    {
        public void Apply(EdmModel item, DbModel model)
        {
            var functionParseInt = new EdmFunctionPayload()
            {
                CommandText = String.Format("CAST(strValue AS {0})", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)),
                Parameters = new[] {
                    FunctionParameter.Create("strValue", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.In),
                },

                ReturnParameters = new[] {
                    FunctionParameter.Create("ReturnValue", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32), ParameterMode.ReturnValue),
                },
                IsComposable = true
            };

            var function = EdmFunction.Create("ParseInt", model.ConceptualModel.EntityTypes.First().NamespaceName, DataSpace.CSpace, functionParseInt, null);
            model.ConceptualModel.AddItem(function);
        }
    }

    public class RootDataContext : DbContext
    {
        public RootDataContext()
            : base("Data Source=******")
        {
            Database.SetInitializer(new NullDatabaseInitializer<RootDataContext>());
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Conventions.Add<CustomFunctionConvention>();
        }

        public DbSet<RootEntity> Roots { get; set; }

        // declare the function with the Context's NameSpace
        [DbFunction("EfTestModelFunctions", "ParseInt")]
        public static int ParseInt(string value)
        {
            throw new NotImplementedException();
        }
    }
}

usage will then be :

var query = ctx.Roots.Where(r => RootDataContext.ParseInt(r.StringProperty)==123);

Also, it seems there is an issue when database migration/initialization is involved. See UpForGrabs: Unblock creation of model defined functions in model conventions

查看更多
登录 后发表回答