目录
AutoMapper是一个很好的工具,可以轻松地在两个类之间进行映射。但是,如果映射在NHibernate实体和DTO(数据传输对象)之间,则可能会变得很危险。使用NHibernate的延迟加载功能,这很容易导致不需要的数据库选择。AutoMapper有一些很好的特性,通过Queryable Extensions让开发人员可以控制在运行时应该和不应该映射的内容。
但是还有一个被严重低估的特性,隐藏在它的ResolutionContext->Options中,它只是在文档中简单提到:Passing in key-value to Mapper。
我说的是一个名为“Items”的属性的小宝石,它只是一个IDictionary<string, object>。
有了这个,您可以直接控制要从数据库中接收哪些数据,我将通过一个小示例向您展示。
一方面拥有具有引用和列表的实体,另一方面拥有DTO,您可能会发现,AutoMapper将始终尝试尽可能映射所有内容。因此,您要么必须为不同的用例创建不同的DTO,要么只需使用NHibernates延迟加载的魔力并通过标志直接控制前端可以直接传递远程调用。
让我们看看这是如何工作的!
考虑两个类的这个简单示例:Customer和Orders。
(这只是一个基本示例,不要与真实的公司应用程序混淆。)
一方面,你得到了这些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设置为true,AutoMapper将通过触发NHibernates延迟加载来映射Orders。
如果设置为false,AutoMapper则不会映射Orders并且DTO内的Order列表保持为空。NHibernate不会延迟加载Orders。
当然,这可以通过任何其他属性来完成,并且在String
-Object
的Dictionary中存储任何东西的可能性是无穷无尽的。在这种情况下,它们只是更好地控制应该映射的内容的标志。
使用AutoMapper的Operation-Options可以让您从前到后直接控制运行时映射,从而减少类和更短的代码。
我知道您也可以简单地通过代码检查includeOrders,执行另一个查询Orders并手动填写它们。但这只是提高认识的一个基本示例。我确信其他开发人员可以为很多其他的东西使用Dictionary来控制和操纵AutoMapper的行为。
老实说,当我第一次偶然发现这个简单的Dictionary时,它真的让我大吃一惊。
我首先想到的是这个扩展,它可以更好地控制AutoMappers的行为,而不必维护太多不同的DTO。我希望这会引起其他开发人员的注意AutoMapper,他们不喜欢它或改用其他映射器。
控制就是一切!
https://www.codeproject.com/Tips/5320490/AutoMapper-Runtime-Mapping-Control-via