【问题标题】:Calling scalar function from c# using Entity Framework 4.0 / .edmx使用 Entity Framework 4.0 / .edmx 从 c# 调用标量函数
【发布时间】:2012-12-31 08:49:43
【问题描述】:

我想将我的标量函数映射到我的 .edmx,但它失败了。我右键单击我的实体框架映射,然后从数据库中选择更新模型。它出现在模型浏览器的存储过程文件夹中。

但是,当我想将其添加到模型浏览器中的Function Imports 文件夹时,消息scalar function does not appear显示在下拉列表中。有人可以帮我吗?

我可以使用旧的方式调用标量函数,例如:

dbContext.ExecuteStoreQuery<DateTime?>(
"SELECT dbo.getMinActualLoadDate ({0}, {1}, {2}) AS MyResult", 
LoadPkid, LoadFkStartLoc, TripSheetPkid).First();

但这不是最好的方法。我的经理希望我找到一种方法能够将标量函数放在“函数导入”文件夹中,这样我就可以使用以下代码而不是之前的代码来调用标量函数:

dbContext.ExecuteFunction("getMinActualLoadDate ", paramList);

我尝试添加图像来显示我的意思,但由于我的声誉仍然很低,我无法这样做。不过图片可以在这里找到:http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/756865e5-ff25-4f5f-aad8-fed9d741c05d

谢谢。

【问题讨论】:

标签: c# entity-framework-4 user-defined-functions


【解决方案1】:

我也遇到过同样的问题。这是我发现自己足够合适的解决方案(在 EF5 中测试,但也应该在 EF4 中工作):

不支持开箱即用的映射标量值函数,但您可以直接执行它们。

您也可以编辑 edmx 文件以使 edmx 为标量值函数生成正确的方法,但如果您将模型与数据库同步,它将被删除。

自己编写标量值函数实现:

string sqlQuery = "SELECT [dbo].[CountMeals] ({0})";
Object[] parameters = { 1 };
int activityCount = db.Database.SqlQuery<int>(sqlQuery, parameters).FirstOrDefault();

或编辑 edmx 并添加 Xml 用于标量值函数的自定义映射:

<Function Name="CountActivities" Aggregate="false" BuiltIn="false"    NiladicFunction="false" IsComposable="false"   ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
    <CommandText>
        SELECT [dbo].[CountActivities] (@personId)
    </CommandText>
    <Parameter Name="personId" Type="int" Mode="In" />
</Function>

此信息在此blog post中找到

【讨论】:

    【解决方案2】:

    这是我对这个问题的解决方案,这几乎正是您的经理所要求的……尽管晚了 18 个月。

    作为普通方法:

        /// <summary>
        /// Calls a given Sql function and returns a singular value
        /// </summary>
        /// <param name="db">Current DbContext instance</param>
        /// <typeparam name="T">CLR Type</typeparam>
        /// <param name="sql">Sql function</param>
        /// <param name="parameters">Sql function parameters</param>
        /// <param name="schema">Owning schema</param>
        /// <returns>Value of T</returns>
        public T SqlScalarResult<T>(DbContext db, 
                                    string sql, 
                                    SqlParameter[] parameters,
                                    string schema = "dbo") {
    
            if (string.IsNullOrEmpty(sql)) {
                throw new ArgumentException("function");
            }
    
            if (parameters == null || parameters.Length == 0) {
                throw new ArgumentException("parameters");
            }
    
            if (string.IsNullOrEmpty(schema)) {
                throw new ArgumentException("schema");
            }
    
            string cmdText =
                $@"SELECT {schema}.{sql}({string.Join(",",
                    parameters.Select(p => "@" + p.ParameterName).ToList())});";
    
            // ReSharper disable once CoVariantArrayConversion
            return db.Database.SqlQuery<T>(cmdText, parameters).FirstOrDefault();
    
        }
    
    }
    

    作为 EF 的扩展方法:

    namespace System.Data.Entity {
    
        public static class DatabaseExtensions {
    
            /// <summary>
            /// Calls a given Sql function and returns a singular value
            /// </summary>
            /// <param name="db">Current DbContext instance</param>
            /// <typeparam name="T">CLR Type</typeparam>
            /// <param name="sql">Sql function</param>
            /// <param name="parameters">Sql function parameters</param>
            /// <param name="schema">Owning schema</param>
            /// <returns>Value of T</returns>
            public static T SqlScalarResult<T>(this Database db, 
                                               string sql, 
                                               SqlParameter[] parameters,
                                               string schema = "dbo") {
    
                if (string.IsNullOrEmpty(sql)) {
                    throw new ArgumentException("sql");
                }
    
                if (parameters == null || parameters.Length == 0) {
                    throw new ArgumentException("parameters");
                }
    
                if (string.IsNullOrEmpty(schema)) {
                    throw new ArgumentException("schema");
                }
    
                string cmdText =
                    $@"SELECT {schema}.{sql}({string.Join(",", 
                        parameters.Select(p => "@" + p.ParameterName).ToList())});";
    
                // ReSharper disable once CoVariantArrayConversion
                return db.SqlQuery<T>(cmdText, parameters).FirstOrDefault();
    
            }
    
        }
    
    }
    

    虽然这里不会冒烟,但我建议在认真使用之前进行单元测试。

    【讨论】:

    • 您不需要更改“SELECT dbo.{0}({1});”到“SELECT * FROM dbo.{0}({1});” ??或者你只是按照你的方式工作。只是想知道。
    • 是的,我已经在生产项目中使用了这种扩展方法,并且括号是有意的..尽管您也可以在函数名称和参数之间留一个空格。
    • 这应该被标记为接受的答案。此方法效果很好,并且还利用了 SqlParameters。
    • 如果请求的函数在 SQL Server 上不存在怎么办?在我看来,此代码尚未“准备好”用于生产目的。当然是一个很好的建议,并准备开始使用:-)
    • @RobAngelier 我看到你指出生产“准备就绪”。但是,EF 团队自己的扩展方法似乎也没有处理任何远程异常:github.com/aspnet/EntityFramework/blob/…。如果在此处捕获异常,您希望此方法返回什么?
    【解决方案3】:

    我猜你错过了Edit Function Import 对话框,你可以在其中生成复杂类型。尝试探索。

    如果您已成功创建scalars,您现在可以像这样导航

    using (var con = new DatabaseEntities())
    {
       long? invoiceNo = con.sp_GetInvoiceMaxNumber(code.Length + 2).First();
       ....
    }
    

    【讨论】:

    • 嗨 spajce,基本上这就是我打算做的。但是我创建的标量函数名称在存储过程名称下拉列表中不存在(尽管我在模型浏览器中添加了标量函数)。如果是存储过程,则出现在 DDL 中,但如果是表/标量函数,则不会出现在 DDL 中。因此,我很难按照我在第一篇文章中提到的方式调用我的标量函数。谢谢
    • EF 6.1.2 中仍然存在问题social.msdn.microsoft.com/Forums/en-US/…
    【解决方案4】:

    唯一的解决方案是将函数标量类型转换为表中具有单个值的表值类型,请参见代码示例。

    EDMX XML中不需要做任何改动,请修改SQL函数

    原来的标量函数不起作用

    CREATE FUNCTION [dbo].[GetSha256]
    (
        -- Add the parameters for the function here
        @str nvarchar(max)
    )
    RETURNS VARBINARY(32)
    AS
    BEGIN
        RETURN ( SELECT * FROM HASHBYTES('SHA2_256', @str) AS HASH256 );
    END -- this doesn't work.
    

    标量函数 -> 转换为表值函数,它可以工作

    CREATE FUNCTION [dbo].[GetSha2561]
    (
        -- Add the parameters for the function here
        @str nvarchar(max)
    )
    RETURNS  @returnList TABLE (CODE varbinary(32))
    AS
    BEGIN
    
        INSERT INTO @returnList
        SELECT HASHBYTES('SHA2_256', @str);
    
        RETURN; -- This one works like a charm.
    
    END
    

    Edmx 截图

    【讨论】:

      【解决方案5】:

      页面代码:

      protected void Page_Load(object sender, EventArgs e)
      {
          if (!IsPostBack)
          {
              using (MayEntities context = new MayEntities())
              {
                  string str = context.Database.SqlQuery<string>("select dbo.HeyYou()").Single().ToString();
                  Response.Write(str); //output:'Hey this works'
              }
          }
      }
      

      标量函数:

      CREATE FUNCTION [dbo].[HeyYou] ()
      RETURNS varchar(20)
      AS
      BEGIN
          RETURN 'Hey this works'
      END
      go
      

      【讨论】:

        猜你喜欢
        • 2020-03-01
        • 2018-12-11
        • 1970-01-01
        • 2015-07-30
        • 1970-01-01
        • 1970-01-01
        • 2020-08-18
        • 2016-02-13
        • 2011-06-22
        相关资源
        最近更新 更多