使用自动映射器对字符串的枚举描述



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

  • 订单状态.cs

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

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

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

在我的自动映射器中使用以下配置.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属性是"已发送",我希望它作为其关联的"描述"属性,在本例中为"已交付"。

我尝试了两种解决方案,但没有一种奏效:

使用
  1. 解析使用自动映射器函数,如此处所述,但如此处所述:

投影不支持解析使用,有关支持的操作,请参阅 LINQ 投影上的 Wiki。

  1. 使用静态方法返回"按反射字符串"中的"说明"属性。

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

但这给了我以下错误:

LINQ to Entities 无法识别方法"System.String GetEnumDescription(System.String(' 方法,并且此方法不能 转换为商店表达式。

那么,我该如何实现这一目标呢?

你可以添加一个这样的扩展方法(借用了这篇文章的逻辑(:

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"

查看 DotNetFiddle 上的工作示例

编辑1:不要认为您可以将这样的本地查找函数添加到实体的LINQ中。它只能在 LINQ 中对对象起作用。您应该寻求的解决方案可能是数据库中的域表,它允许您加入它并返回所需的列,这样您就不必对 AutoMap per 执行任何操作。

可以通过构建一个表达式来实现基于表达式的枚举描述映射,该表达式的计算结果为包含条件语句(例如 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);
}

然后,您的自动映射器映射将变为:

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

在运行时使用带有 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]

这也适用于其他实体框架数据库提供程序。

最新更新