5

Tôi đang phát triển một dịch vụ bằng WebApi2 và EntityFramework6. Tôi có một SQLServer DB kế thừa mà dịch vụ của tôi phải làm việc.Cách gọi thủ tục lưu sẵn từ EntityFramework 6 với tham số 'hierarchyid'

DB đó đang sử dụng rất nhiều loại dữ liệu 'phân cấp' và loại này được sử dụng nội bộ trong các thủ tục được lưu trữ của DB.

Có vẻ như EF6 không hỗ trợ kiểu dữ liệu 'phân cấp', vì vậy tôi đã sử dụng this fork để thêm hỗ trợ cho 'hierarchyid'.

Trong khi quá trình truy xuất từ ​​DB hoạt động tốt với kiểu 'hierarchyid', thì vấn đề của tôi là với các thủ tục được lưu trữ cần một tham số 'hierarchyid'.

Các thủ tục lưu trữ trông như thế này:

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

Mã của tôi khách hàng để gọi stored procedure này trông như thế này:

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

Nhưng tiếc là kêu gọi truy vấn này ném một ngoại lệ mà nói:

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. 

Bất kỳ ý tưởng nào về cách tôi có thể thực hiện công việc này?

+0

Bắn trong bóng tối ở đây ... Bạn có thể thay đổi Sproc để ngoại trừ một Nvarchar và sau đó đúc giá trị cho một hirachyId nội bộ trong Sproc? – SimonGates

Trả lời

3

Thật không may, MetaType.GetMetaTypeFromValue không cho phép thêm loại (tất cả các loại được hỗ trợ đều được mã hóa cứng). Tôi nghĩ bạn có thể hoàn thành mục tiêu của mình với các thông số và chuyển đổi nvarchar.

Trong mã # 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()); 

Trong thủ tục của bạn (tôi đã viết một số mã kiểm tra bên trong):

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 

Ngoài ra, bạn có thể thử sử dụng Microsoft.SqlServer.Types và loại SqlHierarchyId như thế này:

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

Nhưng, tôi nghĩ, đây là hướng sai.

1

Câu trả lời của Oleg là chính xác, hệ thống phân cấp vẫn chưa được tích hợp với EF rất tốt và bạn nên hoạt động với chuỗi trong .net. Dưới đây là một cách tiếp cận nhiều hơn nữa mà đã được sử dụng từ những ngày đầu tiên của HierarchyId datatype:

Stored Procedure:

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

END

Trong một ứng dụng mà bạn đang thêm một lớp học phần cho bối cảnh dữ liệu EF của bạn với cuộc gọi SP sử dụng ADO.NET cũ đơn giản.Có lẽ bạn sẽ viết cách khác này hoặc sử dụng Dapper thay vào đó, nhưng ý tưởng chính ở đây là đi qua tham số như chuỗi SQL Server, và nó sẽ chuyển sang HierarchyId ngầm:

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

Sau đó gọi phương thức này như bất kỳ khác được lưu trữ thủ tục sử dụng bối cảnh EF của bạn:

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

UPD: đây là cách các phương pháp khuyến nông cho gọi thủ tục di sản HierarchyId sẽ trông giống như với Dapper (như đối với tôi nó trông tốt hơn nhiều so với ADO.NET đồng bằng):

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"); 
      } 
     } 
Các vấn đề liên quan