Ich habe untersucht, ob wir etwas von unserem EF6-Code an Dapper portieren konnten, um eine bessere Leistung zu erzielen, als ich auf ein seltsames Problem stieß. Eine einzelne Zeile Abfrage dauerte fast 10 mal so viel in Dapper als in EF. Es sah so aus:
using (IDbConnection conn = new SqlConnection("connection string"))
{
row = conn.Query<ReportView>("select * from ReportView where ID = @ID",
new {ID = id}))
.FirstOrDefault();
}
Diese Abfrage zielt auf eine Ansicht mit ungefähr 80 Spalten ab, und die EF-Version verwendet dieselbe exakte Abfrage und dasselbe Modell. Als Referenz ist dies die EF-Version:
row = context.ReportViews.Where(s => s.ID == id).FirstOrDefault();
Ich habe berücksichtigt, dass die erste Abfrage langsam sein könnte, also nahm ich Messungen nach einer "Aufwärmphase" vor. Ich dachte, es könnte ein Problem bei der Wiederverwendung des EF-Modells sein, also habe ich ein einfaches POCO als Modell erstellt. Nichts davon hat funktioniert. Also habe ich damit herumgespielt, verschiedene Dinge ausprobiert und beschlossen, eine SQL-Injectiony verkettete SQL-Anweisung zu verwenden.
using (IDbConnection conn = new SqlConnection("connection string"))
{
row = conn.Query<ReportView>(string.Format("select * from ReportView where ID = '{0}'",
id)).FirstOrDefault();
}
Diese Abfrage war tatsächlich schneller als die EF.
Was passiert hier? Warum ist die parametrisierte Abfrage so viel langsamer?
Basierend auf Ihrem letzten Beispiel scheint es höchstwahrscheinlich, dass Ihre Spalte varchar
Wenn Sie jedoch die parametrisierte Abfrage verwenden, wird der Parameter als nvarchar
. Da nvarchar to varchar Datenverlust verursachen kann, konvertiert SQL jeden Wert in der Tabelle in nvarchar zum Vergleich. Wie Sie sich vorstellen können, ist das Konvertieren jeder Zeile zum Vergleich langsam und verhindert die Verwendung des Indexes.
Um dies zu umgehen, haben Sie zwei Möglichkeiten:
Wenn Ihre Datenbank nvarchar überhaupt nicht verwendet, können Sie die Zuordnung einfach während des Anwendungsstarts ändern:
Dapper.SqlMapper.AddTypeMap(typeof(string), System.Data.DbType.AnsiString);
Ansonsten können Sie es per Abfrage ändern:
row = conn.Query<ReportView>("select * from ReportView where ID = @ID",
new {ID = new DbString { Value = id, IsAnsi = true }})
.FirstOrDefault();