c# - 使用 AutoMapper 对字符串的枚举描述

c# - Enum description to string with AutoMapper

我正在尝试从我的 Order 模型投影到我的 OrderDTO 模型。 Order 有一个枚举。问题是如果我尝试从枚举中获取 Description 属性,投影将不起作用。这是我的代码:

在我的 AutoMapper.cs 中使用以下配置:

cfg.CreateMap<Order, OrderDTO>().ForMember(
    dest => dest.Status,
    opt => opt.MapFrom(src => src.Status.ToString())
);

投影有效,但我得到一个 OrderDTO 对象,如下所示:

 - Id: 1
 - OrderLines: List<OrderLines>
 - Sent //I want "Delivered"!

我不希望 Status 属性 成为 "Sent",我希望它成为其关联的 Description 属性,在这种情况下,"Delivered".

我尝试了两种解决方案,其中 none 有效:

  1. 使用 ResolveUsing AutoMapper 函数,如解释的那样 , but, as it's stated here:

ResolveUsing is not supported for projections, see the wiki on LINQ projections for supported operations.

  1. 使用静态方法 return String 中的 Description 属性。

    cfg.CreateMap<Order, OrderDTO>().ForMember(
        dest => dest.Status,
        opt => opt.MapFrom(src => EnumHelper<OrderStatus>.GetEnumDescription(src.Status.ToString()))
    );
    

但这给了我以下错误:

LINQ to Entities does not recognize the method 'System.String GetEnumDescription(System.String)' method, and this method cannot be translated into a store expression.

那么,我该如何实现呢?

您可以添加这样的扩展方法(借用 this post 的逻辑):

public static class ExtensionMethods
{
    static public string GetDescription(this OrderStatus This)
    {
        var type = typeof(OrderStatus);
        var memInfo = type.GetMember(This.ToString());
        var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
        return ((DescriptionAttribute)attributes[0]).Description;
    }
}

然后在您的地图中访问它:

cfg => 
{
    cfg.CreateMap<Order, OrderDTO>()
    .ForMember
    (
        dest => dest.Status,
        opt => opt.MapFrom
        (
            src => src.Status.GetDescription()
        )
    );
}

这就是您所要求的结果:

Console.WriteLine(dto.Status);  //"Delivered", not "sent"

See a working example on DotNetFiddle

Edit1:不要以为你可以将这样的本地查找函数添加到 LINQ to entities。它只适用于 LINQ to objects。您应该寻求的解决方案可能是数据库中的域 table 允许您加入它和 return 您想要的列,这样您就不必使用 AutoMapper 做任何事情。

您可以通过构建一个表达式来实现基于表达式的枚举描述映射,该表达式的计算结果为包含条件语句的字符串(例如 switch/if/case,具体取决于提供程序如何实现它),枚举描述作为结果。

因为可以提前提取枚举描述,所以我们可以获得它们并将它们用作条件表达式结果的常量。

注意: 我使用了上面的扩展方法 GetDescription() 但您可以使用您需要的任何类型的属性提取。

  public static Expression<Func<TEntity, string>> CreateEnumDescriptionExpression<TEntity, TEnum>(
    Expression<Func<TEntity, TEnum>> propertyExpression)
     where TEntity : class
     where TEnum : struct
  {
     // Get all of the possible enum values for the given enum type
     var enumValues = Enum.GetValues(typeof(TEnum)).Cast<Enum>();

     // Build up a condition expression based on each enum value
     Expression resultExpression = Expression.Constant(string.Empty);
     foreach (var enumValue in enumValues)
     {
        resultExpression = Expression.Condition(
           Expression.Equal(propertyExpression.Body, Expression.Constant(enumValue)),
           // GetDescription() can be replaced with whatever extension 
           // to get you the needed enum attribute.
           Expression.Constant(enumValue.GetDescription()),
           resultExpression);
     }

     return Expression.Lambda<Func<TEntity, string>>(
        resultExpression, propertyExpression.Parameters);
  }

然后您的 Automapper 映射变为:

  cfg.CreateMap<Order, OrderDTO>().ForMember(
     dest => dest.Status, opts => opts.MapFrom(
             CreateEnumDescriptionExpression<Order, OrderStatus>(src => src.Status)));

当使用 Entity Framework 和 SQL 服务器提供商在运行时对其进行评估时,生成的 SQL 将类似于:

SELECT 
   -- various fields such as Id
   CASE WHEN (2 = [Extent1].[Status]) THEN N'Delivered' 
        WHEN (1 = [Extent1].[Status]) THEN N'Processing' 
        WHEN (0 = [Extent1].[Status]) THEN N'Paid' ELSE N'' END AS [C1]
FROM [Orders] as [Extent1]

这也适用于其他 Entity Framework 数据库提供商。