How can I get NHibernate to make a multicolumn table-valued parameter?

688 Views Asked by At

I'd like to pass a two-column table-valued parameter (TVP) to an ISQLQuery:

var sql = @"
    INSERT INTO MovieRatings (PersonID, MovieID, Score)
    SELECT :personID, o.movieID, o.score
    FROM :scoreObject o";
var query = session.CreateSQLQuery(sql);
query.SetInt32("personID", fred.ID);
query.SetParameterList("scoreObject", fredsRatings.Select(r => new {
        movieID = r.Movie.ID,
        score = r.Score
    }).ToArray() /*, Optional IType hint here */);
query.ExecuteUpdate();

When I do this (or try to hint the IType as NHibernateUtil.Object or NHibernateUtil.Class), NHibernate complains:

NHibernate.HibernateException : Could not determine a type for class: <>f__AnonymousType5`2[[System.Int32],[System.String]]

It also complains if I try to make a struct with properties for movieID and score and make an array of those instead of anonymous objects. The only thing that changes is the type it "could not determine."

Microsoft seems to say that a TVP can have whatever columns you want:

Passing a Table-Valued Parameter to a Parameterized SQL Statement

The following example demonstrates how to insert data into the dbo.Categories table by using an INSERT statement with a SELECT subquery that has a table-valued parameter as the data source. When passing a table-valued parameter to a parameterized SQL statement, you must specify a type name for the table-valued parameter by using the new TypeName property of a SqlParameter. This TypeName must match the name of a compatible type previously created on the server. The code in this example uses the TypeName property to reference the type structure defined in dbo.CategoryTableType.

// Assumes connection is an open SqlConnection.  
using (connection)  
{  
    // Create a DataTable with the modified rows.  
    DataTable addedCategories = CategoriesDataTable.GetChanges(  
        DataRowState.Added);  
    
    // Define the INSERT-SELECT statement.  
    string sqlInsert =   
        "INSERT INTO dbo.Categories (CategoryID, CategoryName)"  
        + " SELECT nc.CategoryID, nc.CategoryName"  
        + " FROM @tvpNewCategories AS nc;"  
    
    // Configure the command and parameter.  
    SqlCommand insertCommand = new SqlCommand(  
        sqlInsert, connection);  
    SqlParameter tvpParam = insertCommand.Parameters.AddWithValue(  
        "@tvpNewCategories", addedCategories);  
    tvpParam.SqlDbType = SqlDbType.Structured;  
    tvpParam.TypeName = "dbo.CategoryTableType";  
    
    // Execute the command.  
    insertCommand.ExecuteNonQuery();  
}

Is there a way to convince NHibernate to make a TVP with named columns? I'd like to avoid writing my own IType.

1

There are 1 best solutions below

0
On

Remondo has a novel solution to this problem: Add the TVP definition to the transaction with a SqlParameter from the Microsoft library.

Edit to follow with sample code for the query in the question.