cmd.AddTableParam - lobodava/artisan-orm GitHub Wiki

cmd.AddTableParam builds or accepts a DataTable, adds it as a SqlDbType.Structured parameter, and sets the SQL type name from DataTable.TableName. It configures the command; it does not execute SQL.

The corresponding user-defined table type must already exist. Match its schema-qualified name, column order, types, and nullability. A stored procedure declares its TVP parameter READONLY.

Choose the correct overload

Call Behavior
AddTableParam(name, dataTable) Uses the supplied table and its TableName
AddTableParam(name, users) Uses registered CreateDataTable and CreateDataRow methods
AddTableParam(name, users, tableName) Uses the same registered mapper and overrides the table name
AddTableParam(name, users, tableName, columnNames) Auto-maps the named readable properties into columns in the requested order

The table describes entity collections such as IEnumerable<User>. Integer ID lists have separate convenience overloads; see ID parameters.

Using a mapper

Inside a RepositoryBase subclass, this method configures and executes a procedure. Its int result is the procedure's T-SQL RETURN value, not an affected-row count:

public int SaveUsers(IList<User> users)
{
    return ExecuteCommand(cmd =>
    {
        cmd.UseProcedure("dbo.SaveUsers");
        cmd.AddTableParam("@Users", users);
    });
}

The mapper must provide CreateDataTable() and CreateDataRow(User). Use RequiredMethod.BothForDataTable for a write-only mapper, or AllMain when the entity also needs CreateObject. The collection is enumerated while constructing the parameter.

Using auto-mapping

The four-argument overload takes an explicit comma- or semicolon-separated property list. This example assumes dbo.UserTableType has Id, Login, Name, and Email columns in that order with compatible types:

public int SaveUsersWithProjection(IList<User> users)
{
    return ExecuteCommand(cmd =>
    {
        cmd.UseProcedure("dbo.SaveUsers");
        cmd.AddTableParam("@Users", users, "dbo.UserTableType", "Id,Login,Name,Email");
    });
}

Omitting columnNames from this call selects a different overload: AddTableParam("@Users", users, "dbo.UserTableType") still requires a registered mapper. It does not auto-map all properties.

To use all eligible public readable simple properties deliberately, construct the table explicitly:

public int SaveUsersWithAllProperties(IList<User> users)
{
    return ExecuteCommand(cmd =>
    {
        cmd.UseProcedure("dbo.SaveUsers");
        cmd.AddTableParam("@Users", users.AsDataTable("dbo.UserTableType"));
    });
}

An explicit column list is usually a better fit for a fixed UDTT: reflection property order is not a substitute for checking the SQL contract. The current auto-mapper skips unknown property names; inspect the resulting schema instead of assuming a typo will throw. Its generated delegates are cached by type, table name, and selected properties.

For new parent/child graphs, prepare temporary IDs and foreign keys before materializing child parameters. TVPs are read-only in T-SQL; translate temporary IDs through a join with an ID map, rather than trying to update the TVP. See graph saving.

Source: parameter overloads, DataTable conversion.

See also:

⚠️ **GitHub.com Fallback** ⚠️