我真的很喜欢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; }
}
Run Code Online (Sandbox Code Playgroud)
两个挑战一直困扰着我.
问题1: 在我有单个属性差异或简单的枚举/结构映射的情况下,我是否应该始终在DTO-Entity之间创建具有映射逻辑的DTO?
例如:有我的Vendor实体,它将Balance属性作为结构(否则它可能是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();
}
Run Code Online (Sandbox Code Playgroud)
在这个方法中,我有2个开销代码:1.我必须创建LoadVendorRow作为DTO对象; 2.我必须在LoadVendorRow和Vendor之间编写自己的映射:
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
};
}
}
Run Code Online (Sandbox Code Playgroud)
也许你可能会建议我必须将金额和货币存储在一起并检索它_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)
};
Run Code Online (Sandbox Code Playgroud)
我可以在没有自己实现的情况下完成这项工作并节省时间吗
问题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();
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,我的问题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()
};
}
Run Code Online (Sandbox Code Playgroud)
我想消除很多地图制作工具以防止不必要的工作.
是否有一种干净的方法来检索和映射Order实体与相关属性,如供应商,项目,类别等)
您没有展示您的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;
});
Run Code Online (Sandbox Code Playgroud)
注意:left join根据您的表格结构使用和调整它.
小智 1
我不确定我是否 100% 理解你的问题。事实上,还没有人试图回答这个问题,这让我相信,当我说这个问题可能有点令人困惑时,我并不孤单。
您提到您喜欢 Dapper 的功能,但我没有看到您在示例中使用它。您是否想开发 Dapper 的替代品?或者您不知道如何在代码中使用 Dapper?
无论如何,这里有一个 Dapper 代码库的链接供您查看: https: //github.com/StackExchange/dapper-dot-net
希望您能解答您的疑问,期待您的回复。