Unterstützt Dapper SQL 2008-Parameter mit Tabellenwerten?

78

Weiß jemand, ob es möglich ist, tabellenwertige Parameterdaten mit Dapper an eine gespeicherte Prozedur zu übergeben?

Carlos Mendes
quelle
1
Alternativ können Sie Bulkinsert für eine temporäre
Chris Marisic

Antworten:

91

Es gibt jetzt (n Dapper 1.26 und höher) direkte Unterstützung für in dapper eingebrannte tabellenwertige Parameter. Bei gespeicherten Prozeduren müssen Sie, da der Datentyp in die sproc-API integriert ist, lediglich Folgendes angeben DataTable:

var data = connection.Query<SomeType>(..., new {
    id=123, name="abc", values = someTable
}, ...);

Für direkten Befehlstext haben Sie zwei weitere Möglichkeiten:

  • Verwenden Sie eine Hilfsmethode, um den benutzerdefinierten Datentyp anzugeben:

    var data = connection.Query<SomeType>(..., new {
        id=123, name="abc", values = someTable.AsTableValuedParameter("mytype")
    }, ...);
    
  • Teilen Sie der Datentabelle selbst mit, welcher benutzerdefinierte Datentyp verwendet werden soll:

    someTable.SetTypeName("mytype");
    var data = connection.Query<SomeType>(..., new {
        id=123, name="abc", values = someTable
    }, ...);        
    

Jedes davon sollte gut funktionieren.

Marc Gravell
quelle
In einem Fall, in dem ich mit Dapper kein TVP hinzufügen kann, wenn ich einen Zusatz für Nicht-Eingabeparameter benötige und mit Dynamic Parameters kein TVP hinzufügen kann, überprüfen Sie bitte meine Frage @ stackoverflow.com/questions/33087629/ …
Mrinal Kamboj
9
Bah, keine IEnumerable Konvertierung?
Nuzzolilo
Mit ExecuteReadererhalte ich "Die Mitgliedsereignisse vom Typ System.Data.DataTable können nicht als Parameterwert verwendet werden".
Ian Warburton
28

Ja, wir unterstützen sie, aber Sie müssen Ihre eigenen Helfer codieren.

Zum Beispiel:

class IntDynamicParam : Dapper.SqlMapper.IDynamicParameters
{
    IEnumerable<int> numbers;
    public IntDynamicParam(IEnumerable<int> numbers)
    {
        this.numbers = numbers;
    }

    public void AddParameters(IDbCommand command)
    {
        var sqlCommand = (SqlCommand)command;
        sqlCommand.CommandType = CommandType.StoredProcedure;

        List<Microsoft.SqlServer.Server.SqlDataRecord> number_list = new List<Microsoft.SqlServer.Server.SqlDataRecord>();

        // Create an SqlMetaData object that describes our table type.
        Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int) };

        foreach (int n in numbers)
        {
            // Create a new record, using the metadata array above.
            Microsoft.SqlServer.Server.SqlDataRecord rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition);
            rec.SetInt32(0, n);    // Set the value.
            number_list.Add(rec);      // Add it to the list.
        }

        // Add the table parameter.
        var p = sqlCommand.Parameters.Add("@ints", SqlDbType.Structured);
        p.Direction = ParameterDirection.Input;
        p.TypeName = "int_list_type";
        p.Value = number_list;

    }
}

// SQL Server specific test to demonstrate TVP 
public void TestTVP()
{
    try
    {
        connection.Execute("CREATE TYPE int_list_type AS TABLE (n int NOT NULL PRIMARY KEY)");
        connection.Execute("CREATE PROC get_ints @ints int_list_type READONLY AS select * from @ints");

        var nums = connection.Query<int>("get_ints", new IntDynamicParam(new int[] { 1, 2, 3 })).ToList();
        nums[0].IsEqualTo(1);
        nums[1].IsEqualTo(2);
        nums[2].IsEqualTo(3);
        nums.Count.IsEqualTo(3);
        connection.Execute("DROP PROC get_ints");
        connection.Execute("DROP TYPE int_list_type");

    }
}

Stellen Sie sicher, dass Sie die Leistung für Parameter mit Tabellenwerten ordnungsgemäß testen. Als ich dies zum Übergeben von int-Listen getestet habe, war es erheblich langsamer als das Übergeben mehrerer Parameter.

Ich bin absolut nicht dagegen, einige SQL Server-spezifische Helfer für Dapper im Contrib-Projekt zu haben, aber der Core-Dapper vermeidet es, wenn möglich herstellerspezifische Tricks hinzuzufügen.

Sam Safran
quelle
1
Tatsächlich ist die Verwendung von TVP langsamer als "where col in @values". Wie kann ich die Listenunterstützungsfunktion (mit Dapper können Sie IEnumerable <int> übergeben und Ihre Abfrage automatisch parametrisieren) verwenden, um eine Liste von Ints an ein StoredProcedure zu übergeben?
Carlos Mendes
Dies ist einer dieser verrückten Randfälle, in denen Stapel schneller sind als SPS. Die Technik, die Dapper für die Listenunterstützung verwendet, ist nicht mit gespeicherten Prozessen kompatibel
Sam Saffron,
Können Sie dies aktualisieren? Ich kann nicht genau herausfinden, ob es in Ordnung ist, den Identitätsparameter in neueren Versionen von Dapper zu ignorieren.
Chris Pfohl
Würde dieser Code so funktionieren, wie er ist, oder müsste die IEnumerable <int> -Sammlung in eine DataTable konvertiert werden, um als TVP verwendet zu werden
Mrinal Kamboj,
11

Ich weiß, dass dieses Ticket ALT ist, sehr alt, aber ich wollte Sie wissen lassen, dass ich das Dapper.Microsoft.Sql-Paket veröffentlicht habe, das generische TVPs unterstützt.

https://www.nuget.org/packages/Dapper.Microsoft.Sql/

Beispielverwendung:

List<char> nums = this.connection.Query<char>(
  "get_ints", 
  new TableValuedParameter<char>(
    "@ints", "int_list_Type", new[] { 'A', 'B', 'C' })).ToList();

Es basiert auf den ursprünglichen Klassen aus dem Dapper-Testprojekt.

Genießen!

Darek
quelle
Ich versuche zu verstehen, wie man einen TVP als Parameter zusammen mit anderen typischen typisierten Parametern hat. Wie geht das?
Snowy
Ich habe diesen Teil noch nicht implementiert.
Darek
5

heute ist es nicht. Wir haben tatsächlich tabellenwertige Parameter für unsere freche "in" -Implementierung ( where col in @values) untersucht, waren jedoch von der Leistung sehr unbeeindruckt. Im Kontext eines SPROC ist dies jedoch sinnvoll.

Am besten protokollieren Sie dies als Problem auf der Projektwebsite, damit wir es verfolgen / priorisieren können. Es hört sich jedoch so an, als wäre etwas machbar, wahrscheinlich ähnlich wie bei den Optionen DbString oder DynamicParameters.

Aber heute? Nein.

Marc Gravell
quelle
2
Korrektur, wir unterstützen es irgendwie ... Sie müssen es nur selbst codieren :)
Sam Saffron