151

我目前正在使用实体框架来访问我的数据库,但想看看 Dapper。我有这样的课程:

public class Course{
   public string Title{get;set;}
   public IList<Location> Locations {get;set;}
   ...
}

public class Location{
   public string Name {get;set;}
   ...
}

因此,可以在多个地点教授一门课程。Entity Framework 为我进行映射,因此我的 Course 对象填充了位置列表。我将如何使用 Dapper 来解决这个问题,甚至有可能还是我必须在几个查询步骤中完成它?

4

8 回答 8

204

或者,您可以使用一个带有查找的查询:

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/

于 2013-07-19T14:33:09.683 回答
58

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()}
);

全部映射

将此留给读者,您创建一些地图并遍历您的课程填充位置。

警告in如果您的查找次数少于2100 (Sql Server),该技巧将起作用,如果您有更多查找,您可能希望将查询修改为select * from CourseLocations where CourseId in (select Id from Courses ... )如果是这种情况,您不妨一次性使用所有结果QueryMultiple

于 2011-09-23T01:18:34.880 回答
46

不需要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();
于 2017-10-17T14:23:04.033 回答
37

我知道我真的迟到了,但还有另一种选择。您可以在此处使用 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();
}
于 2017-02-23T15:27:19.133 回答
8

很抱歉迟到了(和往常一样)。对我来说,就性能和可读性而言,使用 a 更容易Dictionary就像 Jeroen K 所做的那样。此外,为了避免跨位置的标题乘法,我使用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;
}
于 2018-10-10T11:44:00.997 回答
6

缺了点什么。如果您未Locations在 SQL 查询中指定每个字段,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.*,我有位置列表但没有数据。

于 2014-02-14T14:16:43.723 回答
1

不确定是否有人需要它,但我有没有模型的动态版本,用于快速灵活的编码。

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;
于 2017-07-18T01:49:54.757 回答
0

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

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

using (var db = connection.OpenConnection())
{                
  var results = await db.QueryAsync("your_sp_name",..);
  var result = results.FirstOrDefault();    
                    
  string Json = result?.your_result_json_row;
                   
  if (!string.IsNullOrEmpty(Json))
  {
     List<Course> Courses= JsonConvert.DeserializeObject<List<Course>>(Json);
  }
    
  //map to your custom class and dto then return the result        
}

这是另一个思考过程。请检查相同。

于 2021-05-16T17:41:17.483 回答