如何使用dapper返回包含其他类型列表的类型?

时间:2012-04-25 20:00:56

标签: c# dapper

如何在一个查询中使用dapper选择所有SpaceShips及其Sightings

我有以下对象:

public class SpaceShip
{
    public int Id { get; set; }
    public string DriveType { get; set; }
    public List<Sighting> Sightings { get; set; }
}
public class Sighting
{
    public int Id { get; set; }
    public double Lat { get; set; }
    public double Lon { get; set; }
}

使用以下架构:

If Exists(Select * from sysobjects where name = 'Sightings')
Drop Table Sightings

If Exists(Select * from sysobjects where name = 'SpaceShips')
Drop Table SpaceShips

CREATE TABLE [dbo].[SpaceShips](
[Id] [int] IDENTITY(1,1) NOT NULL,
[DriveType] [varchar](max) NOT NULL,
CONSTRAINT [PK_SpaceShips] PRIMARY KEY CLUSTERED 
([Id] ASC) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Sightings](
[Id] [int] IDENTITY(1,1) NOT NULL,
[SpaceShipId] [int] NOT NULL,
[Lat] [decimal](18, 0) NOT NULL,
[Lon] [decimal](18, 0) NOT NULL,
CONSTRAINT [PK_Sightings] PRIMARY KEY CLUSTERED 
([Id] ASC) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Sightings]  WITH CHECK ADD CONSTRAINT [FK_Sightings_SpaceShips] FOREIGN KEY([SpaceShipId]) REFERENCES [dbo].[SpaceShips] ([Id])
GO

ALTER TABLE [dbo].[Sightings] CHECK CONSTRAINT [FK_Sightings_SpaceShips]
GO

Insert into SpaceShips (DriveType) Values ('X18-9'),('PV-276M')
Insert into Sightings (SpaceShipId, Lat, Lon) Values (1, 10, 90), (1, 20, 80), (1, 30, 70), (1, 40, 60)
Insert into Sightings (SpaceShipId, Lat, Lon) Values (2, 104, 64), (2, 105, 63), (2, 106, 62), (2, 107, 61)

我正在尝试使用dapper来选择SpaceShip的列表,包括其关联的Sightings,如下所示:

using (var con = MuzakiFactory.OpenPortal())
{
    try
    {
      var sql = @"Select * From SpaceShips ship left join Sightings s on s.SpaceShipId = ship.id";
      var result = con.Query<SpaceShip, List<Sighting>, SpaceShip>
                       (sql, (ship, sightings) => {
                                                   ship.Sightings = sightings; 
                                                   return ship;
                                                  });
      return result;
    }
    catch (Exception ex)
    {
     Captains.Log(ex);
     throw;
    }
}

但结果是SpaceShips列表为空Sightings

更新

使用Marc对QueryMultiple的建议似乎更容易并自己连接。为了完成这项工作,我必须将public int SpaceShipId {get;set;}添加到我的Sighting课程中。我最终得到了这个:

var sql = @"Select * From SpaceShips; Select * from Sightings;";
using (var multi = con.QueryMultiple(sql))
  {
   var ships = multi.Read<SpaceShip>().ToList();
   var sightings = multi.Read<Sighting>().ToList();
   foreach(var ship in ships)
   {
    ship.Sightings = new List<Sighting>(sightings.Where(x => x.SpaceShipId == ship.Id));
   }
   return ships;
  }

注意: 您显然希望在每个查询的where子句中包含父ID。

1 个答案:

答案 0 :(得分:2)

首先,您必须使用<SpaceShip, Sighting, SpaceShip>,并编写自己的身份管理器(读取:字典)以使重复数据唯一。伪代码:

(ship, sighting) => {
    SpaceShip actualShip;
    if(!ships.TryGetValue(ship.Id, out actualShip)) {
        ships.Add(ship.Id, actualShip = ship);
    }
    actualShip.Sightings.Add(sighting);
    return actualShip;
}

其中shipsDictionary<int, SpaceShip>或类似的。如果您认为这是一种常见情况,那么我们可以将认为作为内置选项。

然而!这可能需要很多额外的列。就个人而言,我很想在这里考虑一个多结果查询,QueryMultiple并将两者结合在一起作为后处理。