28

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

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

问题是:

我无法通过 [Table]、[Column] 等属性映射函数。只有 1 个属性可用 [DbFunction],它需要 *.edmx 文件。

我可以在 *.edmx 文件中进行函数映射,但这意味着我不能对实体使用属性映射:[表]、[列]。*.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。有人可以建议我如何将功能添加到模型中吗?添加功能的接口是暴露的,所以应该可以做,但是网上没有关于这种情况的信息。可能有人知道 Entity Framework 团队何时会为 Line To Sql 之类的函数实现属性映射。

EF 版本:6.0.0-beta1-20521

谢谢!


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

 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

4 回答 4

16

尚未尝试过,但Entity Framework 6.1包含公共映射 API。Moozzyk使用这个新功能为 EntityFramework CodeFirst实现了 Store Functions 。

代码如下所示:

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);
    }
}
于 2014-05-14T15:00:30.647 回答
4

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

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
于 2016-11-17T19:35:21.547 回答
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

于 2013-11-22T22:22:45.933 回答
1

现在实体框架不是测试版,所以也许你解决了你的问题,但是这个解决了我的问题如何将标量值函数与 linq 一起使用到实体?

于 2014-02-18T11:49:33.280 回答