Fla*_*ack 3 c# linq asp.net-mvc entity-framework
我首先使用EF代码编写ASP.NET MVC项目,我面临的情况是需要通过枚举描述进行排序:
public partial class Item
{
public enum MyEnumE
{
[Description("description of enum1")]
Enum1,
[Description("description of enum2")]
Enum2,
...
}
public MyEnumE MyEnum { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这是Search和SortAndPaginate功能:
public async Task<IPagedList<Item>> Search(ItemCriteria criteria, SortableTypeE sortName, SortOrder.TypeE sortOrder, int pageNb)
{
var itemFilter = GenerateFilter(criteria);
var items = entities.Items.Where(itemFilter);
return await SortAndPaginate(items, sortName, sortOrder, pageNb);
}
private async Task<IPagedList<Item>> SortAndPaginate(IQueryable<Item> items, SortableTypeE sortName, SortOrder.TypeE sortOrder, int pageNb)
{
IOrderedQueryable<Item> result = null;
switch (sortName)
{
...
case SortableTypeE.Type:
result = sortOrder == SortOrder.TypeE.ASC
? items.OrderBy(i => i.MyEnum.GetDescription())
: items.OrderByDescending(i => i.MyEnum.GetDescription());
result = result.ThenBy(i => i.SomeOtherProperty);
break;
...
}
if (result != null)
{
return await result.ToPagedListAsync(pageNb, 10);
}
return PagedListHelper.Empty<Item>();
}
Run Code Online (Sandbox Code Playgroud)
问题是Item桌子可能非常庞大.
我考虑过之后立即调用ToListAsync,entities.Items.Where(itemFilter)但这将取回所有过滤的项目,虽然我只需要一页.听起来不是一个好主意.
但是,如果我不这样做,那EF将不会知道GetDescription()mathod,我只能考虑两个解决方案:
- 将我的数据库列更改为字符串(枚举描述)而不是枚举本身(但听起来像是对我的黑客攻击)
- 或者MyEnumE直接在enum声明中按字母顺序排列组件(看起来很脏,也很难维护)
我很困惑,因为我关注性能,如果我ToListAsync在过滤后调用,所有其他解决方案看起来很脏,我绝对需要IPagedList从Search方法返回.
有人会对如何处理这个问题有所了解吗?
非常感谢.
UPDATE
这是GetDescription方法(必要时可以更改):
public static string GetDescription(this Enum e)
{
FieldInfo fi = e.GetType().GetField(e.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
return attributes[0].Description;
else
return e.ToString();
}
Run Code Online (Sandbox Code Playgroud)
解决方案
我最终会选择Ivan Stoev的建议,因为我的项目主要基于Linq(使用Linq而不是存储过程等),因此这个解决方案似乎比创建引用表更适合我的特定情况.
但是Niyoko Yuliawan的和Michael Freidgeim的也真的很好的答案对我来说,任何人阅读这篇文章,并有多个数据库的做法应该去他们的解决方案;)
非常感谢大家.
您可以通过将枚举投影到自定义值并对其进行排序来实现。
例子:
items
.Select(x=> new
{
x,
Desc = (
x.Enum == Enum.One ? "Desc One"
: x.Enum == Enum.Two ? "Desc Two"
... and so on)
})
.OrderBy(x=>x.Desc)
.Select(x=>x.x);
Run Code Online (Sandbox Code Playgroud)
实体框架然后将生成这样的 SQL
SELECT
*
FROM
YourTable
ORDER BY
CASE WHEN Enum = 1 THEN 'Desc One'
WHEN Enum = 2 THEN 'Desc Two'
...and so on
END
Run Code Online (Sandbox Code Playgroud)
如果你有很多这样的查询,你可以创建扩展方法
public static IQueryable<Entity> OrderByDesc(this IQueryable<Entity> source)
{
return source.Select(x=> new
{
x,
Desc = (
x.Enum == Enum.One ? "Desc One"
: x.Enum == Enum.Two ? "Desc Two"
... and so on)
})
.OrderBy(x=>x.Desc)
.Select(x=>x.x);
}
Run Code Online (Sandbox Code Playgroud)
并在需要时调用它
var orderedItems = items.OrderByDesc();
Run Code Online (Sandbox Code Playgroud)
另一种替代解决方案是创建附加表,将枚举值映射到枚举描述并将您的表连接到该表。此解决方案的性能会更高,因为您可以在枚举描述列上创建索引。
如果你想要基于你的枚举描述属性的动态表达式,你可以自己构建
public class Helper
{
public MyEntity Entity { get; set; }
public string Description { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
public static string GetDesc(MyEnum e)
{
var type = typeof(MyEnum);
var memInfo = type.GetMember(e.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute),
false);
return ((DescriptionAttribute)attributes[0]).Description;
}
private static Expression<Func<MyEntity, Helper>> GetExpr()
{
var descMap = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.ToDictionary(value => value, GetDesc);
var paramExpr = Expression.Parameter(typeof(MyEntity), "x");
var expr = (Expression) Expression.Constant(string.Empty);
foreach (var desc in descMap)
{
// Change string "Enum" below with your enum property name in entity
var prop = Expression.Property(paramExpr, typeof(MyEntity).GetProperty("Enum"));
expr = Expression.Condition(Expression.Equal(prop, Expression.Constant(desc.Key)),
Expression.Constant(desc.Value), expr);
}
var newExpr = Expression.New(typeof(Helper));
var bindings = new MemberBinding[]
{
Expression.Bind(typeof(Helper).GetProperty("Entity"), paramExpr),
Expression.Bind(typeof(Helper).GetProperty("Description"), expr)
};
var body = Expression.MemberInit(newExpr, bindings);
return (Expression<Func<MyEntity, Helper>>) Expression.Lambda(body, paramExpr);
}
Run Code Online (Sandbox Code Playgroud)
var e = GetExpr();
items.Select(e)
.OrderBy(x => x.Description)
.Select(x => x.Entity);
Run Code Online (Sandbox Code Playgroud)
我会用动态表达.它更灵活,可以轻松更改,不会影响数据库表和查询.
但是,我不是按数据库中的描述字符串排序,而是在内存中创建有序映射,将int"order"值与每个枚举值相关联,如下所示:
public static class EnumHelper
{
public static Expression<Func<TSource, int>> DescriptionOrder<TSource, TEnum>(this Expression<Func<TSource, TEnum>> source)
where TEnum : struct
{
var enumType = typeof(TEnum);
if (!enumType.IsEnum) throw new InvalidOperationException();
var body = ((TEnum[])Enum.GetValues(enumType))
.OrderBy(value => value.GetDescription())
.Select((value, ordinal) => new { value, ordinal })
.Reverse()
.Aggregate((Expression)null, (next, item) => next == null ? (Expression)
Expression.Constant(item.ordinal) :
Expression.Condition(
Expression.Equal(source.Body, Expression.Constant(item.value)),
Expression.Constant(item.ordinal),
next));
return Expression.Lambda<Func<TSource, int>>(body, source.Parameters[0]);
}
public static string GetDescription<TEnum>(this TEnum value)
where TEnum : struct
{
var enumType = typeof(TEnum);
if (!enumType.IsEnum) throw new InvalidOperationException();
var name = Enum.GetName(enumType, value);
var field = typeof(TEnum).GetField(name, BindingFlags.Static | BindingFlags.Public);
return field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? name;
}
}
Run Code Online (Sandbox Code Playgroud)
用法如下:
case SortableTypeE.Type:
var order = EnumHelper.DescriptionOrder((Item x) => x.MyEnum);
result = sortOrder == SortOrder.TypeE.ASC
? items.OrderBy(order)
: items.OrderByDescending(order);
result = result.ThenBy(i => i.SomeOtherProperty);
break;
Run Code Online (Sandbox Code Playgroud)
这将生成如下表达式:
x => x.MyEnum == Enum[0] ? 0 :
x.MyEnum == Enum[1] ? 1 :
...
x.MyEnum == Enum[N-2] ? N - 2 :
N - 1;
Run Code Online (Sandbox Code Playgroud)
其中0,1,.. N-2是按描述排序的值列表中的对应索引.
| 归档时间: |
|
| 查看次数: |
2051 次 |
| 最近记录: |