Sql server 是否可以使用`SqlDbType.Structured`在NHibernate中传递表值参数?

Sql server 是否可以使用`SqlDbType.Structured`在NHibernate中传递表值参数?,sql-server,nhibernate,table-valued-parameters,Sql Server,Nhibernate,Table Valued Parameters,我想将一组ID传递给一个存储过程,该存储过程将使用NHibernate进行映射。此技术是在Sql Server 2008中引入的(更多信息,请参见=>)。我只是不想在一个nvarchar参数中传递多个ID,然后在SQL Server端切掉它的值。您可以轻松地传递值集合 例如: varids=new[]{1,2,3}; var query=session.CreateQuery(“来自Foo,其中id位于(:id)”); SetParameterList(“ids”,ids); NHiberna

我想将一组ID传递给一个存储过程,该存储过程将使用NHibernate进行映射。此技术是在Sql Server 2008中引入的(更多信息,请参见=>)。我只是不想在一个
nvarchar
参数中传递多个ID,然后在SQL Server端切掉它的值。

您可以轻松地传递值集合

例如:

varids=new[]{1,2,3};
var query=session.CreateQuery(“来自Foo,其中id位于(:id)”);
SetParameterList(“ids”,ids);

NHibernate将为每个元素创建一个参数。

我的第一个临时想法是实现我自己的
IType

公共类Sql2008Structured:IType{
私有静态只读SqlType[]x=new[]{new SqlType(DbType.Object)};
公共SqlType[]SqlTypes(NHibernate.Engine.IMapping映射){
返回x;
}
公共集合类型{
获取{return true;}
}
public int GetColumnSpan(NHibernate.Engine.IMapping映射){
返回1;
}
public void NullSafeSet(dbst命令、对象值、int索引、NHibernate.Engine.ISessionImplementor会话){
var s=st作为SqlCommand;
如果(s!=null){
s、 参数[index].SqlDbType=SqlDbType.Structured;
s、 参数[index].TypeName=“IntTable”;
s、 参数[索引]。值=值;
}
否则{
抛出新的NotImplementedException();
}
}
#区域IType成员。。。
#区域汇编程序成员。。。
}
不再实施任何方法;a
抛出新的NotImplementedException()包含在所有其他内容中。接下来,我为
IQuery
创建了一个简单的扩展

公共静态类结构{
私有静态只读Sql2008Structured=新Sql2008Structured();
公共静态IQuery SetStructured(此IQuery查询、字符串名称、数据表dt){
返回query.SetParameter(名称,dt,结构化);
}
}
我的典型用法是

DataTable dt=。。。;
ISession s=。。。;
var l=s.CreateSQLQuery(“EXEC some_sp@id=:id,@par1=:par1”)
.SetStructured(“id”,dt)
.SetParameter(“par1”,…)
.SetResultTransformer(Transformers.AliasToBean())
.List();
好的,但是什么是
“IntTable”
?它是为传递表值参数而创建的SQL类型的名称

将类型IntTable创建为TABLE
(
ID INT
);
某些sp
可能是

CREATE PROCEDURE some\u sp
@id IntTable READONLY,
@第1部分。。。
作为
开始
...
结束
当然,它只适用于Sql Server 2008,在这个特定的实现中,它只适用于一列
DataTable

var dt=new DataTable();
添加(“ID”,typeof(int));

它只是POC,不是一个完整的解决方案,但它可以工作,并且在定制时可能有用。如果有人知道更好/更短的解决方案,请告诉我们。

一个比公认答案更简单的解决方案是使用ADO.NET。NHibernate允许用户将
IDbCommands
登记到NHibernate事务中

DataTable myIntsDataTable=newdatatable();
添加(“ID”,typeof(int));
// ... 向数据表添加行
ISession session=sessionFactory.GetSession();
使用(ITransaction transaction=session.BeginTransaction())
{
IDbCommand=newSQLCommand(“StoredProcedureName”);
command.Connection=session.Connection;
command.CommandType=CommandType.storedProcess;
var参数=新的SqlParameter();
parameter.ParameterName=“IntTable”;
parameter.SqlDbType=SqlDbType.Structured;
parameter.Value=MyIntsDataable;
command.Parameters.Add(参数);
会话.事务.登记(命令);
command.ExecuteNonQuery();
}

<代码> > p>对于我的情况,需要在打开事务的中间调用存储过程。 如果存在打开的事务,则此代码有效,因为它自动重用NHibernate会话的现有事务:

NHibernateSession.GetNamedQuery(“SaveStoredProc”)
.SetInt64(“spData”,500)
.ExecuteUpdate();
但是,对于我的新存储过程,参数并不像
Int64
那么简单。它是一个表值参数(用户定义的表类型) 我的问题是我找不到合适的集合函数。 我尝试了
SetParameter(“spData”,tvpbj)
,但它返回了以下错误:

无法确定类的类型:

无论如何,经过一些尝试和错误,下面的方法似乎是可行的。
inclist()
函数是这种方法的关键。它基本上告诉
SQLCommand
使用现有事务。没有它,就会有一个错误的说法

ExecuteNonQuery
要求命令在 分配给命令的连接位于挂起的本地事务中

使用(SqlCommand cmd=NHibernateSession.Connection.CreateCommand()作为SqlCommand)
{
cmd.CommandText=“MyStoredProc”;
NHibernateSession.Transaction.Enlist(cmd);//因为有一个挂起的事务
cmd.CommandType=CommandType.storedProcess;
Add(新的SqlParameter(“@wiData”,SqlDbType.Structured){Value=wisnsqlslist});
int impacted=cmd.ExecuteNonQuery();
}
由于我使用的是
SqlParameter
类,因此可以使用
SqlDbType.Structured

这是分配
wiSnList
的函数:

private IEnumerable TransformWisnListSQL(IList wiSnList)
{
如果(wiSnList==null)
{
屈服断裂;
}
var schema=new[]
{
新的SqlMetaData(“OriginalId”,SqlDbType.BigInt),//0
新的SqlMetaData(“ReportId”,SqlDbType.BigInt),