5

Zajmuję się tworzeniem usługi przy użyciu WebApi2 i EntityFramework6. Mam starszą SQLServer DB, z którą moja usługa musi pracować.Jak wywołać procedurę przechowywaną z EntityFramework 6 z parametrem "hierarchyid"

Ten DB używa silnie typu danych "hierarchyid", a ten typ jest używany wewnętrznie w procedurach przechowywanych bazy danych.

Wygląda na to, że EF6 nie obsługuje typu danych "hierarchyid", więc użyłem this fork, który dodaje obsługę "hierarchyid".

Podczas pobierania z DB działa wspaniale z typem "hierarchyid", moim problemem jest z procedur przechowywanych, które potrzebują "hierarchyid" jako parametr.

Procedura przechowywana wygląda następująco:

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

Mój kod klienta na wywoływanie tej procedury przechowywanej wygląda następująco:

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

ale niestety wywołanie tego zapytania zgłasza wyjątek, który mówi:

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. 

Jakieś pomysły na to, jak to działa?

+0

Strzał w ciemność tutaj ... Czy możesz zmienić Sproca, z wyjątkiem Nvarchar, a następnie odrzucić wartość do hirachyId wewnętrznie w Sprocu? – SimonGates

Odpowiedz

3

Niestety, MetaType.GetMetaTypeFromValue nie pozwala na dodawanie typów (wszystkie obsługiwane typy są zakodowane na stałe). Myślę, że możesz osiągnąć swój cel dzięki nvarchar parametrom i konwersji.

W swojej kodu 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()); 

W swojej procedury (pisałem jakiś kod testowy wewnątrz):

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 

Ponadto, można spróbować użyć Microsoft.SqlServer.Types i typ SqlHierarchyId takiego:

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

Ale myślę, że to niewłaściwy kierunek.

1

Odpowiedź Olega jest poprawna, hierarchyid nadal nie jest zintegrowany z EF, i powinieneś operować ciągami w .net. Oto jeszcze jeden podejście, którego użyto od pierwszych dni hierarchyid typu danych:

procedury przechowywanej:

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

W aplikacji dodawanego częściowej klasy dla kontekstu danych EF z wywołanie SP za pomocą zwykłego starego ADO.NET.Prawdopodobnie będziesz pisać to w inny sposób lub użyć Dapper zamiast, ale główną ideą jest tu przechodzącej parametr jako ciąg SQL Server i będzie konwertować do hierarchyid domyśle:

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

Następnie wywołać tę metodę, jak każdy inny przechowywane Procedura korzystania z kontekstu EF

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

UPD: oto jak metoda rozszerzenie dla procedury spuścizna hierarchyid rozmowy będzie wyglądać z Dapper (jak dla mnie to wygląda o wiele lepiej niż zwykły 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"); 
      } 
     }