在Linq中将变量用于属性

时间:2019-03-08 10:15:10

标签: c# linq

我想根据各种搜索参数和运算符从List返回结果。 但是我想避免所有组合的较长列表,因此能够在Linq搜索中使用变量会很好...类似这样的东西(显然不起作用):

//NOW searchParameter contains one of the following: "orderNumber", "customerOrderNumber", "customerCode", "customerName", "shippingCity", "shippingCountry"
//AND searchOperator contains one of the following: "equalto", "notequalto", "before", "after", "lessthan", "morethan"
//AND searchCriteria is either a string or a DateTime
if (searchOperator == "equalto")    
{
    List<SalesOrder> results = Program.salesOrderCollection.getSalesOrders().Where(o => o[searchParameter].Equals(searchCriteria)).ToList();
}

您能给我一些实现此目标的想法吗?谢谢。

1 个答案:

答案 0 :(得分:0)

为什么要使用字符串作为搜索参数?字符串是人类的,而不是计算机的!

分开您的担忧

这意味着:将您的输入与处理以及输出分开。

使用字符串的唯一原因是,如果您有一些不知道来源的外部来源,例如人类(可以,有时是外部计算机)。如果您有一个UI,操作员在其中输入代表他要使用的参数的字符串,则应立即转换为他的输入所代表的含义:此输入不是要用作字符串,而应作为a中的参数谓词Where声明。

根据您的情况,此参数的格式为Func<SalesOrder, bool>, or if it is a database query: Expression>`

这种转换功能的优点在于,您可以在操作员输入完数据后立即在早期检查输入字符串的有效性。当然,另一个示例是可重用性:如果谓词参数来自其他来源而不是人为输入字符串,例如,人在组合框中选择项目或程序的另一部分,则可以重用处理部分。

除了重用性之外,分离您的关注点的其他优点是该代码更易于理解,更易于更改和易于测试。需求变更会导致代码变更量减少。

例如:您现在的要求是操作员从一个包含IdOrderDateCustomerName之类的组合框中选择要在搜索字符串中使用的参数。

但是假设您有新要求  -更改您的表单应用程序,以便操作员可以选择CustomerName Kennedy或OrderDate> 2019年1月  -制作程序的命令行版本,用户可以在其中键入-CustomerName Kennedy,也可以键入CUSTOMERNAME kennedy-c Kennedy甚至-cuSTOMErname KENnedY

如果您有一个将输入转换为`Func的过程,则处理和输出无需更改。

所以让我们分开吧!

回到您的问题

输入:两个字符串:paramNameparamValueparamName是要使用的参数的名称,而paramValue是要比较的值的字符串表示形式。

应该可以将paramValue从字符串转换为正确的参数类型。因此,如果参数是DateTime,则paramValue应该是可以转换为DateTime的字符串。

输出:代表Func的参数谓词,可在Where语句中使用以选择SalesOrders的子集。

class InputConverter
{
  static Func<SalesOrder, bool> ToWherePredicate(string paramName, string paramValue)
  {
    switch (paramName)
    {
      case "Id:"
        int value = Int32.Parse(paramValue);
        return salesOrder => salesOrder.Id == value;
        break;
      case "OrderDate":
        DateTime value = DateTime.Parse(paramValue);
        return salesOrder => salesOrder.OrderDate == value;
        break;
      case "CustomerId":
        int value = Int32.Parse(paramValue);
        return salesOrder => salesOrder.CustomerId == value;
        break;
      case ...

      default:
        // Other string values are not supported
        throw new NotSupportedException(...);
    }
  }
}

看看扩展此功能以接受-Date-Id之类的命令行输入有多么容易?

您正在等待的扩展功能:现在将Where作为输入,该SalesOrdersKeyValuePair<string, string>的序列作为输入,并输出选定的SalesOrders单线。

我决定将其创建为扩展功能。这样,您就可以像使用标准LINQ函数一样使用它,请参见Extension Methods Demystified

static IEnumerable<SalesOrder> Where(this IEnumerable<SalesOrder> salesOrders,
       string paramName, string paramValue)
{
  Func<SalesOrder, bool> predicate = InputConverter.ToWherePredicate(paramName, paramValue)
  return salesOrders.Where(predicate);
}

用法,从ComboBox和TextBox读取输入(按婴儿步骤):

string paramName = (string)comboBoxParamName.SelectedValue;
string paramValue = (string)textBoxParamValue.SelectedValue

var requestedSalesOrders = Program.salesOrderCollection.getSalesOrders()
    .Where(paramName, paramValue)
    .GroupBy(salesOrder => salesOrder.OrderDate)
    .OrderBy(group => group.Key)
    ...

请注意,因为我将其创建为IEnumerable的扩展函数,所以它的行为与其他LINQ函数完全一样。

最后一点:如果函数GetSalesOrders返回IQueryable<SalesOrder>而不是IEnumerable<SalesOrder>,则应使用Expression<Func<SalesOrder, bool>>而不是Func<SalesOrder, bool>ToPredicate中的lambda表达式不会改变。