如何在可查询投影期间使用 AutoMapper 将 int 映射到其枚举描述?
How to map an int to its enum description using AutoMapper during a queryable projection?
这里是获取其描述属性的枚举扩展方法。
public static string GetDescription(this Enum enumeration)
{
if (enumeration == null)
throw new ArgumentNullException();
var value = enumeration.ToString();
var type = enumeration.GetType();
var descriptionAttribute =
(DescriptionAttribute[]) type.GetField(value).GetCustomAttributes(typeof (DescriptionAttribute), false);
return descriptionAttribute.Length > 0 ? descriptionAttribute[0].Description : value;
}
这是源对象:
public class Account {
public int AccountId {get;set;}
public int AccountStatusId {get;set;}
}
这是枚举:
public enum AccountStatus {
[Description("N/A")]
None,
[Description("OPEN")]
Open,
[Description("CLOSED")]
Closed,
[Description("BAD CREDIT")
Problem
}
这是目标对象:
public class GetAccountResponse {
public int AccountId {get;set;}
public string Status {get;set;}
}
这是我的映射尝试(使用最新的非静态自动映射器版本)。请记住,这是在 EF 可查询投影期间。
_config = new MapperConfiguration(cfg => cfg.CreateMap<Account, GetAccountsResponse>()
.ForMember(dest => dest.Status,
opts => opts.MapFrom(src => ((AccountStatus) src.AccountStatusId).GetDescription())));
这是查询是 IQueryable<Account>
:
的投影
query.ProjectToList<GetAccountResponse>(_config);
这是我得到的异常:
Can't resolve this to Queryable Expression
如果您查看 MapFrom 方法的签名,您会注意到其中一个重载采用 Expression<Func<TSource, TMember>>
.
类型的参数
这表明您可以编写一种方法,从三元表达式构建表达式树,该表达式树可以将枚举的任何可能值转换为其适当的字符串。然后 AutoMapper 会通过 LINQ 将其转换为适当的 SQL 表达式。
这是一个仅使用枚举名称本身的示例:您应该能够直接调整它以使用您的描述:
public static class EnumerableExpressionHelper
{
public static Expression<Func<TSource, String>> CreateEnumToStringExpression<TSource, TMember>(
Expression<Func<TSource, TMember>> memberAccess, string defaultValue = "")
{
var type = typeof(TMember);
if (!type.IsEnum)
{
throw new InvalidOperationException("TMember must be an Enum type");
}
var enumNames = Enum.GetNames(type);
var enumValues = (TMember[])Enum.GetValues(type);
var inner = (Expression)Expression.Constant(defaultValue);
var parameter = memberAccess.Parameters[0];
for (int i = 0; i < enumValues.Length; i++)
{
inner = Expression.Condition(
Expression.Equal(memberAccess.Body, Expression.Constant(enumValues[i])),
Expression.Constant(enumNames[i]),
inner);
}
var expression = Expression.Lambda<Func<TSource,String>>(inner, parameter);
return expression;
}
}
您将按如下方式使用它:
CreateMap<Entry, EntryListItem>()
.ForMember(e => e.ReviewStatus,
c => c.MapFrom(EnumerableExpressionHelper.CreateEnumToStringExpression((Entry e) => e.ReviewStatus)))
这里是获取其描述属性的枚举扩展方法。
public static string GetDescription(this Enum enumeration)
{
if (enumeration == null)
throw new ArgumentNullException();
var value = enumeration.ToString();
var type = enumeration.GetType();
var descriptionAttribute =
(DescriptionAttribute[]) type.GetField(value).GetCustomAttributes(typeof (DescriptionAttribute), false);
return descriptionAttribute.Length > 0 ? descriptionAttribute[0].Description : value;
}
这是源对象:
public class Account {
public int AccountId {get;set;}
public int AccountStatusId {get;set;}
}
这是枚举:
public enum AccountStatus {
[Description("N/A")]
None,
[Description("OPEN")]
Open,
[Description("CLOSED")]
Closed,
[Description("BAD CREDIT")
Problem
}
这是目标对象:
public class GetAccountResponse {
public int AccountId {get;set;}
public string Status {get;set;}
}
这是我的映射尝试(使用最新的非静态自动映射器版本)。请记住,这是在 EF 可查询投影期间。
_config = new MapperConfiguration(cfg => cfg.CreateMap<Account, GetAccountsResponse>()
.ForMember(dest => dest.Status,
opts => opts.MapFrom(src => ((AccountStatus) src.AccountStatusId).GetDescription())));
这是查询是 IQueryable<Account>
:
query.ProjectToList<GetAccountResponse>(_config);
这是我得到的异常:
Can't resolve this to Queryable Expression
如果您查看 MapFrom 方法的签名,您会注意到其中一个重载采用 Expression<Func<TSource, TMember>>
.
这表明您可以编写一种方法,从三元表达式构建表达式树,该表达式树可以将枚举的任何可能值转换为其适当的字符串。然后 AutoMapper 会通过 LINQ 将其转换为适当的 SQL 表达式。
这是一个仅使用枚举名称本身的示例:您应该能够直接调整它以使用您的描述:
public static class EnumerableExpressionHelper
{
public static Expression<Func<TSource, String>> CreateEnumToStringExpression<TSource, TMember>(
Expression<Func<TSource, TMember>> memberAccess, string defaultValue = "")
{
var type = typeof(TMember);
if (!type.IsEnum)
{
throw new InvalidOperationException("TMember must be an Enum type");
}
var enumNames = Enum.GetNames(type);
var enumValues = (TMember[])Enum.GetValues(type);
var inner = (Expression)Expression.Constant(defaultValue);
var parameter = memberAccess.Parameters[0];
for (int i = 0; i < enumValues.Length; i++)
{
inner = Expression.Condition(
Expression.Equal(memberAccess.Body, Expression.Constant(enumValues[i])),
Expression.Constant(enumNames[i]),
inner);
}
var expression = Expression.Lambda<Func<TSource,String>>(inner, parameter);
return expression;
}
}
您将按如下方式使用它:
CreateMap<Entry, EntryListItem>()
.ForMember(e => e.ReviewStatus,
c => c.MapFrom(EnumerableExpressionHelper.CreateEnumToStringExpression((Entry e) => e.ReviewStatus)))