当前位置: 首页 > 工具软件 > AutoMapper > 使用案例 >

AutoMapper运行时通过IMappingOperationOptions映射控制

卫皓
2023-12-01

目录

介绍

背景

使用代码

结论

注意

兴趣点


介绍

AutoMapper是一个很好的工具,可以轻松地在两个类之间进行映射。但是,如果映射在NHibernate实体和DTO(数据传输对象)之间,则可能会变得很危险。使用NHibernate的延迟加载功能,这很容易导致不需要的数据库选择。AutoMapper有一些很好的特性,通过Queryable Extensions让开发人员可以控制在运行时应该和不应该映射的内容。

但是还有一个被严重低估的特性,隐藏在它的ResolutionContext->Options中,它只是在文档中简单提到:Passing in key-value to Mapper

我说的是一个名为Items的属性的小宝石,它只是一个IDictionary<string, object>

有了这个,您可以直接控制要从数据库中接收哪些数据,我将通过一个小示例向您展示。

背景

一方面拥有具有引用和列表的实体,另一方面拥有DTO,您可能会发现,AutoMapper将始终尝试尽可能映射所有内容。因此,您要么必须为不同的用例创建不同的DTO,要么只需使用NHibernates延迟加载的魔力并通过标志直接控制前端可以直接传递远程调用。

让我们看看这是如何工作的!

使用代码

考虑两个类的这个简单示例:CustomerOrders
(这只是一个基本示例,不要与真实的公司应用程序混淆。)

一方面,你得到了这些NHibernate实体:

public class Customer
{
    public virtual long Id { get; set; }
 
    public virtual string Name { get; set; }
 
    public virtual IList<Order> Orders { get; set; }
}
 
public class Order
{
    public virtual long Id { get; set; }
 
    public virtual string ProductNumber { get; set; }
 
    public virtual int Amount { get; set; }
}

以下是DTO

public class CustomerDto
{
    public long Id { get; set; }
 
    public string Name { get; set; }
 
    public IList<OrderDto> Orders { get; set; }
}
 
public class OrderDto
{
    public long Id { get; set; }
 
    public string ProductNumber { get; set; }
 
    public int Amount { get; set; }
}

现在,假设您有两个用例:

  • 仅检索用户
  • 检索用户及其订单

为此,我们需要为IMappingOperationOptions扩展类。

此扩展将从Options->Items存储和检索标志。

public static class OperationOptionExtensions
{
    // List of different keys for the Items Dictionary
 
    private const string ShouldIncludeOrdersForCustomerKey = "ShouldIncludeOrdersForCustomer";
 
    // ----
    
    /// <summary>
    /// Retreives a bool value from the Items Dictionary by key
    /// </summary>
    private static bool GetBoolValue(IMappingOperationOptions options, string key)
    {
        if (options.Items.ContainsKey(key) && options.Items[key] is bool value)
        {
            return value;
        }
 
        return false;
    }
 
    /// <summary>
    /// Saves the bool value, whenever or not the mapping should include
    /// the Orders List for Customer
    /// </summary>
    public static void IncludeOrdersForCustomer(
        this IMappingOperationOptions options,
        bool include = true)
    {
        options.Items[ShouldIncludeOrdersForCustomerKey] = include;
    }
 
    /// <summary>
    /// Mapping in Profile requests, whenever or not it should include
    /// the Orders List for Customer
    /// </summary>
    public static bool ShouldIncludeOrdersForCustomer(this IMappingOperationOptions options)
    {
        return GetBoolValue(options, ShouldIncludeOrdersForCustomerKey);
    }
}

现在我们可以配置我们的映射配置文件,告诉AutoMapper只映射Orders是否使用PreCondition操作设置了标志:

public class AutoMapperConfig : Profile
{
    public AutoMapperConfig()
    {
        CreateMap<Customer, CustomerDto>()
 
            // The PreCondition retreives the stored value
            // from the Items Dictionary inside the mapping options
            .ForMember(
                customer => customer.Orders,
                config => config.PreCondition(
                    context => context.Options.ShouldIncludeOrdersForCustomer()));
 
        CreateMap<Order, OrderDto>();
    }
}

该条件默认返回false,因此只有在运行时启用该选项时才会映射Order

考虑这个使用NHibernate的数据库访问层的简化示例:

public class DBAccess
{
    // Assume Dependency-Injection of NHibernate Session here
    public ISession Session { get; set; }
 
    private IMapper Mapper { get; }
 
    public DBAccess()
    {
        // Profile and configuration
        var config = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile<AutoMapperConfig>();
        });
 
        // Also typically by Dependency-Injection
        Mapper = config.CreateMapper();
    }
 
    /// <summary>
    /// Data-Access method: includeOrders will be requested by the front-end
    /// </summary>
    public CustomerDto GetCustomerById(long customerId, bool includeOrders)
    {
        // Retreive the Customer from database
        var customer = Session.Get<Customer>(customerId);
 
        // We directly inject the clients request for the Orders
        // into this mapping operation option
        return Mapper.Map<CustomerDto>(customer, options =>
        {
            options.IncludeOrdersForCustomer(includeOrders);
        });
    }
}

如果includeOrders设置为trueAutoMapper将通过触发NHibernates延迟加载来映射Orders

如果设置为falseAutoMapper则不会映射Orders并且DTO内的Order列表保持为空。NHibernate不会延迟加载Orders

当然,这可以通过任何其他属性来完成,并且在String-ObjectDictionary中存储任何东西的可能性是无穷无尽的。在这种情况下,它们只是更好地控制应该映射的内容的标志。

结论

使用AutoMapperOperation-Options可以让您从前到后直接控制运行时映射,从而减少类和更短的代码。

注意

我知道您也可以简单地通过代码检查includeOrders,执行另一个查询Orders并手动填写它们。但这只是提高认识的一个基本示例。我确信其他开发人员可以为很多其他的东西使用Dictionary来控制和操纵AutoMapper的行为。

兴趣点

老实说,当我第一次偶然发现这个简单的Dictionary时,它真的让我大吃一惊。

我首先想到的是这个扩展,它可以更好地控制AutoMappers的行为,而不必维护太多不同的DTO。我希望这会引起其他开发人员的注意AutoMapper,他们不喜欢它或改用其他映射器。

控制就是一切!

https://www.codeproject.com/Tips/5320490/AutoMapper-Runtime-Mapping-Control-via

 类似资料: