简体   繁体   English

将 API 结果映射到 class,我应该使用反射吗?这是 DataAdapter 设计模式的一个例子吗?

[英]Mapping API result to class, should I use reflection and is this a case of DataAdapter design pattern?

It is my first time using an API and I am not entirely sure about the good practices regarding it.这是我第一次使用 API,我不完全确定关于它的良好做法。 I am using a third party API that returns me a class 'A' in C#, that is not quite compatible with my DTO class 'MyA'.我正在使用第三方 API,它返回 C# 中的 class 'A',这与我的 DTO class 'MyA' 不太兼容。 I obviously have to map it to my entity, but I don't know what the best way to do it is.我显然必须 map 到我的实体,但我不知道最好的方法是什么。 I have read of the Data Adapter pattern and from what I understand, it has the purpose to act as a bridge between two incompatible interfaces.我读过数据适配器模式,据我了解,它的目的是充当两个不兼容接口之间的桥梁。 Those classes share some properties, but differ in more important ones, so I wonder if this pattern is what I am looking for.这些类共享一些属性,但在更重要的属性上有所不同,所以我想知道这种模式是否是我正在寻找的。

I believe I could create a base class that uses reflection to map the matching properties for the generic case, and build on top of that for the different properties.我相信我可以创建一个基础 class,它使用反射到 map 通用案例的匹配属性,并在此基础上构建不同的属性。 I have however heard that reflections should be avoided, so another approach would be to manually map even the shared properties.然而,我听说应该避免反射,所以另一种方法是手动 map 甚至共享属性。 This would probably be easier to do now, but it is not maintainable at all, involves some avoidable logic repetition, and overall contradicts good design practices.现在这可能更容易做到,但它根本不可维护,涉及一些本可以避免的逻辑重复,并且总体上与良好的设计实践相矛盾。

Would my situation be considered a case usage of DataAdapter?我的情况会被视为 DataAdapter 的案例使用吗? From the definitions I read, I think yes, however the posts I found were predominantly about using different data sources (XML,JSON,CSV), not just classes in the same language with some properties that differ?从我阅读的定义来看,我认为是的,但是我发现的帖子主要是关于使用不同的数据源(XML、JSON、CSV),而不仅仅是使用相同语言的具有不同属性的类?

The question is very hard to read but I suspect it asks how to map between two similar DTO types.这个问题很难读懂,但我怀疑它问的是如何在两个相似的 DTO 类型之间进行 map。 There are libraries that do this, for example AutoMapper .有库可以执行此操作,例如AutoMapper

AutoMapper can map between types based on property names and some conventions that allow it to handle eg flattening, or mapping method results to properties, eg GetTotal() to Total {get;set;} . AutoMapper 可以 map 基于属性名称的类型和一些允许它处理的约定,例如展平,或将方法结果映射到属性,例如GetTotal()Total {get;set;} It's possible to configure specific mappings for properties with different names, but if the types are too different, it may not be worth it.可以为具有不同名称的属性配置特定映射,但如果类型差异太大,则可能不值得。

AutoMapper uses Reflection to find which properties to map but caches that information instead of recalculating it every time. AutoMapper 使用反射来查找 map 的哪些属性,但会缓存该信息而不是每次都重新计算它。

For example, given a User and UserDTO:例如,给定一个 User 和 UserDTO:

public class User
{
    public string UserName {get;set;}
    public string Email {get;set;}
    public string Address {get;set;}
}

public class UserDTO
{
    public string UserName {get;set;}
    public string Email {get;set;}
}

AutoMapper can map between them directly: AutoMapper 可以直接在它们之间 map :

var config = new MapperConfiguration(cfg => {
    cfg.CreateMap<User, UserDTO>();
})
;
var mapper = config.CreateMapper();
...
UserDto dto = mapper.Map<UserDto>(user);

The Flattening example shows how properties of a complex model can be mapped to a simpler DTO based on naming conventions, eg Customer.Name gets mapped to CustomerName . Flattening 示例显示了如何根据命名约定将复杂的 model 的属性映射到更简单的 DTO,例如Customer.Name映射到CustomerName It also shows how Order.GetTotal() gets mapped to它还显示了Order.GetTotal()如何映射到

This complex source model:本复源model:

public class Order
{
    private readonly IList<OrderLineItem> _orderLineItems = new List<OrderLineItem>();

    public Customer Customer { get; set; }

    public OrderLineItem[] GetOrderLineItems()
    {
        return _orderLineItems.ToArray();
    }

    public void AddOrderLineItem(Product product, int quantity)
    {
        _orderLineItems.Add(new OrderLineItem(product, quantity));
    }

    public decimal GetTotal()
    {
        return _orderLineItems.Sum(li => li.GetTotal());
    }
}

public class Product
{
    public decimal Price { get; set; }
    public string Name { get; set; }
}

public class OrderLineItem
{
    public OrderLineItem(Product product, int quantity)
    {
        Product = product;
        Quantity = quantity;
    }

    public Product Product { get; private set; }
    public int Quantity { get; private set;}

    public decimal GetTotal()
    {
        return Quantity*Product.Price;
    }
}

public class Customer
{
    public string Name { get; set; }
}

can be mapped to this simple DTO simply using conventions:可以简单地使用约定映射到这个简单的 DTO:

public class OrderDto
{
    public string CustomerName { get; set; }
    public decimal Total { get; set; }
}

without special configuration:无需特殊配置:

// Configure AutoMapper

var configuration = new MapperConfiguration(cfg =>{ 
    cfg.CreateMap<Order, OrderDto>();
});
// Complex model

var customer = new Customer
    {
        Name = "George Costanza"
    };
var order = new Order
    {
        Customer = customer
    };
var bosco = new Product
    {
        Name = "Bosco",
        Price = 4.99m
    };
order.AddOrderLineItem(bosco, 15);

// Perform mapping

OrderDto dto = mapper.Map<Order, OrderDto>(order);

It's possible to specify complex mappings when it's not possible to map by convention:当按照惯例不可能达到 map 时,可以指定复杂的映射:

public class CalendarEvent
{
    public DateTime Date { get; set; }
    public string Title { get; set; }
}

public class CalendarEventForm
{
    public DateTime EventDate { get; set; }
    public int EventHour { get; set; }
    public int EventMinute { get; set; }
    public string Title { get; set; }
}

In this case each Event property can be mapped individually, with a full mapping expression:在这种情况下,可以使用完整的映射表达式单独映射每个Event属性:

var configuration = new MapperConfiguration(cfg =>
  cfg.CreateMap<CalendarEvent, CalendarEventForm>()
    .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date))
    .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.Date.Hour))
    .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.Date.Minute)));

This makes sense when only a few properties need custom mapping, eg when only 2 out of 10 properties need mapping.当只有少数属性需要自定义映射时,这是有意义的,例如,当 10 个属性中只有 2 个需要映射时。 It also helps when mappings can occur in many places.当映射可以出现在许多地方时,它也有帮助。

For this example though, 3 out of 4 properties need mapping and the "manual" code is actually simpler than the configuration itself:不过对于这个例子,4 个属性中有 3 个需要映射,“手动”代码实际上比配置本身更简单:

var dest=new CalendarEventForm {
    Title     =scr.Title,
    EventDate =src.Date.Date,
    EventHour =src.Date.Hour,
    EventMinute =src.Date.Minute,
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM