Linq中变量的属性使用

oknwwptz  于 2022-12-06  发布在  其他
关注(0)|答案(1)|浏览(183)

我希望从基于各种搜索参数和操作符的List返回结果。但是我希望避免为所有的组合使用一个长的if then else列表,所以能够在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();
}

你能给予我一些实现这一目标的想法吗?谢谢。

tuwxkamq

tuwxkamq1#

为什么你要用字符串作为搜索参数?字符串是给人用的,不是给电脑用的!

分离您的关注点

这意味着:将输入与处理和输出分开。
使用字符串的唯一理由是,如果你有一些外部源,而你不知道它的来源,比如一个人(好吧,有时是一台外部计算机)。如果你有一个UI,其中一个操作员键入一个字符串来表示他想要使用的参数,你应该立即转换成他的输入所代表的:这个输入并不意味着要用作字符串,而是用作Where语句中的参数 predicate 。
在您的情况下,此参数的格式为Func<SalesOrder, bool>,如果是数据库查询,则为:Expression<Func<SalesOrder, bool>>
这样的转换函数的优点是,你可以在早期检查输入字符串的有效性,在操作员输入完数据后立即检查。另一个例子当然是可重用性:如果predicate参数来自其他来源而不是输入字符串的人,例如,选择组合框中的项的人,或者程序的其他部分,则可以重用处理部分。
除了可重用性,分离关注点的其他好处是代码更容易理解,更容易修改,更容易测试。需求的变化会导致代码的较小变化。
例如:现在您的要求是操作员从组合框中选择要在搜索字符串中使用的参数,组合框包含IdOrderDateCustomerName等项。
但是假设你有一个新的要求

  • 更改您的表单应用程序,使操作员可以选择CustomerName Kennedy或OrderDate〉January 2019
  • 制作一个命令行版本的程序,用户可以输入-CustomerName Kennedy,也可以输入CUSTOMERNAME kennedy-c Kennedy,甚至-cuSTOMErname KENnedY

如果有一个过程将输入转换为Func<SalesOrder, bool>,则不必更改处理和输出。
所以我们分开吧!

返回您的问题

    • 输入**:两个字符串:一个paramName和一个paramValueparamName是要使用的参数的名称,paramValue是要比较的值的字符串表示形式。

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

    • 输出:**表示参数Predicate得Func<SalesOrder, bool>,该参数可在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等命令行输入是多么容易吧?
分机功能您在哪里等待:将x1M23N1x和x1M24N1x的序列作为输入并将所选择的x1M25N1x作为输出的x1M22N1x现在将是一行程序。
我决定将它创建为一个扩展函数。这样您就可以像使用标准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表达式不会更改。

相关问题