如何使用Dapper映射嵌套对象的列表

时间:2011-09-22 00:48:35

标签: orm dapper

我目前正在使用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;}
   ...
}

因此,可以在多个地点教授一门课程。实体框架为我执行映射,因此我的Course对象填充了一个位置列表。我怎么会用Dapper来解决这个问题,它是否可能,或者我必须在几个查询步骤中进行此操作?

8 个答案:

答案 0 :(得分:147)

或者,您可以将一个查询与查找结合使用:

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/

答案 1 :(得分:44)

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技巧将起作用,如果您有更多,则可能需要将查询修改为{{ 1}}如果是这种情况,您可以使用select * from CourseLocations where CourseId in (select Id from Courses ... )

一次性地抽出所有结果

答案 2 :(得分:28)

无需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(); 字典

get user

答案 3 :(得分:26)

我知道我已经很晚了,但还有另一种选择。您可以在此处使用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();
}

答案 4 :(得分:2)

缺少一些东西。如果未在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.*,我有位置列表但没有数据。

答案 5 :(得分:2)

很抱歉晚到派对(像往常一样)。对我来说,就性能和可读性而言,使用Dictionarylike Jeroen K did更容易。另外,为了避免跨个位置的标头乘法,我使用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;
}

答案 6 :(得分:0)

不确定是否有人需要它,但我有动态版本没有Model for quick&amp;灵活的编码。

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;

答案 7 :(得分:0)

还有另一种使用 JSON 结果的方法。尽管接受的答案和其他答案都得到了很好的解释,但我只是想了另一种方法来获得结果。

创建存储过程或选择qry以json格式返回结果。然后将结果对象反序列化为所需的类格式。请仔细阅读示例代码。

{{1}}

这是另一个思考过程。请查看相同内容。