我正在开发一个项目,我的任务是添加一个高级搜索和过滤选项,允许用户通过指定任意数量的条件来查询Windows事件列表中的所需结果.
我们的想法是每个Windows事件日志中有一些特性,如LogName,Source,CreatedDate,Message,Number,等(的一部分FieldItem枚举).总共有4种 possbile数据类型:String,DateTime,Integral (Int/Long),和EventEntryType.这四种数据类型中的每一种都有自己的选择器操作数集合(SelectorOperator枚举的一部分).这是一张图片,可以让您更好地了解整体结构如何:

我最初实现这个想法是这样的:
public static class SearchProvider
{
public static List<EventLogItem> SearchInLogs(List<EventLogItem> currentLogs, SearchQuery query)
{
switch (query.JoinType)
{
case ConditionJoinType.All:
return SearchAll(currentLogs, query);
case ConditionJoinType.Any:
return SearchAny(currentLogs, query);
default:
return null;
}
}
private static List<EventLogItem> SearchAll(List<EventLogItem> currentLogs, SearchQuery query)
{
foreach (SearchCondition condition in query.Conditions)
{
switch (condition.FieldName)
{
case FieldItem.Category:
switch (condition.SelectorOperator)
{
case SelectorOperator.Contains:
currentLogs = currentLogs.Where(item => item.Category.ToLower().Contains(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.EndsWith:
currentLogs = currentLogs.Where(item => item.Category.ToLower().EndsWith(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.Is:
currentLogs = currentLogs.Where(item => string.Equals(item.Category, condition.FieldValue as string, StringComparison.OrdinalIgnoreCase)).ToList();
break;
case SelectorOperator.StartsWith:
currentLogs = currentLogs.Where(item => item.Category.ToLower().StartsWith(condition.FieldValue as string)).ToList();
break;
}
break;
case FieldItem.InstanceID:
switch (condition.SelectorOperator)
{
case SelectorOperator.Equals:
currentLogs = currentLogs.Where(item => item.InstanceID == long.Parse(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.IsGreaterThan:
currentLogs = currentLogs.Where(item => item.InstanceID > long.Parse(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.IsLessThan:
currentLogs = currentLogs.Where(item => item.InstanceID < long.Parse(condition.FieldValue as string)).ToList();
break;
}
break;
case FieldItem.LogName:
switch (condition.SelectorOperator)
{
case SelectorOperator.Contains:
currentLogs = currentLogs.Where(item => item.LogName.ToLower().Contains(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.EndsWith:
currentLogs = currentLogs.Where(item => item.LogName.ToLower().EndsWith(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.Is:
currentLogs = currentLogs.Where(item => string.Equals(item.LogName, condition.FieldValue as string, StringComparison.OrdinalIgnoreCase)).ToList();
break;
case SelectorOperator.StartsWith:
currentLogs = currentLogs.Where(item => item.LogName.ToLower().StartsWith(condition.FieldValue as string)).ToList();
break;
}
break;
case FieldItem.Message:
switch (condition.SelectorOperator)
{
case SelectorOperator.Contains:
currentLogs = currentLogs.Where(item => item.Message.ToLower().Contains(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.EndsWith:
currentLogs = currentLogs.Where(item => item.Message.ToLower().EndsWith(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.Is:
currentLogs = currentLogs.Where(item => string.Equals(item.Message, condition.FieldValue as string, StringComparison.OrdinalIgnoreCase)).ToList();
break;
case SelectorOperator.StartsWith:
currentLogs = currentLogs.Where(item => item.Message.ToLower().StartsWith(condition.FieldValue as string)).ToList();
break;
}
break;
case FieldItem.Number:
switch (condition.SelectorOperator)
{
case SelectorOperator.Equals:
currentLogs = currentLogs.Where(item => item.Number == int.Parse(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.IsGreaterThan:
currentLogs = currentLogs.Where(item => item.Number > int.Parse(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.IsLessThan:
currentLogs = currentLogs.Where(item => item.Number < int.Parse(condition.FieldValue as string)).ToList();
break;
}
break;
case FieldItem.Source:
switch (condition.SelectorOperator)
{
case SelectorOperator.Contains:
currentLogs = currentLogs.Where(item => item.Source.ToLower().Contains(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.EndsWith:
currentLogs = currentLogs.Where(item => item.Source.ToLower().EndsWith(condition.FieldValue as string)).ToList();
break;
case SelectorOperator.Is:
currentLogs = currentLogs.Where(item => string.Equals(item.Source, condition.FieldValue as string, StringComparison.OrdinalIgnoreCase)).ToList();
break;
case SelectorOperator.StartsWith:
currentLogs = currentLogs.Where(item => item.Source.ToLower().StartsWith(condition.FieldValue as string)).ToList();
break;
}
break;
case FieldItem.Type:
switch (condition.SelectorOperator)
{
case SelectorOperator.Is:
currentLogs = currentLogs.Where(item => item.Type == (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), condition.FieldValue as string)).ToList();
break;
case SelectorOperator.IsNot:
currentLogs = currentLogs.Where(item => item.Type != (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), condition.FieldValue as string)).ToList();
break;
}
break;
}
}
return currentLogs;
}
Run Code Online (Sandbox Code Playgroud)
示例查询可能如下所示:
条件选择器:
All of the conditions are met
Run Code Online (Sandbox Code Playgroud)
条件:
LogName Is "Application"
Message Contains "error"
Type IsNot "Information"
InstanceID IsLessThan 1934
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,SearchAll()由于嵌套switch语句,该方法很长且不易维护.但是,代码有效,我觉得这不是实现此设计的最优雅方式.有没有更好的方法来解决这个问题?也许通过找出降低switch层次结构复杂性的方法或使代码更通用?任何帮助/建议表示赞赏.
我认为你确实需要两个 switch 语句,但它们不需要嵌套。您可以分离出对任何类型的对象通用的操作,然后传入您在运行时搜索的对象。
public static class SearchProvider
{
static Func<object, bool> GetSearchMethod(SelectorOperator selectorOperator, string conditionFieldValue)
{
switch (selectorOperator)
{
//strings
case SelectorOperator.Contains:
return new Func<object, bool>(s => s.ToString().ToLower().Contains(conditionFieldValue));
case SelectorOperator.StartsWith:
return new Func<object, bool>(s => s.ToString().ToLower().StartsWith(conditionFieldValue));
case SelectorOperator.EndsWith:
return new Func<object, bool>(s => s.ToString().ToLower().EndsWith(conditionFieldValue));
case SelectorOperator.Is:
return new Func<object, bool>(s => string.Equals(s.ToString(), conditionFieldValue, StringComparison.OrdinalIgnoreCase));
//numbers
case SelectorOperator.Equals:
return new Func<object, bool>(n => (long)n == long.Parse(conditionFieldValue));
case SelectorOperator.IsGreaterThan:
return new Func<object, bool>(n => (long)n > long.Parse(conditionFieldValue));
case SelectorOperator.IsLessThan:
return new Func<object, bool>(n => (long)n < long.Parse(conditionFieldValue));
//type
case SelectorOperator.TypeIs:
return new Func<object, bool>(t => (EventLogEntryType)t == (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), conditionFieldValue));
case SelectorOperator.TypeIsNot:
return new Func<object, bool>(t => (EventLogEntryType)t != (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), conditionFieldValue));
default:
throw new Exception("Unknown selector operator");
}
}
private static List<EventLogItem> SearchAll(List<EventLogItem> currentLogs, SearchQuery query)
{
foreach (SearchCondition condition in query.Conditions)
{
var search = GetSearchMethod(condition.SelectorOperator, condition.FieldValue as string);
switch (condition.FieldName)
{
case FieldItem.Category:
currentLogs = currentLogs.Where(item => search(item.Category)).ToList();
break;
case FieldItem.InstanceID:
currentLogs = currentLogs.Where(item => search(item.InstanceID)).ToList();
break;
case FieldItem.LogName:
currentLogs = currentLogs.Where(item => search(item.LogName)).ToList();
break;
case FieldItem.Message:
currentLogs = currentLogs.Where(item => search(item.Message)).ToList();
break;
case FieldItem.Number:
currentLogs = currentLogs.Where(item => search(item.Number)).ToList();
break;
case FieldItem.Source:
currentLogs = currentLogs.Where(item => search(item.Source)).ToList();
break;
case FieldItem.Type:
currentLogs = currentLogs.Where(item => search(item.Type)).ToList();
break;
}
}
return currentLogs;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我这么晚发布是因为 SO 服务器崩溃了,然后我就去睡觉了:(
因此它类似于 @dasblinkenlight 的答案。