2015-08-13 7 views
16

Мне очень нравится простота и возможности Даппера. Я хотел бы использовать 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 с отображением логики между DTO-Сущности в тех случаях, когда у меня есть разница одного свойства или простое перечисление отображение/STRUCT?

Например: Существует мой Vendor объект, который имеет Баланс свойство как структуры (в противном случае он может быть 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 и 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 
     }; 
    } 
} 

Может быть, вы могли бы предположить, что я должен хранить сумму & валюты вместе и получить его как _db.QueryAsync<Vendor, Money, Vendor>(...) - Возможно, вы правы , В этом случае, что мне делать, если мне нужно хранить/извлекать Enum (OrderStatus property)?

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

Вы пытались использовать что-то вроде [AutoMapper] (http://automapper.org/) или [ValueInjecter] (https://valueinjecter.codeplex.com/), чтобы автоматически сопоставлять объекты или вы не хотите использовать какие-либо картографы? – pasty

+0

Вы загружаете данные из db в 'LoadVendorRow' и имеете поле' Balance'. Так что же это на самом деле в вашей таблице «Продавец»? Имеются ли в нем поля «Валюта и сумма»? Поле Balance совпадает с полем Amount в вашем db? Также кажется, что каждый раз, когда есть поле «деньги/стоимость», вы конвертируете его в свою структуру «Деньги». Так всегда есть поля «деньги + валюта» в вашей БД? Например, DeliveryPrice и DeliveryPriceCurrency? –

+0

@pasty, нет, я не пробовал, но должен ли он дать мне какие-нибудь льготы? – FSou1

ответ

0

Я не уверен, что я понимаю ваш вопрос на 100%. И тот факт, что никто еще не пытался ответить на него, заставляет меня поверить, что я не одинок, когда говорю, что это может быть немного запутанным.

Вы упомянули, что любите функциональность Dapper, но я не вижу, чтобы вы использовали ее в своих примерах. Разве вы хотите разработать альтернативу Dapper? Или вы не знаете, как использовать Dapper в своем коде?

В любом случае, вот ссылка на базу коды щеголеватой для вашего обзора: https://github.com/StackExchange/dapper-dot-net

Надеясь, что вы были бы в состоянии прояснить ваши вопросы, я с нетерпением ждет вашего ответа.

+1

Он использует Dapper ... См. Код QueryAsync. –

+0

Мой вопрос: как мог dapper помочь мне работать с объектами домена, должен ли я всегда писать свой собственный код картографа и т. Д. – FSou1

8

Есть чистый способ retrive & карты заказа объекта с относительными свойствами, как поставщик, Предмет, категорией и т.д.)

Вы не показывая свою Order сущности, но я возьму вашу OrderItem, как пример и покажите, что вам не нужен инструмент сопоставления для конкретной проблемы (как указано). Вы можете извлечь OrderItem S вместе с информацией 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 и настроить его соответствующим образом на основе вашей структуры таблицы.

+0

Как вы могли заметить, я не смог восстановить объекты домена 'OrderItem' и' Vendor', потому что у него есть 'Balance' свойство, которое является структурой, и мне нужно написать mapper. В этом случае нет никакого отличия от кода, который уже предоставил мой вопрос. Я думаю, что это не совсем понятно, и попытайтесь найти более чистую. – FSou1

+0

Не мешайте вам делать сопоставление в выражении - см. Мое редактирование 'Новые деньги'. Это предполагает, что у вас есть как сумма, так и валюта в базе данных, как вы упомянули в своем комментарии к своему вопросу. –

+0

У продавца нет свойства 'Amount' в базе данных - есть свойство' Balance' с десятичным типом – FSou1