我想根据各种搜索参数和运算符从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();
}
您能给我一些实现此目标的想法吗?谢谢。
答案 0 :(得分:0)
为什么要使用字符串作为搜索参数?字符串是人类的,而不是计算机的!
这意味着:将您的输入与处理以及输出分开。
使用字符串的唯一原因是,如果您有一些不知道来源的外部来源,例如人类(可以,有时是外部计算机)。如果您有一个UI,操作员在其中输入代表他要使用的参数的字符串,则应立即转换为他的输入所代表的含义:此输入不是要用作字符串,而应作为a中的参数谓词Where
声明。
根据您的情况,此参数的格式为Func<SalesOrder, bool>, or if it is a database query:
Expression>`
这种转换功能的优点在于,您可以在操作员输入完数据后立即在早期检查输入字符串的有效性。当然,另一个示例是可重用性:如果谓词参数来自其他来源而不是人为输入字符串,例如,人在组合框中选择项目或程序的另一部分,则可以重用处理部分。
除了重用性之外,分离您的关注点的其他优点是该代码更易于理解,更易于更改和易于测试。需求变更会导致代码变更量减少。
例如:您现在的要求是操作员从一个包含Id
,OrderDate
,CustomerName
之类的组合框中选择要在搜索字符串中使用的参数。
但是假设您有新要求
-更改您的表单应用程序,以便操作员可以选择CustomerName Kennedy或OrderDate> 2019年1月
-制作程序的命令行版本,用户可以在其中键入-CustomerName Kennedy
,也可以键入CUSTOMERNAME kennedy
,-c Kennedy
甚至-cuSTOMErname KENnedY
如果您有一个将输入转换为`Func的过程,则处理和输出无需更改。
所以让我们分开吧!
输入:两个字符串:paramName
和paramValue
,paramName
是要使用的参数的名称,而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
作为输入,该SalesOrders
和KeyValuePair<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表达式不会改变。