我真的很喜欢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()
};
}
为了避免不必要的工作,我想消除很多地图制作者。
答案 0 :(得分:9)
是否有一种干净的方式来追溯&amp;地图订单 具有相关属性的实体,如供应商,项目,类别等)
您没有展示Order
实体,但我会以您的OrderItem
为例,向您展示您不需要针对特定问题的映射工具(如引用的那样)。您可以通过执行以下操作检索OrderItem
以及每个Item
和Vendor
的信息:
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
希望您能够澄清您的问题,我期待您的回复。