verwendetes Datenbanksystem: <IBM DB2>
Hallo,
ich versuche mich gerade mit dem ORM Dapper in einem kleinen Beispielprojekt vertraut zu machen.
Es geht um das Mapping eines SQL-JOINS zweier Tabellen. Auf der Dapper-Website habe ich die Multi-Mapping Möglichkeiten gesehen, welche ich hierfür verwenden möchte.
Ich habe in der Datenbank zwei Test-Tabellen - ADDRESSES und STATES. Tabelle ADDRESSES hat eine Fremdschlüssel-Spalte namens "Status_Id" welche auf die Spalte "Id" der Tabelle STATES referenziert.
Eine Adresse kann genau einen Status haben. Den Status können aber mehrere Adressen haben.
Hier die Modelllklassen:
public class Address
{
[Key]
public int Id { get; set; }
[StringLength(20, MinimumLength = 3)]
public string Anrede { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Vname { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Nname { get; set; }
[StringLength(10, MinimumLength = 10)]
public string Geburt { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Strasse { get; set; }
[StringLength(5, MinimumLength = 5)]
public string Plz { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Ort { get; set; }
[StringLength(20, MinimumLength = 3)]
public string Telefon { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Email { get; set; }
// Foreign key
[Required]
public int Status_Id { get; set; }
// State-Modell
public State Status { get; set; }
}
public class State
{
[Key]
public int Id { get; set; }
[StringLength(20, MinimumLength = 3)]
public string Name { get; set; }
}
Mit der folgenden Methode versuche ich das Abfrageergebnis in das Datenmodell zu mappen:
public static List<Address> GetAddressByAdrnr(int id)
{
string sql = "SELECT * FROM DBTEST01.ADDRESSES AS A JOIN DBTEST01.STATES AS B ON A.STATUS_ID = B.ID WHERE A.ID = " + id;
using (IDbConnection db = new iDB2Connection(ConfigurationManager.ConnectionStrings["csAs400"].ConnectionString))
{
if (db.State == ConnectionState.Closed)
{
db.Open();
var addresses = db.Query<Address, State, Address>(
sql,
(address, state) =>
{
address.Status = state;
return address;
})
.Distinct()
.ToList();
}
System.Diagnostics.Debug.Print("QUERY: " + sql);
}
return null;
}
Die Query lasse ich mir in der Fehlerliste ausgeben und kopiere ihn in ein Query-Tool, welches mir den korrekten Datensatz liefert:
SELECT * FROM DBTEST01.ADDRESSES AS A JOIN DBTEST01.STATES AS B ON A.STATUS_ID = B.ID WHERE A.ID = 5
-->
ID ANREDE VNAME NNAME GEBURT STRASSE PLZ ORT TELEFON EMAIL STATUS_ID ID NAME
5 Frau Magda Malhingehen 1945-08-01 Am Waldrand 98 56123 Waldhausen 085476822 magdamalhingehen@hotweb.com 3 3 Abgekündigt
Das Ergebnis der Methode vom Typ List<Address> wird als DataSource an ein DataGridView gebunden, welches aber leer bleibt.
Ich vermute, dass ich mein Datenmodell nicht richtig angelegt habe.
Kann mir bitte jemand helfen und erklären, was falsch läuft?
Gruß NEUMee
Das Ergebnis der Methode vom Typ List<Address>
Dein Code/Ergebnis liefert immer null zurück.
Schön, dass Du mit einem addresses Objekt arbeitest und dieses füllst; aber nie zurück gibst.
null bei einem List<T> Typen zurück zu liefern verletzt ohnehin die allgemeinen Best Practises bei Listen.
Was genau Dein Query zurück liefert siehst Du via Debugging
[Artikel] Debugger: Wie verwende ich den von Visual Studio?
Damit solltest Du auch den Fehler finden.
Ansonsten gibt es hunderte Dapper Extensions, die teilweise sogar Linq auf Multi Query Results / Multi Mapping ermöglichen; je nachdem, was Du willst.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo Abt,
danke erstmal!
Ich habe die Methode folgendermaßen abgeändert und erhalte nun auch eine korrekte Ausgabe:
public static List<Address> GetAddressByAdrnr(int id)
{
string sql = "SELECT A.*, B.NAME FROM DBTEST01.ADDRESSES AS A JOIN DBTEST01.STATES AS B ON A.STATUS_ID = B.ID WHERE A.ID = " + id;
System.Diagnostics.Debug.Print("QUERY: " + sql);
using (IDbConnection db = new iDB2Connection(ConfigurationManager.ConnectionStrings["csAs400"].ConnectionString))
{
if (db.State == ConnectionState.Closed)
{
db.Open();
var addresses = db.Query<Address, State, Address>(
sql,
(address, state) =>
{
address.Status = state.Name;
return address;
}, splitOn: "NAME")
.Distinct()
.ToList();
return addresses;
}
}
return new List<Address>();
}
Das mit dem return null; habe ich auch geändert und gebe immer ein List<Addrerss> Objekt zurück.
Das Address Klassenmodell habe ich wiefolgt verändert:
public class Address
{
[Key]
public int Id { get; set; }
[StringLength(20, MinimumLength = 3)]
public string Anrede { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Vname { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Nname { get; set; }
[StringLength(10, MinimumLength = 10)]
public string Geburt { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Strasse { get; set; }
[StringLength(5, MinimumLength = 5)]
public string Plz { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Ort { get; set; }
[StringLength(20, MinimumLength = 3)]
public string Telefon { get; set; }
[StringLength(40, MinimumLength = 3)]
public string Email { get; set; }
// Foreign key
//[Required]
//public int Status_Id { get; set; }
// State-Modell
public string Status { get; set; }
}
Kann man das so machen, oder ist das wieder falsch?
Gruß, NEUMee
Dapper Parameter verwenden und diese String Frickelei gar nicht erst angewöhnen.
Ebenso ist das static halt noch untestbarer Quatsch 😉
Ich würde es zudem eher so schreiben:
IList<Address> addresses = new List<Address>();
using (IDbConnection db = new iDB2Connection(ConfigurationManager.ConnectionStrings["csAs400"].ConnectionString))
{
if (db.State == ConnectionState.Closed)
{
db.Open();
addresses = db.Query<Address, State, Address>(
sql,
(address, state) =>
{
address.Status = state.Name;
return address;
}, splitOn: "NAME")
.Distinct()
.ToList();
}
}
return addresses;
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Das mit dem Static habe ich aus nem Tutorial von Youtube 😃. das werde ich mir noch anschauen.
Das mit den Parameter habe ich hoffentlich jetzt richtig gemacht.
Dein methodenaufbau gefällt mir auch viel besser! 😃
public static IList<Address> GetAddressByAdrnr(int id)
{
string sql = "SELECT A.*, B.NAME FROM DBTEST01.ADDRESSES AS A JOIN DBTEST01.STATES AS B ON A.STATUS_ID = B.ID WHERE A.ID = @paramId";
System.Diagnostics.Debug.Print("QUERY: " + sql);
IList<Address> addresses = new List<Address>();
using (IDbConnection db = new iDB2Connection(ConfigurationManager.ConnectionStrings["csAs400"].ConnectionString))
{
if (db.State == ConnectionState.Closed)
{
db.Open();
addresses = db.Query<Address, State, Address>(
sql,
(address, state) =>
{
address.Status = state.Name;
return address;
}, new { paramId = id }, splitOn: "NAME")
.Distinct()
.ToList();
}
}
return addresses;
}
Danke nochmals!
Gruß, NEUMee
Das mit dem Static habe ich aus nem Tutorial von Youtube 😃.
[Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code