按枚举描述排序 [英] Order by enum description

查看:344
本文介绍了按枚举描述排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一个使用EF代码的ASP.NET MVC projet,而我正面临一种情况,我需要通过枚举描述进行排序:

  public partial class Item 
{
public enum MyEnumE
{
[描述(enum1的描述)]
Enum1,
[描述(enum2的描述)]
Enum2,
...
}

public MyEnumE MyEnum {get;组;
}

这里是搜索 SortAndPaginate 函数:

  public async任务< IPagedList< Item& ;>搜索(ItemCriteria条件,SortableTypeE sortName,SortOrder.TypeE sortOrder,int pageNb)
{
var itemFilter = GenerateFilter(criteria);
var items = entities.Items.Where(itemFilter);

return await SortAndPaginate(items,sortName,sortOrder,pageNb);
}

私有异步任务< 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);
}

返回PagedListHelper.Empty< Item>();
}

问题是项目 table可能相当巨大。

我想到在$ code> entities.Items.Where(itemFilter)之后调用 ToListAsync ,但是这样可以恢复所有已过滤的项目,但我只需要一个页面。听起来不是一个好主意。



但是,如果我不这样做,那么 EF 不会知道我只能想到两个解决方案:

- 将我的数据库列更改为字符串(枚举描述)而不是枚举本身(但听起来像是一个黑客)

- 或按字母顺序在枚举中直接订购 MyEnumE 组件声明(看起来很脏,不可维护)



我很困扰,因为我关心的性能,如果我调用 ToListAsync 过滤后,所有其他解决方案看起来很脏,我绝对需要从搜索 IPagedList c>方法。



有没有人会想到如何处理这个问题?



非常感谢。



更新



code> 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)
返回属性[0] .Description;
else
return e.ToString();
}

解决方案 / p>

我终于会找到Ivan Stoev的建议,因为我的项目主要是基于 Linq (使用 Linq 而不是存储过程等),所以这个解决方案似乎比创建参考表更适合我的具体情况。



然而 Niyoko Yuliawan Michael Freidgeim 对我来说也是非常好的答案,任何人阅读这篇文章,并有更多的数据库方法应该适用于他们的解决方案;)



非常感谢大家。

解决方案

我会用动态表达式去。它更灵活,可以很容易地改变w / o影响数据库表和查询​​。



然而,不是按照数据库中的描述字符串进行排序,我将创建有序映射在内存中,将$ code> int order值与每个枚举值相关联:

  public static class EnumHelper 
{
public static Expression< Func< TSource,int>>描述方法< TSource,TEnum>(此表达式&FunC< TSource,TEnum>>源)
其中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));

返回Expression.Lambda }

public static string GetDescription< TEnum>(此TEnum值)
其中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>()?.描述??名称;
}
}

使用方法如下:

  case SortableTypeE.Type:
var order = EnumHelper.DescriptionOrder((Item x)=> x.MyEnum);
result = sortOrder == SortOrder.TypeE.ASC
? item.OrderBy(order)
:items.OrderByDescending(order);
result = result.ThenBy(i => i.SomeOtherProperty);
break;

这将生成如下表达式:

  x => x.MyEnum ==枚举[0]? 0:
x.MyEnum ==枚举[1]? 1:
...
x.MyEnum ==枚举[N-2]? N - 2:
N - 1;

其中0,1,.. N-2是按照描述排序的值列表中的相应索引。


I am working on an ASP.NET MVC projet using EF code first, and I am facing a situation where I need to order by an enum description:

public partial class Item
{
    public enum MyEnumE
    {
        [Description("description of enum1")]
        Enum1,
        [Description("description of enum2")]
        Enum2,
        ...
    }

    public MyEnumE MyEnum { get; set; }
}

Here is the Search and SortAndPaginate functions:

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>();
    }

The problem is that the Item table can be quite huge.
I thought about calling ToListAsync right after entities.Items.Where(itemFilter) but this will get back all filtered items although I only need one page. Does not sound like a good idea.

But if I don't do that EF won't know about GetDescription() mathod and I can only think about two solutions:
- Change my database column to a string (the enum description) instead of the enum itself (but sounds like a hack to me)
- Or alphabetically order MyEnumE components directly in the enum declaration (seems dirty and quite unmaintainable too)

I'm quite stuck since I'm concerned about performances if I call ToListAsync right after filtering, all other solutions seem dirty, and I absolutely need a IPagedList returned from the Search method.

Would anyone have an idea about how to deal with this issue ?

Thanks a lot.

UPDATE

Here is the GetDescription method (can change it if necessary):

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();
}

SOLUTIONS

I'll finally go for Ivan Stoev's suggestion because my project is mainly based on Linq (using Linq instead of stored procedures etc.), so this solution seems more suitable to my particular case than creating reference tables.

However Niyoko Yuliawan's and Michael Freidgeim's are also really good answers to me, anyone reading this post and having a more database approach should go for their solutions ;)

Thanks a lot to all of you.

解决方案

I would go with dynamic expression. It's more flexible and can easily be changed w/o affecting the database tables and queries.

However, instead of sorting by description strings in the database, I would create ordered map in memory, associating int "order" value with each enum value like this:

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;
    }
}

The usage would be like this:

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;

which would generate expression like this:

x => x.MyEnum == Enum[0] ? 0 :
     x.MyEnum == Enum[1] ? 1 :
     ...
     x.MyEnum == Enum[N-2] ? N - 2 :
     N - 1;

where 0,1,..N-2 is the corresponding index in the value list sorted by description.

这篇关于按枚举描述排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆