简体   繁体   English

代码第一个实体框架6.1自定义聚合函数

[英]Code first Entity Framework 6.1 Custom Aggregate Function

I have a custom CLR Aggregate function on SQL Server to calculate percentiles. 我在SQL Server上有一个自定义CLR聚合函数来计算百分位数。 Is it possible to call my custom aggregate function through Entity Framework? 是否可以通过Entity Framework调用我的自定义聚合函数? How is the mapping configured to allow this? 如何配置映射以允许此操作?

I have tried using codefirstfunctions similar to what is described on Entity Framework 6 Code First Custom Functions , however the functions seem to only be allowed to take scaler parameters, where my function is an aggregate function so will need to take a list of items (similar to how Sum, Averagg and Count work). 我尝试使用类似于Entity Framework 6 Code First Custom Functions中描述的codefirstfunctions,但是函数似乎只允许使用scaler参数,其中我的函数是一个聚合函数,因此需要获取一个项目列表(类似如何Sum,Averagg和Count工作)。

The Aggregate functions has the following signature, taking in the value we want the median from and the percentile (50 is median, 25 lower quartile, 75 upper quartile) Aggregate函数具有以下签名,包含我们想要的中值和百分位值(50是中位数,25下四分位数,75上四分位数)

CREATE AGGREGATE [dbo].[Percentile]
(@value [float], @tile [smallint])
RETURNS[float]
EXTERNAL NAME [SqlFuncs].[Percentile]
GO

I have tried adding a DbFunctionAttribute, but not entirely sure how to hook it up to entity framework store model using code first. 我已经尝试添加DbFunctionAttribute,但不完全确定如何首先使用代码将其连接到实体框架存储模型。

[DbFunction("SqlServer", "Percentile")]

public static double? Percentile(IEnumerable<int?> arg, int tile)
{
    throw new NotSupportedException("Direct calls are not supported.");
}

What I am looking for is to be able to write something like 我正在寻找的是能够写出类似的东西

paymentsTable
    .GroupBy(x=>x.CustomerId)
    .Select(new{
            Median = MyDbContext.Percentile(x.Select(g=>g.Amount), 50)
    });

Which will map to SQL like 哪个会映射到SQL之类的

SELECT [dbo].[Percentile](Amount, 50) as Median
FROM Payments
GROUP BY CustomerId

As @srutzky alluded to in the comments, EF doesnt seem to like binding to aggregate functions with multiple parameters. 正如@srutzky在评论中提到的那样,EF似乎不喜欢使用多个参数绑定聚合函数。 So you have to change percentile function to a median function or whatever fixed percentile you are interested (you will need to update your SqlClr function so the parameters match as well) 因此,您必须将百分位数函数更改为中值函数或您感兴趣的任何固定百分位数(您将需要更新SqlClr函数以使参数匹配)

public class MySqlFunctions
{
    [DbFunction("dbo", "Median")]
    public static float? Median(IEnumerable<float?> arg)
    {
        throw new NotSupportedException("Direct calls are not supported.");
    }
}

The next step is letting EF know that a the database has a function called median We can do this in our DbContext. 下一步是让EF知道数据库有一个名为median的函数我们可以在DbContext中执行此操作。 Create a new convention to access the the dbModel then we add the function in the dbModel. 创建一个新的约定来访问dbModel然后我们在dbModel中添加该函数。 You must make sure the parameters and the parameter types match both the SQL and the C# function exactly. 您必须确保参数和参数类型完全匹配SQL和C#函数。

public class EmContext : DbContext
{    
    ...

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        //Register a convention so we can load our function
        modelBuilder.Conventions.Add(new AddMedianFunction());

        ...

    }

    public class AddMedianFunction : IConvention, IStoreModelConvention<EntityContainer>
    {
        public void Apply(EntityContainer item, DbModel dbModel)
        {
            //these parameter types need to match both the database method and the C# method for EF to link
            var edmFloatType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Single);

            //CollectionType constructor is internal making it impossible to get a collection type. 
            //We resort to reflection instantiation.
            var edmFloatListType = CreateInstance<CollectionType>(edmFloatType);

            var medianfunction = EdmFunction.Create("Median", "dbo", DataSpace.SSpace, new EdmFunctionPayload
            {
                ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion,
                IsComposable = true,
                IsAggregate = true,
                Schema = "dbo",
                ReturnParameters = new[]
                {
                    FunctionParameter.Create("ReturnType", edmFloatType, ParameterMode.ReturnValue)
                },
                Parameters = new[]
                {
                    FunctionParameter.Create("input", edmFloatListType, ParameterMode.In),
                }
            }, null);

            dbModel.StoreModel.AddItem(medianfunction);
            dbModel.Compile();       
        }

        public static T CreateInstance<T>(params object[] args)
        {
            var type = typeof(T);
            var instance = type.Assembly.CreateInstance(
                type.FullName, false,
                BindingFlags.Instance | BindingFlags.NonPublic,
                null, args, null, null);
            return (T)instance;
        }
    }
}

With all that in place you should just be able to call your function as expected 有了这一切,您应该能够按预期调用您的功能

paymentsTable
    .GroupBy(x=>x.CustomerId)
    .Select(new{
            Median = MySqlFunctions.Median(x.Select(g=>g.Amount))
    });

Note: I am already assume you have loaded your SqlClr function which I have not covered here 注意:我已经假设你已经加载了我在这里没有涉及的SqlClr函数

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 首先使用Entity Framework 6.1代码保留和检索序列化实体属性 - Persisting and retrieving serialized entity property with Entity Framework 6.1 code first 首先创建可选的多对多映射实体框架6.1代码 - Creating Optional Many To Many Mapping Entity Framework 6.1 Code First 实体框架6.1代码优先中不需要的外键约束 - Unwanted Foreign Key Constraint in Entity Framework 6.1 Code First 实体框架6.1代码优先TPH / TPT混合映射问题 - Entity Framework 6.1 Code First TPH/TPT hybrid mapping issue 无法使用Code First Entity Framework 6.1更新数据库 - Not able to update-database with Code First Entity Framework 6.1 实体框架6.1-代码优先-参考属性未正确加载 - Entity Framework 6.1 - code first - reference properties not loading correctly 在Entity Framework 6.1中添加列 - 多对多 - 代码优先 - Adding column in Entity Framework 6.1 - Many-To-Many - Code First 如何首先使此基类与Entity Framework 6.1代码一起使用 - How to make this base class work with Entity Framework 6.1 code first 实体框架6.1代码优先MySql实体跟踪在上下文中持续存在 - Entity Framework 6.1 Code First MySql entity tracking persists across context 实体框架6.1-单个实体上的代码优先一对多:不能将PK映射到FK吗? - Entity Framework 6.1 - Code-First One-to-Many on single entity: Can't map PK to FK?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM