2010-09-13 49 views
12

Quiero pasar una colección de identificadores a un procedimiento almacenado que se asignará utilizando NHibernate. Esta técnica se introdujo en Sql Server 2008 (más información aquí =>Table-Valued Parameters). Simplemente no quiero pasar múltiples identificadores dentro de un parámetro nvarchar y luego cortar su valor en el lado del Servidor SQL.¿Es posible utilizar `SqlDbType.Structured` para pasar los parámetros con valores de tabla en NHibernate?

+0

No es una pregunta mala, pero ¿podría ampliar por qué quiere hacer esto? – Kendrick

+1

¿Se puede usar SqlDBType.Structured para los parámetros de valores de tabla? –

Respuesta

28

Mi primera idea, ad hoc, fue implementar mi propio IType. se implementan

public class Sql2008Structured : IType { 
    private static readonly SqlType[] x = new[] { new SqlType(DbType.Object) }; 
    public SqlType[] SqlTypes(NHibernate.Engine.IMapping mapping) { 
     return x; 
    } 

    public bool IsCollectionType { 
     get { return true; } 
    } 

    public int GetColumnSpan(NHibernate.Engine.IMapping mapping) { 
     return 1; 
    } 

    public void NullSafeSet(IDbCommand st, object value, int index, NHibernate.Engine.ISessionImplementor session) { 
     var s = st as SqlCommand; 
     if (s != null) { 
      s.Parameters[index].SqlDbType = SqlDbType.Structured; 
      s.Parameters[index].TypeName = "IntTable"; 
      s.Parameters[index].Value = value; 
     } 
     else { 
      throw new NotImplementedException(); 
     } 
    } 

    #region IType Members... 
    #region ICacheAssembler Members... 
} 

métodos más; un throw new NotImplementedException(); está en el resto. A continuación, creé una extensión simple para IQuery.

public static class StructuredExtensions { 
    private static readonly Sql2008Structured structured = new Sql2008Structured(); 

    public static IQuery SetStructured(this IQuery query, string name, DataTable dt) { 
     return query.SetParameter(name, dt, structured); 
    } 
} 

uso típico para mí es

DataTable dt = ...; 
ISession s = ...; 
var l = s.CreateSQLQuery("EXEC some_sp @id = :id, @par1 = :par1") 
      .SetStructured("id", dt) 
      .SetParameter("par1", ...) 
      .SetResultTransformer(Transformers.AliasToBean<SomeEntity>()) 
      .List<SomeEntity>(); 

Ok, pero lo que es un "IntTable"? Es el nombre del tipo de SQL creado para pasar argumentos de valor de tabla.

CREATE TYPE IntTable AS TABLE 
(
    ID INT 
); 

Y some_sp podría ser como

CREATE PROCEDURE some_sp 
    @id IntTable READONLY, 
    @par1 ... 
AS 
BEGIN 
... 
END 

Sólo funciona con SQL Server 2008 y, por supuesto, en esta implementación particular, con una sola columna DataTable.

var dt = new DataTable(); 
dt.Columns.Add("ID", typeof(int)); 

Solo es POC, no es una solución completa, pero funciona y puede ser útil cuando se personaliza. Si alguien conoce una solución mejor/más corta, háganoslo saber.

+0

alguien sabe acerca de dos columnas DataTable? – rcarvalhoxavier

+0

Pawel Kupis - ¿Puedes ver esta pregunta similar pero diferente: http://stackoverflow.com/questions/42228253/sqldbtype-structured-to-pass-table-valued-parameters-in-nhibernate-to-select-wit – Remy

2

Puede pasar colecciones de valores sin la molestia.

Ejemplo:

var ids = new[] {1, 2, 3}; 
var query = session.CreateQuery("from Foo where id in (:ids)"); 
query.SetParameterList("ids", ids); 

NHibernate creará un parámetro para cada elemento.

+2

¿No hay una limitación de 2100 parámetros? Además, no creo que sea una solución para mi necesidad :) ¿O sí? – IamDeveloper

+0

Bueno, ¿cuántos necesitas? Es un enfoque alternativo. –

+2

esto no resuelve el problema del parámetro 2100 –

1

¿Han conseguido esta implementación otros que funcionen? Necesito pasar varias filas de múltiples columnas (5 enteros para ser exactos) usando este método – Definí mi tipo subyacente de SQL estructurado y cambié el ColumnSpan para devolver 5. La única otra diferencia que puedo ver es que no devuelvo ninguna valores en mi ejecución de procedimiento almacenado. Simplemente está consumiendo los valores que se le suministran.

DataTable dt = ...; 

var query = session.CreateSqlQuery("EXEC foo_proc_here @data = :data") 
    .SetStructured("data", dt) 
    .ExecuteUpdate(); 

Desafortunadamente, esto arroja un mensaje "No se pudo ejecutar la consulta de manipulación masiva nativa". Las excepciones internas se quejan de errores de "Índice fuera de rango".

No estoy muy seguro de a dónde recurrir ahora, ya que el tema de los parámetros de tipos estructurados/valor de tabla con NHibernate parece lo suficientemente esotérico como para continuar terminando en esta misma publicación de Stack Overflow.

+0

adjunte un generador de perfiles (nhProf por ejemplo) o servidor SQL y ver qué consulta se crea con su código. ¿Puedes publicar una excepción real? – IamDeveloper

2

Una solución más simple que la aceptada sería utilizar ADO.NET. NHibernate permite a los usuarios incluir IDbCommands en las transacciones de NHibernate.

DataTable myIntsDataTable = new DataTable(); 
myIntsDataTable.Columns.Add("ID", typeof(int)); 

// ... Add rows to DataTable 
ISession session = sessionFactory.GetSession(); 
using(ITransaction transaction = session.BeginTransaction()) 
{ 
    IDbCommand command = new SqlCommand("StoredProcedureName"); 
    command.Connection = session.Connection; 
    command.CommandType = CommandType.StoredProcedure; 
    var parameter = new SqlParameter(); 
    parameter.ParameterName = "IntTable"; 
    parameter.SqlDbType = SqlDbType.Structured; 
    parameter.Value = myIntsDataTable; 
    command.Parameters.Add(parameter);    
    session.Transaction.Enlist(command); 
    command.ExecuteNonQuery(); 
} 
+0

¿Podría adjuntar el 'parámetro' a' NHibernate.ISQLQuery', en lugar de usar ADO para toda la consulta? [Ref] (https://stackoverflow.com/q/46244598/241211) – Michael

Cuestiones relacionadas