Sto cercando di usare Dapper in un'applicazione ASP.Net Core per mappare più tabelle su un oggetto con altri oggetti come sue proprietà.
Le mie tabelle sono le seguenti (solo sommario di base):
tabella address_type (tabella di ricerca)
tabella phone_number (nessun ID utente memorizzato in questa tabella, questa tabella è solo record telefonici)
Sostanzialmente, ciò che sta accadendo è che i risultati vengono restituiti in modo corretto se tutti gli utenti non hanno alcun record di indirizzo o telefono o se solo il primo utente nell'elenco ha record di indirizzo / telefono. Quello che voglio è che se un utente ha un indirizzo / numero di telefono, allora quel dizionario è popolato, altrimenti voglio ancora tutte le informazioni utente ma quel dizionario indirizzo / numero telefonico sarà vuoto.
I miei oggetti assomigliano al seguente:
public class User
{
public uint id { get; set; }
public DateTime modified_date { get; set; }
public uint modified_by { get; set; }
public string user_name { get; set; }
public uint company_code { get; set; }
public string email { get; set; }
public bool active { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public Dictionary<uint, Address.Address> addresses { get; set; }
public Dictionary<uint, Address.PhoneNumber> phone_numbers { get; set; }
}
public class Address
{
public uint address_id { get; set; }
public AddressType address_type { get; set; }
public string address_line1 { get; set; }
public string address_line2 { get; set; }
public string address_line3 { get; set; }
public string city { get; set; }
public string state { get; set; }
public string country_code { get; set; }
public string postal_code { get; set; }
public sbyte is_po_box { get; set; }
}
public class AddressType
{
public uint id { get; set; }
public string name { get; set; }
}
public class PhoneNumber
{
public uint id { get; set; }
public PhoneNumberType phone_number_type { get; set; }
public string phone_number { get; set; }
public string phone_ext { get; set; }
}
public class PhoneNumberType
{
public uint id { get; set; }
public string name { get; set; }
}
Ecco la mia funzione in cui cerco di usare Dapper per mappare alla classe User:
public List<User> GetUsersByStatus(uint companyCode, string status)
{
if (companyCode == 0)
throw new ArgumentOutOfRangeException("companyID", "The Company ID cannot be 0.");
List<User> Users = new List<User>();
try
{
string sql = @"SELECT u.*, ad.*, adt.*, p.*, pt.*
FROM master.user u
LEFT JOIN master.user_has_address AS uha ON uha.user_id = u.id
LEFT JOIN master.address AS ad ON ad.id = uha.address_id
LEFT JOIN master.lookup_address_type adt ON adt.id = uha.address_type_id
LEFT JOIN master.user_has_phone_number AS uhp ON uhp.user_id = u.id
LEFT JOIN master.phone_number AS p ON p.id = uhp.phone_number_id
LEFT JOIN master.lookup_phone_number_type pt ON pt.id = uhp.phone_number_type_id
WHERE u.company_code = " + companyCode;
switch (status)
{
case "1":
// Active Status.
sql = sql + " AND (u.active = TRUE)";
break;
case "2":
// Retired Status.
sql = sql + " AND (u.active = FALSE)";
break;
}
sql = sql + " ORDER BY u.user_name";
using (var conn = new MySqlConnection(connectionString))
{
conn.Open();
var userDictionary = new Dictionary<uint, User>();
conn.Query<User, Address, AddressType, PhoneNumber, PhoneNumberType, User>(sql, (u, ad, adt, p, pt) =>
{
User user;
if (!userDictionary.TryGetValue(u.id, out user))
userDictionary.Add(u.id, user = u);
if (ad != null && adt != null)
{
Address address = ad;
address.address_type = new AddressType() { id = adt.id, name = adt.name };
if (user.addresses == null)
user.addresses = new Dictionary<uint, Address>();
if (!user.addresses.ContainsKey(adt.id))
user.addresses.Add(adt.id, address);
}
if (p != null && pt != null)
{
PhoneNumber phone = p;
phone.phone_number_type = new PhoneNumberType() { id = pt.id, name = pt.name };
if (user.phone_numbers == null)
user.phone_numbers = new Dictionary<uint, PhoneNumber>();
if (!user.phone_numbers.ContainsKey(pt.id))
user.phone_numbers.Add(pt.id, phone);
}
return user;
},
splitOn: "id,id,id,id").AsQueryable();
Users = userDictionary.Values.ToList();
}
}
catch(Exception ex)
{
//TO DO: log exception
}
return Users;
}
Ho provato a tracciarlo e nei casi in cui il terzo utente (ad esempio) ha record di indirizzi / telefono, sembra che i primi due utenti siano soddisfatti, ma poi saltano fuori dalla porzione di query quando arriva al record con indirizzo / numeri di telefono e quindi restituisce una lista utenti vuota.
Qualcuno ha idea di cosa sto facendo male?
Risulta che il problema principale è stato il fatto che ho lasciato il parametro address_id nella classe Address chiamata come era, dovrebbe essere stato solo 'id'. L'ho aggiornato e ora funziona, ho anche aggiornato il mio codice in base al suggerimento di Palle Due, in modo che possa aver contribuito anche.
Non sono sicuro che questa sia la soluzione, ma ho un problema con questo codice:
User user;
if (!userDictionary.TryGetValue(u.id, out user))
userDictionary.Add(u.id, user = u);
Ricorda che u è il parametro del lambda e verrà modificato durante l'esecuzione. Lo farei:
User user;
if (!userDictionary.TryGetValue(u.id, out user))
{
user = new User(u); // Make a new instance of user
userDictionary.Add(u.id, user);
}
Inoltre dovresti assolutamente usare i parametri per la tua query:
WHERE u.company_code = @CompanyCode";
E infine non penso che dovrebbe essere la responsabilità di questo codice per costruire dizionari per contenere indirizzi e numeri di telefono. Il costruttore User dovrebbe prendersene cura.