Прохождение динамического выражения для упорядочивания по коду сначала EF-репозиторий

мы написали Generic функцию, чтобы сначала получить записи из EF-кода в шаблоне репозитория. Кажется, что это нормально, но, передавая Integer в динамический порядок, он говорит Cannot cast System.Int32 to System.Object

выражение выглядит следующим образом:

Expression<Func<HeadOffice, object>> orderByFunc = o =>  o.Id;

if (options.sort == "Id")
{
         // this is an Integer
    orderByFunc = o => o.Id;
}
if (options.sort =="Name")
{
   // string
    orderByFunc = o => o.Name;
}
if (options.sort == "Code")
{
    orderByFunc = o => o.Code;
}

общий метод выглядит следующим образом:

public virtual IEnumerable<TEntity> GetSorted<TSortedBy>(
    Expression<Func<TEntity, object>> order,
    int skip, int take, 
    params Expression<Func<TEntity, object>>[] includes)
{
    IQueryable<TEntity> query = dbSet;

    foreach (var include in includes)
    {
        query = dbSet.Include(include);
    }

    IEnumerable<TEntity> data = query.OrderBy(order).Skip(skip).Take(take).ToList();

    return data;
}

если мы преобразуем Expression<Func<TEntity, object>> в Expression<Func<TEntity, int>>, тогда он, кажется, отлично работает с целым числом, но, следовательно, не с строками

любая помощь оценена.

Ответ 1

Возможно, если вы измените тип этого параметра для этого Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy, это может сделать вашу жизнь проще:

public virtual IEnumerable<TEntity> GetSorted(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy,...)
{
    IQueryable<TEntity> query = dbSet;
    //...
    if (orderBy != null)
    {
        query = orderBy(query);
    }
    //...
}

Таким образом вы можете передать Func следующим образом:

Func<IQueryable<HeadOffice>, IOrderedQueryable<HeadOffice>> orderBy=null;
if (options.sort == "Id")
{
   orderBy= query=>query.OrderBy(o => o.Id);
}
//...

Update

Еще одна вещь, которую я замечаю сейчас, заключается в том, что вы не используете общий параметр TSortedBy, поэтому вы также можете это сделать:

public virtual IEnumerable<TEntity> GetSorted<TSortedBy>(Expression<Func<TEntity, TSortedBy>> order,
                                                         int skip, int take, 
                                                         params Expression<Func<TEntity, object>>[] includes)
{
}

Но в любом случае я думаю, что лучше использовать первый вариант и удалить этот общий параметр.

Ответ 2

Одно из решений состоит в том, чтобы иметь два перегруженных метода, один принимает

Expression<Func<TEntity, int>>

и принимает

Expression<Func<TEntity, string>>

Чтобы свести к минимуму дублирование кода, извлеките общий код (например, оператор инициализации запроса и цикл for) в общий метод и просто позвольте двум методам вызвать этот общий метод и затем вызвать OrderBy для результата.

Ответ 3

Создайте класс Сортировщик. Нам также нужен базовый класс, нейтральный по свойствам:

public class SorterBase<TEntity>
{                                                               
    public abstract IEnumerable<TEntity> GetSorted( // Note, no order argument here
        int skip, int take, 
        params Expression<Func<TEntity, object>>[] includes);
}

public class Sorter<TEntity, TSortProp> : SorterBase<TEntity>
{                                                               
    private Expression<Func<TEntity, TSortProp>> _order;

    public Sorter(Expression<Func<TEntity, TSortProp>> order)
    {
        _order = order;
    }

    public override IEnumerable<TEntity> GetSorted(...)
    {
       // Use _order here ...
    }
}

Теперь измените решение сортировки на:

SorterBase<HeadOffice> sorter;
if (options.sort == "Id") {
    sorter = new Sorter<HeadOffice, int>(o => o.Id);
} else if (options.sort == "Name") {
    sorter = new Sorter<HeadOffice, string>(o => o.Name);
}
...

var result = sorter.GetSorted(skip, take, includes);

Ответ 4

Если ни один из ответов не работает для вас, и вы должны иметь выражение порядка:

Expression<Func<TEntity,object>>

затем попробуйте следующее решение:

public class ExpressionHelper : ExpressionVisitor
{
    private MemberExpression m_MemberExpression;

    public MemberExpression GetPropertyAccessExpression(Expression expression)
    {
        m_MemberExpression = null;

        Visit(expression);


        return m_MemberExpression;
    }

    protected override Expression VisitMember(MemberExpression node)
    {

        var property = node.Member as PropertyInfo;

        if (property != null)
        {
            m_MemberExpression = node;
        }

        return base.VisitMember(node);

    }
}

public class DataClass<TEntity>
{
    private readonly IQueryable<TEntity> m_Queryable;

    public DataClass(IQueryable<TEntity> queryable)
    {
        m_Queryable = queryable;
    }


    public virtual IEnumerable<TEntity> GetSorted(
        Expression<Func<TEntity, object>> order,
        int skip, int take,
        params Expression<Func<TEntity, object>>[] includes)
    {

        var property_access_expression = new ExpressionHelper().GetPropertyAccessExpression(order);

        if(property_access_expression == null)
            throw new Exception("Expression is not a property access expression");

        var property_info = (PropertyInfo) property_access_expression.Member;

        var covert_method = this.GetType().GetMethod("Convert").MakeGenericMethod(property_info.PropertyType);

        var new_expression = covert_method.Invoke(this, new object[] {property_access_expression, order.Parameters });


        var get_sorted_method = this.GetType()
            .GetMethod("GetSortedSpecific")
            .MakeGenericMethod(property_info.PropertyType);


        return (IEnumerable<TEntity>)get_sorted_method.Invoke(this, new object[] { new_expression, skip, take, includes });
    }

    public virtual IEnumerable<TEntity> GetSortedSpecific<TSortedBy>(
        Expression<Func<TEntity, TSortedBy>> order,
        int skip, int take,
        params Expression<Func<TEntity, object>>[] includes)
    {

        IQueryable<TEntity> query = m_Queryable;

        //Here do your include logic and any other logic

        IEnumerable<TEntity> data = query.OrderBy(order).Skip(skip).Take(take).ToList();

        return data;
    }

    public Expression<Func<TEntity, TNewKey>> Convert<TNewKey>(
        MemberExpression expression, ReadOnlyCollection<ParameterExpression> parameter_expressions )
    {
        return Expression.Lambda<Func<TEntity, TNewKey>>(expression, parameter_expressions);
    }
}

Вот как я протестировал это:

    void Test()
    {
        Expression<Func<Entity, object>> exp = (x) => x.Text;

        List<Entity> entities = new List<Entity>();

        entities.Add(new Entity()
        {
            Id = 1,
            Text = "yacoub"
        });


        entities.Add(new Entity()
        {
            Id = 2,
            Text = "adam"
        });


        DataClass<Entity> data = new DataClass<Entity>(entities.AsQueryable());

        var result = data.GetSorted(exp, 0, 5, null);

    }

Я тестировал это как с целыми, так и с строковыми свойствами.

Обратите внимание, что это работает только для простых выражений доступа к ресурсам.

Вы можете улучшить метод GetSorted, чтобы сделать эту работу более сложными.