Je souhaite transmettre une collection d'ID à une procédure stockée qui sera mappée à l'aide de NHibernate. Cette technique a été introduite dans Sql Server 2008 (plus d'infos ici =>Table-Valued Parameters). Je ne veux tout simplement pas passer plusieurs ID dans un paramètre nvarchar
, puis couper sa valeur du côté de SQL Server.Est-il possible d'utiliser `SqlDbType.Structured` pour passer des paramètres de table dans NHibernate?
Répondre
Ma première idée ad hoc était de mettre en œuvre la mienne IType
.
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...
}
Plus de méthodes sont mises en œuvre; un throw new NotImplementedException();
est dans tout le reste. Ensuite, j'ai créé une extension simple pour 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);
}
}
utilisation typique pour moi
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, mais qu'est-ce qu'un "IntTable"
? C'est le nom du type SQL créé pour transmettre les arguments de la valeur de la table.
CREATE TYPE IntTable AS TABLE
(
ID INT
);
Et some_sp
pourrait être comme
CREATE PROCEDURE some_sp
@id IntTable READONLY,
@par1 ...
AS
BEGIN
...
END
Il fonctionne uniquement avec Sql Server 2008 et bien sûr dans cette mise en œuvre particulière avec une seule colonne DataTable
.
var dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
C'est uniquement un POC, ce n'est pas une solution complète, mais cela fonctionne et pourrait être utile lors de la personnalisation. Si quelqu'un connaît une solution meilleure/plus courte, faites-le nous savoir.
quelqu'un sait à propos de deux colonnes DataTable? – rcarvalhoxavier
Pawel Kupis - allez-vous regarder cette question similaire mais différente: http://stackoverflow.com/questions/42228253/sqldbtype-structured-to-pass-table-valued-parameters-in-nhibernate-to-select-wit – Remy
Vous pouvez transmettre des collections de valeurs sans les tracas.
Exemple:
var ids = new[] {1, 2, 3};
var query = session.CreateQuery("from Foo where id in (:ids)");
query.SetParameterList("ids", ids);
Hibernate va créer un paramètre pour chaque élément.
N'y at-il pas une limitation de 2100 paramètres? D'ailleurs, je ne pense pas que ce soit une solution pour mon besoin :) Ou est-ce? – IamDeveloper
Eh bien, combien avez-vous besoin ?? C'est une approche alternative. –
cela ne résout pas le problème de paramètre 2100 –
D'autres personnes ont-elles mis en œuvre cette implémentation? J'ai besoin de passer plusieurs lignes de plusieurs colonnes (5 entiers pour être exact) en utilisant cette méthode – J'ai défini mon type structuré SQL sous-jacent et ai changé le ColumnSpan
pour retourner 5. La seule autre différence que je peux voir est que je ne retourne aucun valeurs dans mon exécution de procédure stockée. Il ne fait que consommer les valeurs qui lui sont fournies. Malheureusement, cela déclenche un message "Impossible d'exécuter une requête de manipulation en bloc native". Les exceptions internes se plaignent des erreurs "Index Out of Range". Je ne suis pas vraiment sûr de savoir où aller, car le sujet des types structurés/paramètres de valeur de table avec NHibernate semble assez ésotérique pour que je continue à ce poste Stack Overflow.
attacher un profileur (nhProf par exemple) ou sql server profiler et voir quelle requête est créée en utilisant votre code. Pouvez-vous poster une réelle exception? – IamDeveloper
Une solution plus simple que la réponse acceptée serait d'utiliser ADO.NET. NHibernate permet aux utilisateurs d'inscrire IDbCommands
dans les transactions 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();
}
Puis-je attacher le 'paramètre' à un' NHibernate.ISQLQuery', plutôt que d'utiliser ADO pour l'ensemble de la requête? [Ref] (https://stackoverflow.com/q/46244598/241211) – Michael
Pas que ce soit une mauvaise question, mais pourriez-vous expliquer pourquoi vous voulez faire cela? – Kendrick
Can SqlDBType.Structured être utilisé pour les paramètres de valeur de table? –