[英]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.