私はDapperをASP.Net Coreアプリケーションで使用して、複数のテーブルをそのオブジェクトのプロパティとして他のオブジェクトを持つ1つのオブジェクトにマッピングしようとしています。
私のテーブルは以下の通りです(基本的な要約)。
address_typeテーブル(ルックアップテーブル)
phone_numberテーブル(このテーブルに格納されているユーザIDはありません。このテーブルは単なる電話レコードです)
基本的に何が起きているのかは、すべてのユーザーに住所または電話記録がない場合、またはリストの最初のユーザーのみが住所/電話記録を持っている場合に結果が正常に返されることです。ユーザーが住所/電話番号を持っていて辞書が入力されている場合は、それ以外の場合はすべてのユーザー情報が必要ですが、アドレス/電話番号の辞書は空になります。
私のオブジェクトは次のようになります:
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; }
}
ここではDapperを使って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;
}
私はそれをトレースしようとしたが、3番目のユーザ(例えば)がアドレス/電話記録を持っている場合は、最初の2人のユーザをうまくつかんだように見えるが、 /電話番号を入力し、空のユーザーリストを返します。
誰かが間違って何をしているか考えている人はいますか?
主な問題は、Addressクラスのaddress_idパラメータをそのままの形で残したということでした。それはちょうど 'id'だったはずです。私はそれを更新し、今は動作します、私はまたPalle Dueの提案に基づいて自分のコードを更新しました。
私はこれが解決策であるかどうかはわかりませんが、私はこのコードに問題があります:
User user;
if (!userDictionary.TryGetValue(u.id, out user))
userDictionary.Add(u.id, user = u);
uはラムダのパラメータであり、実行中に変更されることに注意してください。私はこれを行うだろう:
User user;
if (!userDictionary.TryGetValue(u.id, out user))
{
user = new User(u); // Make a new instance of user
userDictionary.Add(u.id, user);
}
また、あなたは間違いなくクエリのパラメータを使用する必要があります:
WHERE u.company_code = @CompanyCode";
最後に、アドレスと電話番号を保持するための辞書を構築することは、このコードの責任であるとは思わない。 Userコンストラクタはそれを処理する必要があります。