tags:

views:

69

answers:

2

Is it somehow possible to use SqlDbType.Structured in NHibernate ?

Explanation: I want to pass collection of ids to stored procedure that will be mapped using nhibernate. This technique was introduced in Sql Server 2008 ( read ). I just don't want to pass (again) multiple ids as nvarchar and then chop in in sql server side.

A: 

You can pass collections of values without the hassle.

Example:

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

NHibernate will create a parameter for each element.

Diego Mijelshon
Isn't there limitation of 2100 parameters? Besides, I don't think it's a solution for my need :) Or is it?
StupidDeveloper
Well, how many do you need?? It's an alternative approach.
Diego Mijelshon
+1  A: 

My first, ad hoc, idea was to implement my own 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...
}

No more methods are implemented, a throw new NotImplementedException(); code is in all the rest. Next simple extension for IQuery was created.

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

Typical usage for me is

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, but what the "IntTable" is? It's a name of SQL type created to pass table value arguments.

CREATE TYPE IntTable AS TABLE
(
    ID INT
);

And some_sp could be like

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

It works with Sql Server 2008 only of course and in this particular implementation with single column DataTable.

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

It's POC only, not a complete solution, but it works and might be useful when customized. If someone knows a better/shorter solution let us know.

Pawel Kupis
holy shit, rządzisz stary! :)
StupidDeveloper