一般来说,您应该存储
enum
在视图模型中设置值,并让视图使用友好的描述(使用扩展方法)或它喜欢的任何格式对其进行格式化。
但是,假设出于某种原因,您需要在LINQ to Entities查询中使用该功能它可以通过动态构建与EF兼容的值来实现,例如:
source == value1 ? description1 :
source == value2 ? description2 :
â¦
source == valueN ? descriptionN :
""
为了做到这一点,首先你需要
Description
方法
通用的
. 这样,调用将包含有关实际枚举类型的信息,稍后我们将需要这些信息(因为我们将处理查询表达式树,所以实际上不会调用该方法):
public static class DescriptionExtensions
{
public static string Description<TEnum>(this TEnum source) where TEnum : struct, Enum
=> typeof(TEnum).GetField(source.ToString()).Description();
public static string Description(this FieldInfo source)
=> source.GetCustomAttribute<DescriptionAttribute>()?.Description ?? source.Name;
}
注意,这是利用引入的C#7.3
enum constraint
. 适用于C#7.3前
where TEnum : struct
断言
typeof(TEnum).IsEnum
方法内部。
然后我们将使用一个自定义扩展方法来查找
说明
方法在查询表达式树中调用,并将其替换为上述值以描述转换表达式,该表达式基于
TEnum
呼叫的类型。与通常的表达式树一样,处理是通过自定义
ExpressionVisitor
.
public static class QueryConverter
{
public static IQueryable<T> Convert<T>(this IQueryable<T> source)
{
var expression = new ExpressionConverter().Visit(source.Expression);
if (expression == source.Expression) return source;
return source.Provider.CreateQuery<T>(expression);
}
class ExpressionConverter : ExpressionVisitor
{
static readonly MethodInfo EnumDescriptionMethod = Expression.Call(
typeof(DescriptionExtensions), nameof(DescriptionExtensions.Description), new[] { typeof(ExpressionType) },
Expression.Constant(default(ExpressionType)))
.Method.GetGenericMethodDefinition();
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.IsGenericMethod && node.Method.GetGenericMethodDefinition() == EnumDescriptionMethod)
return TranslateEnumDescription(Visit(node.Arguments[0]));
return base.VisitMethodCall(node);
}
static Expression TranslateEnumDescription(Expression arg)
{
var names = Enum.GetNames(arg.Type);
var values = Enum.GetValues(arg.Type);
Expression result = Expression.Constant("");
for (int i = names.Length - 1; i >= 0; i--)
{
var value = values.GetValue(i);
var description = arg.Type.GetField(names[i], BindingFlags.Public | BindingFlags.Static).Description();
result = Expression.Condition(
Expression.Equal(arg, Expression.Constant(value)),
Expression.Constant(description),
result);
}
return result;
}
}
}
现在你只需要让EF兼容
IQueryable<T>
来自包含
说明
调用是调用自定义
Convert
最后的方法:
var query = OrderItems(items)
.Skip(pageIndex * pageSize)
.Take(pageSize)
.Select(item => new ItemViewModel
{
Id = item.Id,
Description = item.Description,
ItemPriority = item.Priority.Description(),
})
.Convert();