【问题标题】:How to call stored procedure from EntityFramework 6 with 'hierarchyid' parameter如何使用“hierarchyid”参数从 EntityFramework 6 调用存储过程
【发布时间】:2016-08-12 10:29:29
【问题描述】:

我正在使用 WebApi2 和 EntityFramework6 开发服务。 我有一个旧版 SQLServer 数据库,我的服务必须使用它。

该数据库大量使用“hierarchyid”数据类型,并且该类型在数据库的存储过程中内部使用。

好像 EF6 不支持“hierarchyid”数据类型,所以我使用了this fork,它增加了对“hierarchyid”的支持。

虽然从数据库中检索“hierarchyid”类型效果很好,但我的问题在于需要“hierarchyid”作为参数的存储过程。

存储过程如下所示:

CREATE PROCEDURE [dbo].[GetSomethingByNodeId]
    (
        @startingRoot HIERARCHYID
        ,@return HIERARCHYID OUTPUT
    )

我用于调用此存储过程的客户端代码如下所示:

var param1 = new SqlParameter("@startingRoot", new HierarchyId("/"));
var param2 = new SqlParameter{ ParameterName = "@return", Value = 0, Direction = ParameterDirection.Output };

var obj = context.Database.SqlQuery<HierarchyId>("GetSomethingByNodeId" @startingRoot, @return out", param1, param2).ToList();

但不幸的是,调用此查询会引发异常:

An unhandled exception of type 'System.ArgumentException' occurred in EntityFramework.SqlServer.dll

Additional information: No mapping exists from object type System.Data.Entity.Hierarchy.HierarchyId to a known managed provider native type.

有什么想法可以让我完成这项工作吗?

【问题讨论】:

  • 在这里暗中拍摄...您可以将 Sproc 更改为除 Nvarchar 之外,然后在 Sproc 内部将值转换为 hirachyId 吗?

标签: sql-server entity-framework stored-procedures entity-framework-6 hierarchyid


【解决方案1】:

不幸的是,MetaType.GetMetaTypeFromValue 不允许添加类型(所有支持的类型都是硬编码的)。 我认为您可以使用 nvarchar 参数和转换来实现您的目标。

在您的 C# 代码中:

var param1 = new SqlParameter("@startingRoot", "/1/");
var param2 = new SqlParameter { ParameterName = "@return", Value = "", Size = 1000, Direction = ParameterDirection.Output };

var ids = context.Database.SqlQuery<HierarchyId>("GetSomethingByNodeId @startingRoot, @return out", param1, param2).ToList();
var returnedId = new HierarchyId(param2.Value.ToString());

在你的程序中(我在里面写了一些测试代码):

CREATE PROCEDURE [dbo].[GetSomethingByNodeId]
    (
        @startingRoot nvarchar(max), @return nvarchar(max) OUTPUT
    )
as 
declare @hid hierarchyid = hierarchyid::Parse('/1/')
select @return = @hid.ToString()

declare @root hierarchyid = hierarchyid::Parse(@startingRoot)
select @root as field

另外,你可以尝试像这样使用Microsoft.SqlServer.Types 和 SqlHierarchyId 类型:

var sqlHierarchyId = SqlHierarchyId.Parse("/");
var param1 = new SqlParameter("@startingRoot", sqlHierarchyId) { UdtTypeName = "HierarchyId" };

但是,我认为,这是错误的方向。

【讨论】:

    【解决方案2】:

    Oleg 的回答是正确的,hierarchyid 还没有很好地集成到 EF 中,你应该在.net 中使用字符串进行操作。这是从 HierarchyId 数据类型的第一天开始使用的另一种方法:

    存储过程:

    CREATE PROCEDURE GetSomethingByNodeId
        @startingRoot hierarchyid, -- you don't need to use nvarchar here. String which will come from the application will be converted to hierarchyId implicitly
        @return nvarchar(500) OUTPUT
    AS
    BEGIN
    SELECT @return = @startingRoot.GetAncestor(1).ToString();
    

    结束

    在应用程序中,您正在使用普通的旧 ADO.NET 通过 SP 调用为 EF 数据上下文添加部分类。可能你会以其他方式编写或使用 Dapper,但这里的主要思想是将参数作为字符串传递给 SQL Server,它将隐式转换为 HierarchyId:

    public partial class TestEntities
    {
        public string GetSomethingByNodeId(string startingRoot)
        {
            using (var connection = new SqlConnection(this.Database.Connection.ConnectionString))
            {
                var command = new SqlCommand("GetSomethingByNodeId", connection);
                command.CommandType = CommandType.StoredProcedure;
                command.Parameters.AddWithValue("@startingRoot", startingRoot);
                var outParameter = new SqlParameter("@return", SqlDbType.NVarChar, 500);
                outParameter.Direction = ParameterDirection.Output;
                command.Parameters.Add(outParameter);
                connection.Open();
                command.ExecuteNonQuery();
    
                return outParameter.Value.ToString();
            }
        }
    }
    

    然后使用您的 EF 上下文将该方法作为任何其他存储过程调用:

    using (var context = new TestEntities())
    {
        var s = context.GetSomethingByNodeId("/1/1.3/");
    }
    

    UPD:这是旧 HierarchyId 过程调用的扩展方法在 Dapper 中的样子(对我来说,它看起来比普通的 ADO.NET 好得多):

    public string GetSomethingByNodeId(string startingRoot)
            {
                using (var connection = new SqlConnection(this.Database.Connection.ConnectionString))
                {
                    var parameters = new DynamicParameters();
                    parameters.Add("startingRoot", startingRoot);
                    parameters.Add("return", null, DbType.String, ParameterDirection.Output, 500);
                    connection.Open();
                    connection.Execute("GetSomethingByNodeId", parameters, commandType: CommandType.StoredProcedure);
    
                    return parameters.Get<string>("return");
                }
            }
    

    【讨论】:

      猜你喜欢
      • 2012-12-25
      • 2015-06-05
      • 1970-01-01
      • 2015-11-03
      • 1970-01-01
      • 2022-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多