Dapper 可以返回映射对象和其他非映射值吗?

Posted

技术标签:

【中文标题】Dapper 可以返回映射对象和其他非映射值吗?【英文标题】:Can Dapper return mapped object & other non mapped values? 【发布时间】:2017-02-08 08:09:24 【问题描述】:

我有这个 working sample 使用 dapper 查询(实际上我使用的是真实表):

async void Main()

    var sql = @"SELECT PersonId = 1,
                FirstName = 'john',
                LastName = 'Lennon'";
    using (var conn = new SqlConnection(@"Data Source=....;Initial Catalog=W...."))
    
        var person = await conn.QueryAsync<Person>(sql);
          person.Dump();
    



public class Person

    public int PersonId  get; set; 
    public string FirstName  get; set; 
    public string LastName  get; set; 

结果:

所以映射按预期工作。但有时我的查询会返回另一个值,例如:

SELECT PersonId = 1,
       FirstName = 'john',
       LastName = 'Lennon' , 
       cnt=(SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS) //example

这是完全合法的:

问题:

是否可以返回一个Person 对象 其他非映射值(在同一个select

类似:

 await conn.QueryAsync<Person,int>(sql)

一个真实的例子:

SELECT  [AddressId]
      ,[PersonName]
      ,[Street]
      ,[Address_2]
      ,[House] , 
       cnt=(COUNT(1) OVER (PARTITION BY house)  )
  FROM [WebERP].[dbo].[App_Address]

所以我返回一个带有计数的 Address 对象,该对象与同一张表有关,我不想要另一个选择。

【问题讨论】:

【参考方案1】:

最简单的方法是将 cnt 添加到您的 Person 类中。您当然可以将其重命名为更有意义的名称。将其设为可为空的 int,以便根据其在数据集中的存在情况设置或不设置。

public class Person

    public int PersonId  get; set; 
    public string FirstName  get; set; 
    public string LastName  get; set; 
    public int? cnt  get;set; 

现在,这个 cnt 并不真正属于 person 类,但通常您不会将数据集分配给这样的类,而是将其分配给 DTO 之类的东西,然后将其映射到您拥有的任何模型类或类中并以您认为合适的任何方式使用额外的属性。这将使您的业务类(例如 Person)保持纯净。

【讨论】:

嗯...有道理。所以我可以创建一个名为 PersonCounted 的派生 Person 类型,它会有这个额外的 prop 。 当然,只需将sql代码中的名称更改为“cnt as PersonCount”就可以了,或者只是PersonCount = whateevr【参考方案2】:

是的,您可以为此使用QueryMultiple 扩展名。但是您应该使用单独的选择来获取列数:

SELECT PersonId = 1, FirstName = 'john', LastName = 'Lennon'
SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS) 

然后得到两个结果

using(var result = sqlConnection.QueryMultiple(sql))

    var person = result.Read<Person>().Single();
    int count = multi.Read<int>().Single();

延伸阅读:Multiple Results 部分。

另一种返回附加值的方法是动态查询。但在这种情况下,您将不得不手动构建 Person 对象:

var sql = @"SELECT PersonId = 1,
            FirstName = 'john',
            LastName = 'Lennon',
            cnt=(SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS)";
var row = conn.Query(sql).Single();
var person = new Person  
    PersonId = row.PersonId, 
    FirstName = row.FirstName, 
    LastName = row.LastName
;
int cnt = row.cnt;

AFAIK 没有其他方法可以同时返回人和 cnt。

【讨论】:

我知道。但正如我提到的((在同一个选择中)) - 没有必要打两次桌子。有时我会在分区内容和其他窗口函数上添加行号,所以我需要在同一个选择中使用它 @RoyiNamir 你没有第一次选择打桌子 这只是一个例子(为简单起见)。实际上,我从一个表(及其列)中选择并返回另一个窗口函数(sum over ( order by...) @RoyiNamir 如果您在 Person 类中没有其他字段,则使用多个查询或返回动态结果并手动创建人员【参考方案3】:

您确定要这样做吗?正如歌曲所说,保持生活中阳光的一面。 A 查询应该有 a 返回类型。不同的查询应该有不同的返回类型。您说conn.Query&lt;ReturnType&gt;(SQL) 的神奇之处在于您将手放在心上并承诺SQL 将填充ReturnType。如果在某个地方,ReturnType 具有查询从不打算填充的字段,那对我来说就是代码异味。维护该代码的人永远不会理解为什么会这样。如果稍后将来自不同查询的数据输入到单个方法中,请创建一个接口。

ORM 思维的一个污染副作用是希望“规范化”你的类。如果他们在不同的情况下做不同的事情,那么拥有几十个 Person 对象不一定是个问题。在您的数据库中,您不希望在两个地方使用相同的数据。在您的应用程序中,您不希望在两个地方出现相同的行为。这些是非常不同的概念。如果您使用QueryFirst(免责声明:我写的),这一切都会简单得多,而且您不必把手放在心脏上。

【讨论】:

谢谢 - 所以基本上你是说conn.Query&lt;PersonWithCount&gt;(SQL) 是一个可行的解决方案。对吗? 基本上,这就是我要说的!【参考方案4】:

可以选择使用动态返回类型,如here 所述 我更喜欢像 Tuple 这样的复合返回类型,但它基本上是 Dapper 手册中描述的多重映射。 (YMMV,我自己是 Dapper 的新手)

【讨论】:

以上是关于Dapper 可以返回映射对象和其他非映射值吗?的主要内容,如果未能解决你的问题,请参考以下文章

dapper可以商用吗

如何在 Dapper 上执行严格的映射

使用 Dapper 映射嵌套对象列表

将SqlDataReader的结果映射到对象的最快方法

使用 Dapper 映射嵌套对象列表(3 级嵌套对象)

Dapper 映射结果列表