如何在EntityFramework 6中使用'hierarchyid'参数调用存储过程

5
我正在使用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? - SimonGates
2个回答

3

很不幸,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" };

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

1

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();

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

END

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/");
}

更新:以下是使用Dapper编写遗留的HierarchyId过程调用的扩展方法的代码(我认为这比纯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");
            }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接