現在、dbアクセスにEntity Frameworkを使用していますが、Dapperを見てみたいです。このようなクラスがあります:
public class Course{
public string Title{get;set;}
public IList<Location> Locations {get;set;}
...
}
public class Location{
public string Name {get;set;}
...
}
したがって、1つのコースを複数の場所で教えることができます。 Entity Frameworkがマッピングを行うため、Courseオブジェクトに場所のリストが入力されます。 Dapperでこれをどうやってやるのですか、それも可能ですか、いくつかのクエリステップで行う必要がありますか?
Dapperは本格的なORMではなく、クエリなどの魔法の生成を処理しません。
特定の例では、おそらく次のように機能します。
var courses = cnn.Query<Course>("select * from Courses where Category = 1 Order by CreationDate");
var mappings = cnn.Query<CourseLocation>(
"select * from CourseLocations where CourseId in @Ids",
new {Ids = courses.Select(c => c.Id).Distinct()});
var locations = cnn.Query<Location>(
"select * from Locations where Id in @Ids",
new {Ids = mappings.Select(m => m.LocationId).Distinct()}
);
これを読者に任せて、いくつかのマップを作成し、場所を使用してコースを反復処理します。
警告2100 ルックアップ(SQL Server)未満の場合、in
トリックが機能します。さらにクエリがある場合は、select * from CourseLocations where CourseId in (select Id from Courses ... )
にクエリを修正する必要があります。その場合は、QueryMultiple
を使用して一度にすべての結果をヤンクすることもできます
または、ルックアップで1つのクエリを使用できます。
var lookup = new Dictionary<int, Course>();
conn.Query<Course, Location, Course>(@"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course))
lookup.Add(c.Id, course = c);
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(l); /* Add locations to course */
return course;
}).AsQueryable();
var resultList = lookup.Values;
こちらをご覧ください https://www.tritac.com/blog/dappernet-by-example/
lookup
辞書は不要
var coursesWithLocations =
conn.Query<Course, Location, Course>(@"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (course, location) => {
course.Locations = course.Locations ?? new List<Location>();
course.Locations.Add(location);
return course;
}).AsQueryable();
私はこれに本当に遅れていることを知っていますが、別のオプションがあります。ここでQueryMultipleを使用できます。このようなもの:
var results = cnn.QueryMultiple(@"
SELECT *
FROM Courses
WHERE Category = 1
ORDER BY CreationDate
;
SELECT A.*
,B.CourseId
FROM Locations A
INNER JOIN CourseLocations B
ON A.LocationId = B.LocationId
INNER JOIN Course C
ON B.CourseId = B.CourseId
AND C.Category = 1
");
var courses = results.Read<Course>();
var locations = results.Read<Location>(); //(Location will have that extra CourseId on it for the next part)
foreach (var course in courses) {
course.Locations = locations.Where(a => a.CourseId == course.CourseId).ToList();
}
何かが欠けています。 SQLクエリでLocations
の各フィールドを指定しないと、オブジェクトLocation
に入力できません。見てみましょう:
var lookup = new Dictionary<int, Course>()
conn.Query<Course, Location, Course>(@"
SELECT c.*, l.Name, l.otherField, l.secondField
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course)) {
lookup.Add(c.Id, course = c);
}
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(a);
return course;
},
).AsQueryable();
var resultList = lookup.Values;
クエリでl.*
を使用すると、場所のリストがありましたが、データはありませんでした。
パーティーに遅れてすみません(いつものように)。私にとっては、パフォーマンスと読みやすさの観点から、Dictionary
、 Jeroen Kがやったように を使用する方が簡単です。また、locationsにわたるヘッダーの乗算を回避するために、Distinct()
を使用して潜在的な重複を削除します。
string query = @"SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id";
using (SqlConnection conn = DB.getConnection())
{
conn.Open();
var courseDictionary = new Dictionary<Guid, Course>();
var list = conn.Query<Course, Location, Course>(
query,
(course, location) =>
{
if (!courseDictionary.TryGetValue(course.Id, out Course courseEntry))
{
courseEntry = course;
courseEntry.Locations = courseEntry.Locations ?? new List<Location>();
courseDictionary.Add(courseEntry.Id, courseEntry);
}
courseEntry.Locations.Add(location);
return courseEntry;
},
splitOn: "Id")
.Distinct()
.ToList();
return list;
}
誰かがそれを必要とするかどうかはわかりませんが、迅速で柔軟なコーディングのために、Modelなしの動的バージョンがあります。
var lookup = new Dictionary<int, dynamic>();
conn.Query<dynamic, dynamic, dynamic>(@"
SELECT A.*, B.*
FROM Client A
INNER JOIN Instance B ON A.ClientID = B.ClientID
", (A, B) => {
// If dict has no key, allocate new obj
// with another level of array
if (!lookup.ContainsKey(A.ClientID)) {
lookup[A.ClientID] = new {
ClientID = A.ClientID,
ClientName = A.Name,
Instances = new List<dynamic>()
};
}
// Add each instance
lookup[A.ClientID].Instances.Add(new {
InstanceName = B.Name,
BaseURL = B.BaseURL,
WebAppPath = B.WebAppPath
});
return lookup[A.ClientID];
}, splitOn: "ClientID,InstanceID").AsQueryable();
var resultList = lookup.Values;
return resultList;