实体框架6代码优先功能映射

时间:2013-05-24 14:29:36

标签: c# entity-framework ef-code-first entity-framework-6 sql-function

我想将Entity Framework 6集成到我们的系统中,但是有问题。

  1. 我想使用Code First。出于其他原因,我不想使用Database First * .edmx文件。
  2. 我使用属性映射[Table],[Column],这很好用
  3. 数据库有许多用户定义的函数,我需要在Linq To Entities查询中使用它们。
  4. 问题是:

    我不能通过像[Table],[Column]这样的属性来映射函数。只有1个属性可用[DbFunction],它需要* .edmx文件。

    我可以在* .edmx文件中映射函数,但这意味着我无法使用实体的属性映射:[Table],[Column]。必须在* .edmx或属性中填充映射。

    我尝试通过以下代码创建DbModel并添加功能:

    public static class Functions
    {
        [DbFunction("CodeFirstNamespace", "TestEntity")]
        public static string TestEntity()
        {
            throw new NotSupportedException();
        }
    }
    
    
    public class MyContext : DbContext, IDataAccess
    {
        protected MyContext (string connectionString)
            : base(connectionString, CreateModel())
        {
        }
    
        private static DbCompiledModel CreateModel()
        {
            var dbModelBuilder = new DbModelBuilder(DbModelBuilderVersion.Latest);
            dbModelBuilder.Entity<Warehouse>();
            var dbModel = dbModelBuilder.Build(new DbProviderInfo("System.Data.SqlClient", "2008"));
    
            var edmType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String);
            var payload =
                new EdmFunctionPayload
                {
                    Schema = "dbo",
                    ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion,
                    IsComposable = true,
                    IsNiladic = false,
                    IsBuiltIn = false,
                    IsAggregate = false,
                    IsFromProviderManifest = true,
                    StoreFunctionName = "TestEntity",
                    ReturnParameters =
                        new[]
                        {
                            FunctionParameter.Create("ReturnType", edmType, ParameterMode.ReturnValue)
                        }
                };
    
            var function = EdmFunction.Create("TestEntity", "CodeFirst", DataSpace.CSpace, payload, null);
            dbModel.DatabaseMapping.Model.AddItem(function);
            var compiledModel = dbModel.Compile();       // Error happens here
            return compiledModel;
        }
    }
    

    但有例外:

    在模型生成期间检测到一个或多个验证错误:

    Edm.String: : The namespace 'String' is a system namespace and cannot be used by other schemas. Choose another namespace name.
    

    问题出在“edmType”变量中。我无法正确创建函数的ReturnType。 任何人都可以建议我如何在模型中添加功能? 添加功能的界面是暴露的,所以它应该能够做到,但是在这种情况下web中没有信息。 可能有人知道Entity Framework团队何时会为Line To Sql这样的函数实现属性映射。

    EF版本:6.0.0-beta1-20521

    谢谢!


    是的,这对我有用。但仅适用于标量函数。我也需要map函数,它返回IQueryable:

     IQueryable<T> MyFunction()
    

    其中T是EntityType或RowType或任何类型。我完全不能这样做(EF版本是6.0.2-21211)。我认为这应该是这样的:

    private static void RegisterEdmFunctions(DbModel model)
    {
        var storeModel = model.GetStoreModel();
        var functionReturnValueType = storeModel.EntityTypes.Single(arg => arg.Name == "MyEntity").GetCollectionType();
        var payload =
            new EdmFunctionPayload
            {
                IsComposable = true,
                Schema = "dbo",
                StoreFunctionName = "MyFunctionName",
                ReturnParameters =
                    new[]
                    { 
                        FunctionParameter.Create("ReturnValue", functionReturnValueType, ParameterMode.ReturnValue)
                    },
                Parameters =
                    new[]
                    {
                        FunctionParameter.Create("MyFunctionInputParameter", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32), ParameterMode.In)
                    }
            };
        storeModel.AddItem(EdmFunction.Create(
            payload.StoreFunctionName,
            "MyFunctionsNamespace",
            DataSpace.SSpace,
            payload,
            payload.Parameters.Select(arg => MetadataProperty.Create(arg.Name, arg.TypeUsage, null)).ToArray()));
    }
    

    但仍然没有运气:

      model.Compile();  // ERROR 
    

    有可能吗? 可能步骤不对? 可能会在EF 6.1上添加支持。 任何信息都非常有用。

    谢谢!

4 个答案:

答案 0 :(得分:15)

尚未尝试过,但 Entity Framework 6.1 包括public mapping API。 Moozzyk使用这项新功能实现了Store Functions for EntityFramework CodeFirst

这是代码的样子:

public class MyContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(new FunctionsConvention<MyContext>("dbo"));
    }

    [DbFunction("MyContext", "CustomersByZipCode")]
    public IQueryable<Customer> CustomersByZipCode(string zipCode)
    {
        var zipCodeParameter = zipCode != null ?
            new ObjectParameter("ZipCode", zipCode) :
            new ObjectParameter("ZipCode", typeof(string));

        return ((IObjectContextAdapter)this).ObjectContext
            .CreateQuery<Customer>(
                string.Format("[{0}].{1}", GetType().Name, 
                    "[CustomersByZipCode](@ZipCode)"), zipCodeParameter);
    }

    public ObjectResult<Customer> GetCustomersByName(string name)
    {
        var nameParameter = name != null ?
            new ObjectParameter("Name", name) :
            new ObjectParameter("Name", typeof(string));

        return ((IObjectContextAdapter)this).ObjectContext.
            ExecuteFunction("GetCustomersByName", nameParameter);
    }
}

答案 1 :(得分:3)

您可以使用辅助方法从基本类型获取Store类型:

    public static EdmType GetStorePrimitiveType(DbModel model, PrimitiveTypeKind typeKind)
    {
        return model.ProviderManifest.GetStoreType(TypeUsage.CreateDefaultTypeUsage(
            PrimitiveType.GetEdmPrimitiveType(typeKind))).EdmType;
    }

在您的示例中,您必须更改返回参数的类型:

var edmType = GetStorePrimitiveType(model, PrimitiveTypeKind.String);


我找到了我需要的帮助: http://entityframework.codeplex.com/discussions/466706

答案 2 :(得分:3)

以下是[测试]所需的所有步骤:

Install-Package EntityFramework.CodeFirstStoreFunctions

为输出结果声明一个类:

public class MyCustomObject
{
   [Key]
   public int Id { get; set; }
   public int Rank { get; set; }
}

在DbContext类中创建一个方法

[DbFunction("MyContextType", "SearchSomething")]
public virtual IQueryable<MyCustomObject> SearchSomething(string keywords)
{
   var keywordsParam = new ObjectParameter("keywords", typeof(string)) 
                           { 
                              Value = keywords 
                            };
    return (this as IObjectContextAdapter).ObjectContext
    .CreateQuery<MyCustomObject>(
     "MyContextType.SearchSomething(@keywords)", keywordsParam);
}

添加

public DbSet<MyCustomObject> SearchResults { get; set; }

到您的DbContext类

添加覆盖OnModelCreating方法:

modelBuilder.Conventions
.Add(new CodeFirstStoreFunctions.FunctionsConvention<MyContextType>("dbo"));

现在你可以打电话/加入 表值函数如下:

CREATE FUNCTION SearchSomething
(   
    @keywords nvarchar(4000)
)
RETURNS TABLE 
AS
RETURN 
(SELECT KEY_TBL.RANK AS Rank, Id
FROM MyTable 
LEFT JOIN freetexttable(MyTable , ([MyColumn1],[MyColumn2]), @keywords) AS KEY_TBL      
ON MyTable.Id = KEY_TBL.[KEY]  
WHERE KEY_TBL.RANK > 0   
)
GO

答案 3 :(得分:1)

现在实体框架不是测试版,所以也许你解决了问题,但是这个解决了我的问题How to use scalar-valued function with linq to entity?