EF核心2.1中的问题映射标量函数

时间:2019-02-12 20:01:36

标签: sql-server entity-framework entity-framework-core

我正在尝试映射具有自定义模式的数据库的标量函数。这就是我在上下文中注册函数的方式:

 [DbFunction("ProjectMaterial_GetCostPrice","Project")]
 public static decimal ProjectMaterial_GetCostPrice (int ProjectMaterialID, decimal ExtCost)
 {
    return 0;
 }

我正在上下文的部分类中注册Scalar函数。这是数据库中标量函数的架构:

-- Select Project.ProjectDriver_GetCostPrice (5456921)

ALTER FUNCTION [Project].[ProjectMaterial_GetCostPrice] (@ProjectMaterialID int, @ExtCost money) 
    RETURNS MONEY
AS

我还根据文档建议更改了方法的正文:

throw new NotSupportedException();

它引发了异常,而不是调用了函数

这就是我调用该函数的方式:

 var newCostPrice= NsiteDBContext.ProjectMaterial_GetCostPrice(projectMaterial.ProjectMaterialId, projectMaterial.CostPrice.Value);

2 个答案:

答案 0 :(得分:1)

使用调用本身会引发异常,因为它实际上执行C#代码。建议抛出异常的原因正是这样,以避免意外使用,即通过直接调用它。该签名将由给定的LINQ提供程序解释并转换为适当的SQL语句。

为此,EF上下文需要知道如何使用,因此可能是某种方式

var items = await ctx.Materials.Select(c = > new {
   Material= c,
   CostPrice = ProjectMaterial_GetCostPrice(c.ProjectMaterialId, c.CostPrice.Value),
}).ToListAsync();

现在ctx对象在解析表达式树时将知道如何转换ProjectMaterial_GetCostPrice签名。

即使通过静态调用,在select语句之外执行操作也无法按预期进行,并且会抛出该异常(通知我们)。

答案 1 :(得分:0)

如上一个答案所述,您只能在LINQ查询中使用以这种方式定义的函数。 若要直接调用SQL标量值函数,应使用DbContext.Database.ExecuteSqlCommand方法对其进行定义。这样,它应该可以工作:

public decimal ProjectMaterial_GetCostPrice(int ProjectMaterialID, decimal ExtCost)
{
    System.Data.SqlClient.SqlParameter resultParam =
        new System.Data.SqlClient.SqlParameter
    {
        ParameterName = "@resultCost",
        SqlDbType = System.Data.SqlDbType.Money,
        Direction = System.Data.ParameterDirection.Output
    };
    System.Data.SqlClient.SqlParameter parMaterialID =
        new System.Data.SqlClient.SqlParameter("@MaterialID", ProjectMaterialID);
    SqlParameter parExtCost =
        new System.Data.SqlClient.SqlParameter("@ExtCost", ExtCost);
    Database.ExecuteSqlCommand(
        "select @resultCost = [Project].[ProjectMaterial_GetCostPrice](@MaterialID, @ExtCost);",
        resultParam, parMaterialID, parExtCost);
    return (decimal)resultParam.Value;
}