通过代码中的第一个EF存储库传递动态表达式

时间:2015-09-01 19:35:38

标签: c# linq entity-framework

我们编写了一个Generic函数,首先从存储库模式中获取EF代码中的记录。休息似乎没问题但是当将整数传递给动态订单时,它会显示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>>,那么它似乎可以正常使用整数,但不会使用字符串

任何帮助表示感谢。

4 个答案:

答案 0 :(得分:6)

也许如果您更改此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);
}
//...

更新

我现在注意到的另一件事是您没有使用TSortedBy泛型参数,因此,您也可以这样做:

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

但无论如何,我认为最好使用第一个选项并删除该通用参数。

答案 1 :(得分:4)

创建一个Sorter类。我们还需要一个属性类型中立的基类:

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

答案 2 :(得分:2)

一种解决方案是使用两个重载方法,一个采用

Expression<Func<TEntity, string>>

并且需要

Object reference not set to an instance of an object.

要最小化代码重复,请将公共代码(例如查询初始化语句和for循环)提取到共享方法,然后让这两个方法调用此共享方法,然后在结果上调用OrderBy。

答案 3 :(得分:2)

If none of the answers work for you and you must have the order expression as:

Expression<Func<TEntity,object>>

then try the following solution:

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

Here is how I tested this:

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

    }

I tested this with both integer and string properties.

Please note that this only works for simple property access expressions.

You can enhance the GetSorted method to make this work for more complex cases.