Dapper:映射层次结构和单个不同的属性

时间:2015-08-13 22:00:08

标签: c# .net dapper

我真的很喜欢Dapper的简约和可能性。我想用Dapper来解决我日常面临的共同挑战。这些描述如下。

这是我的简单模型。

public class OrderItem {
    public long Id { get; set; }
    public Item Item { get; set; }
    public Vendor Vendor { get; set; }
    public Money PurchasePrice { get; set; }
    public Money SellingPrice { get; set; }
}

public class Item
{
    public long Id { get; set; }
    public string Title { get; set; }
    public Category Category { get; set; }
}

public class Category
{
    public long Id { get; set; }
    public string Title { get; set; }
    public long? CategoryId { get; set; }
}

public class Vendor
{
    public long Id { get; set; }
    public string Title { get; set; }
    public Money Balance { get; set; }
    public string SyncValue { get; set; }
}

public struct Money
{
    public string Currency { get; set; }
    public double Amount { get; set; }
}

我遇到了两个挑战。

问题1: 在具有单个属性差异或简单的枚举/结构映射的情况下,我是否应始终在DTO-Entity之间创建具有映射逻辑的DTO?

例如:我的供应商实体具有 Balance 属性作为 struct (否则它可能是 Enum )。我还没有找到比解决方案更好的东西:

public async Task<Vendor> Load(long id) {
    const string query = @"
        select * from [dbo].[Vendor] where [Id] = @id
    ";

    var row = (await this._db.QueryAsync<LoadVendorRow>(query, new {id})).FirstOrDefault();
    if (row == null) {
        return null;
    }

    return row.Map();
}

在这个方法中我有2个开销代码: 1.我必须创建 LoadVendorRow 作为DTO对象; 2.我必须在 LoadVendorRow 供应商之间编写自己的映射:

public static class VendorMapper {
    public static Vendor Map(this LoadVendorRow row) {
        return new Vendor {
            Id = row.Id,
            Title = row.Title,
            Balance = new Money() {Amount = row.Balance, Currency = "RUR"},
            SyncValue = row.SyncValue
        };
    }
}

也许你可能会建议我必须存储金额和数量。货币在一起并像_db.QueryAsync<Vendor, Money, Vendor>(...)一样检索它 - 也许,你是对的。在这种情况下,如果我需要存储/检索 Enum OrderStatus 属性),我该怎么办?

var order = new Order
{
    Id = row.Id,
    ExternalOrderId = row.ExternalOrderId,
    CustomerFullName = row.CustomerFullName,
    CustomerAddress = row.CustomerAddress,
    CustomerPhone = row.CustomerPhone,
    Note = row.Note,
    CreatedAtUtc = row.CreatedAtUtc,
    DeliveryPrice = row.DeliveryPrice.ToMoney(),
    OrderStatus = EnumExtensions.ParseEnum<OrderStatus>(row.OrderStatus)
};

我可以在没有自己的实现的情况下完成这项工作并节省时间吗?

问题2: 如果我想将数据恢复到比简单单级DTO稍微复杂的实体,我该怎么办? OrderItem 是很好的例子。这是我现在用来检索它的技术:

public async Task<IList<OrderItem>> Load(long orderId) {
    const string query = @"
            select [oi].*,
                   [i].*,
                   [v].*,
                   [c].*
              from [dbo].[OrderItem] [oi]
              join [dbo].[Item] [i]
                on [oi].[ItemId] = [i].[Id]
              join [dbo].[Category] [c]
                on [i].[CategoryId] = [c].[Id]
              join [dbo].[Vendor] [v]
                on [oi].[VendorId] = [v].[Id]
             where [oi].[OrderId] = @orderId
    ";

    var rows = (await this._db.QueryAsync<LoadOrderItemRow, LoadItemRow, LoadVendorRow, LoadCategoryRow, OrderItem>(query, this.Map, new { orderId }));

    return rows.ToList();
}

正如您所看到的,我的问题1 问题迫使我为层次结构中的每个实体编写自定义映射器和DTO。这是我的映射器:

private OrderItem Map(LoadOrderItemRow row, LoadItemRow item, LoadVendorRow vendor, LoadCategoryRow category) {
    return new OrderItem {
        Id = row.Id,
        Item = item.Map(category),
        Vendor = vendor.Map(),
        PurchasePrice = row.PurchasePrice.ToMoney(),
        SellingPrice = row.SellingPrice.ToMoney()
    };
}

为了避免不必要的工作,我想消除很多地图制作者。

2 个答案:

答案 0 :(得分:9)

  

是否有一种干净的方式来追溯&amp;地图订单   具有相关属性的实体,如供应商,项目,类别等)

您没有展示Order实体,但我会以您的OrderItem为例,向您展示您不需要针对特定​​问题的映射工具(如引用的那样)。您可以通过执行以下操作检索OrderItem以及每个ItemVendor的信息:

var sql = @"
select oi.*, i.*, v.* 
from OrderItem 
    inner join Item i on i.Id = oi.ItemId
    left join Vendor v on v.Id = oi.VendorId
    left join Category c on c.Id = i.CategoryId";
var items = connection.Query<OrderItem, Item, Vendor, Category, OrderItem>(sql, 
    (oi,i,v,c)=>
    {
      oi.Item=i;oi.Item.Category=c;oi.Vendor=v;
      oi.Vendor.Balance = new Money { Amount = v.Amount, Currency = v.Currency};
      return oi; 
    });

注意:使用left join并根据您的表格结构进行相应调整。

答案 1 :(得分:0)

我不确定我100%理解你的问题。事实上,没有人试图回答它,这让我相信当我说它可能有点令人困惑时,我并不孤单。

你提到你喜欢Dapper的功能,但我没有看到你在你的例子中使用它。是否要开发Dapper的替代品?或者您不知道如何在代码中使用Dapper?

无论如何,这里有一个指向Dapper代码库的链接供您查看: https://github.com/StackExchange/dapper-dot-net

希望您能够澄清您的问题,我期待您的回复。