Dapper: hiérarchie de cartographie et propriété unique différente

.net c# dapper

Question

J'aime beaucoup la simplicité et les possibilités de Dapper. Je voudrais utiliser Dapper pour résoudre des problèmes communs auxquels je suis confronté au quotidien. Celles-ci sont décrites ci-dessous.

Voici mon modèle simple

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; }
}

Deux défis m'ont frappé.

Question 1: Dois-je toujours créer un DTO avec une logique de mappage entre l'entité DTO dans les cas où j'ai une différence de propriété unique ou un mappage simple enum / struct?

Par exemple: Il y a mon entité fournisseur, qui a la propriété Balance en tant que structure (sinon elle pourrait être Enum ). Je n'ai rien trouvé de mieux que cette solution:

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();
}

Dans cette méthode, j'ai 2 codes généraux: 1. Je dois créer LoadVendorRow en tant qu'objet DTO; 2. Je dois écrire mon propre mapping entre LoadVendorRow et 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
        };
    }
}

Peut-être pourriez-vous suggérer que je _db.QueryAsync<Vendor, Money, Vendor>(...) stocker le montant et la devise ensemble et les récupérer comme _db.QueryAsync<Vendor, Money, Vendor>(...) - Peut-être avez-vous raison. Dans ce cas, que dois-je faire si je dois stocker / retracer Enum (propriété 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)
};

Puis-je faire ce travail sans mes propres implémentations et gagner du temps?

Question 2: Que dois-je faire si je souhaite restaurer des données vers des entités légèrement plus complexes que le simple DTO à un seul niveau? OrderItem est un bel exemple. C'est la technique que j'utilise pour la récupérer dès maintenant:

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();
}

Comme vous pouvez le voir, le problème de ma question 1 me contraint à écrire des mappeurs personnalisés et DTO pour chaque entité de la hiérarchie. C'est mon mappeur:

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()
    };
}

Il y a beaucoup de mappers que je voudrais éliminer pour éviter tout travail inutile.

Réponse populaire

Existe-t-il un moyen propre de retracer et de mapper l'entité Ordre avec des propriétés relatives telles que le fournisseur, l'article, la catégorie, etc.)

Vous ne montrez pas votre entité Order mais je prends votre OrderItem comme exemple et vous montre que vous n'avez pas besoin d'un outil de mappage pour le problème spécifique (tel que cité). Vous pouvez récupérer les Item OrderItem ainsi que les informations sur chaque Item et chaque Vendor en procédant comme suit:

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; 
    });

REMARQUE: L’utilisation de left join et ajuste en conséquence en fonction de la structure de votre table.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi