Laden...

Dapper - Query Multi-Mapping mit Join bleibt am UI immer leer

Erstellt von NEUMee vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.425 Views
N
NEUMee Themenstarter:in
24 Beiträge seit 2011
vor 5 Jahren
Dapper - Query Multi-Mapping mit Join bleibt am UI immer leer

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

16.806 Beiträge seit 2008
vor 5 Jahren

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.

  1. Dein Code ist so kaum testbar. static ist bei ordentlichem OOP nicht notwendig; hier sogar falsch
  2. [Artikelserie] SQL: Parameter von Befehlen; auch Dapper unterstützt Parameter!
  3. Dapper kennt auch QueryMultiple(), das für Multi Queries gedacht ist - wie der Name schon sagt. Das mal als Hinweis; ist teilweise schneller als JOINs.
    Das liefert Dir ein Mapper-Objekt mit dessen Read-Methoden Du anschließend an die Multi-Query-Results kommst.

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.

N
NEUMee Themenstarter:in
24 Beiträge seit 2011
vor 5 Jahren

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

16.806 Beiträge seit 2008
vor 5 Jahren

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;
N
NEUMee Themenstarter:in
24 Beiträge seit 2011
vor 5 Jahren

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

16.806 Beiträge seit 2008
vor 5 Jahren

Das mit dem Static habe ich aus nem Tutorial von Youtube 😃.

[Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio