简体   繁体   English

c# - 使用 AutoMapper 将描述枚举为字符串

[英]c# - Enum description to string with AutoMapper

I'm trying to project from my Order model to my OrderDTO model.我正在尝试从我的Order模型OrderDTO到我的OrderDTO模型。 Order has an enum. Order有一个枚举。 The problem is that projection doesn't work if I try to to get the Description attribute from the Enum.问题是,如果我尝试从枚举中获取Description属性,则投影不起作用。 Here it's my code:这是我的代码:

  • OrderStatus.cs :订单状态.cs

     public enum OrderStatus { [Description("Paid")] Paid, [Description("Processing")] InProcess, [Description("Delivered")] Sent }
  • Order.cs :订单.cs

     public class Order { public int Id { get; set; } public List<OrderLine> OrderLines { get; set; } public OrderStatus Status { get; set; } }
  • OrderDTO.cs :订购DTO.cs

     public class OrderDTO { public int Id { get; set; } public List<OrderLineDTO> OrderLines { get; set; } public string Status { get; set; } }

With this following configuration in my AutoMapper.cs :在我的AutoMapper.cs 中使用以下配置:

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

Projection works, but I get an OrderDTO object like this:投影有效,但我得到一个像这样的OrderDTO对象:

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

I don't want Status property to be "Sent", I want it to be as its associated Description attribute, in this case, "Delivered".我不希望Status属性为“Sent”,我希望它作为其关联的Description属性,在本例中为“Delivered”。

I have tried two solutions and none of them have worked:我尝试了两种解决方案,但都没有奏效:

  1. Using ResolveUsing AutoMapper function as explained here , but, as it's stated here :使用ResolveUsing AutoMapper功能解释这里,但是,因为它规定在这里

ResolveUsing is not supported for projections, see the wiki on LINQ projections for supported operations.投影不支持 ResolveUsing,有关支持的操作,请参阅 LINQ 投影上的 wiki。

  1. Using a static method to return the Description attribute in String by Reflection.使用静态方法通过反射返回 String 中的Description属性。

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

But this gives me the following error:但这给了我以下错误:

LINQ to Entities does not recognize the method 'System.String GetEnumDescription(System.String)' method, and this method cannot be translated into a store expression. LINQ to Entities 无法识别“System.String GetEnumDescription(System.String)”方法,并且此方法无法转换为存储表达式。

Then, how can I achieve this?那么,我怎样才能做到这一点?

You can add an extension method like this one (borrowed the logic from 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;
    }
}

Then access it in your map:然后在您的地图中访问它:

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

This results in what you are asking for:这会导致您要求的内容:

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

See a working example on DotNetFiddle查看 DotNetFiddle 上的一个工作示例

Edit1 : Don't think you can add a local look up function like that to LINQ to entities. Edit1 :不要认为您可以向 LINQ to entity 添加这样的本地查找功能。 It would only work in LINQ to objects.它只能在 LINQ to objects 中工作。 The solution you should pursue perhaps is a domain table in the database that allows you to join to it and return the column that you want so that you don't have to do anything with AutoMapper.您应该寻求的解决方案可能是数据库中的域表,它允许您加入它并返回您想要的列,这样您就不必对 AutoMapper 执行任何操作。

You can achieve an expression based enum description mapping by building up an expression that evaluates to a string containing a condition statement (such as switch/if/case depending on how the provider implements it) with the enum descriptions as the results.您可以通过构建一个表达式来实现基于表达式的枚举描述映射,该表达式的计算结果为包含条件语句的字符串(例如 switch/if/case,具体取决于提供程序的实现方式),并将枚举描述作为结果。

Because the enum descriptions can be extracted ahead of time we can obtain them and use them as a constant for the result of the condition expression.因为可以提前提取枚举描述,所以我们可以获取它们并将它们用作条件表达式结果的常量。

Note: I've used the above extension method GetDescription() but you can use whatever flavour of attribute extraction you need.注意:我使用了上面的扩展方法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);
  }

Then your Automapper mapping becomes:然后您的 Automapper 映射变为:

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

When this is evaluated at runtime using Entity Framework with SQL server provider, the resulting SQL will be something like:当在运行时使用带有 SQL 服务器提供程序的 Entity Framework 进行评估时,生成的 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]

This should also work for other Entity Framework DB providers.这也适用于其他实体框架 DB 提供程序。

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

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