修改Linq以消除重复性

时间:2015-08-31 15:43:44

标签: c# .net linq

private readonly IRepository<Order> _orderRepo; // initialized in the constructor



IRepository<Order> GetOrder(string orderstate)
{
    if(orderstate == null)
    {
        return null;
    }

    IQueryable<Order> query = null;

    if(orderstate == "OrderStateChanged")
    {
        query = (from c in _orderRepo.Table
                where c.OrderStateChanged != 0
                select c
            );
    }

    else if (orderstate == "PaymentStateChanged")
    {
        query = (from c in _orderRepo.Table
                where c.PaymentStateChanged != 0
                select c
            );
    }
    /*More else if statement*/
}

我使用LINQ从repo中提取数据,我还有更多if if语句。我想删除我的代码的重复行为。 我有一个线索“表达树”但我无法理解我如何在我的代码中使用它或建议我任何其他方式删除 重复性。

3 个答案:

答案 0 :(得分:2)

如果您确实想要阻止将orderState手动映射到成员(使用if/elseswitchIDictionary,...),你唯一的选择确实是表达树:

var orderType = typeof(Order);
var param = Expression.Parameter(orderType);
var member = orderType.GetMember(orderState).FirstOrDefault();
if (member == null) 
{
    /* requested member of "Order" does not exist */
}
var filter = Expression.Lambda<Func<Order, bool>>(  // "param => param.member != 0"
    Expression.NotEqual(                              // "param.member != 0"
        Expression.MakeMemberAccess(param, member),     // "param.member"
        Expression.Constant(0)),                        // "0"
    param);                                           // "param =>"
query = _orderRepo.Table.Where(filter);

替代方案(更简单,但如果该成员不存在则抛出ArgumentException

var orderType = typeof(Order);
var param = Expression.Parameter(orderType);
var member = Expression.PropertyOrField(param, orderState); // may throw ArgumentException!
var filter = Expression.Lambda<Func<Order, bool>>(
    Expression.NotEqual(member, Expression.Constant(0)),
    param);
query = _orderRepo.Table.Where(filter);

这样,即使Order的对象布局发生变化,您也非常通用。一个缺点当然是导致orderState的不存在成员的Order值无效的风险,但我确信您已经拥有某种机制了。

进一步阅读

MSDN - Expression Trees

MSDN - How to: Use Expression Trees to Build Dynamic Queries

CodeProject - Expression Tree Basics

答案 1 :(得分:1)

你不会得到更好的,但扩展方法语法是(恕我直言)这种代码更简洁

IEnumerable<Order> GetOrder(string orderstate)
{

    if(orderstate == null)
    {
        return null;
    }

    IQueryable<Order> query = _orderRep.Table;

    if(orderstate == "OrderStateChanged")
    {
        query = query.Where(c => c.OrderStateChanged != 0);
    }
    else if (orderstate == "PaymentStateChanged")
    {
        query = query.Where(c => c.PaymentStateChanged != 0);
    }
    /*More else if statement*/
}

如果您需要在多个位置进行此过滤,则可以使用表达式谓词维护某种字典:

static IDictionary<string, Expression<Func<Order,bool>> Predicates = new Dictionary<string, Expression<Func<Order,bool>> 
{
    {"OrderStateChanged", o => o.OrderStateChanged != 0},
    {"OrderPaymentChanged", o => o.PaymentStateChanged != 0},
};

然后您的方法可能会变成:

IEnumerable<Order> GetOrder(string orderstate)
{
    if (orderstate == null || !Predicates.ContainsKey(orderstate))
        return null; // or throw exception

    var predicate = Predicates[orderstate];

    return _orderRep.Table.Where(predicate);
}

答案 2 :(得分:0)

LINQ本质上是可组合的。这意味着你可以这样做:

var query = _orderRepoTable.AsQueryable();

switch (orderState)
{
  case "OrderStateChanged": query = query.Where(c => c.OrderStateChanged != 0); break;
  ...
}

// Now query has the filters you want.

我仍然坚持使用显式过滤器,而不是使用表达式树直接基于orderState构建它们 - 这只是在寻找麻烦。使用orderState的枚举也可能是一个好主意。别担心,这不是真正的代码重复 - 基本上它只是一种语言的定义。