Ich arbeite an einem Projekt und meine Aufgabe besteht darin, eine erweiterte Such-und Filteroption hinzuzufügen, die die Benutzer gewünschte Ergebnisse aus einer Liste von Windows-Ereignissen durch die Angabe so viele Bedingungen abfragen können wie sie wollen.Verschachtelte Switch-Anweisungen: Architectural Design Problem
Die Idee ist, jedes Windows-Ereignisprotokoll mehrere Eigenschaften wie LogName
, Source
, CreatedDate
, Message
, Number
usw. (Teil des FieldItem Enum) hat. Insgesamt gibt es vier mögliche Datentypen: String
, DateTime
, Integral (Int/Long)
und EventEntryType
. Jeder dieser vier Datentypen hat seine eigene Sammlung von Selektoroperanden (Teil der SelectorOperator enum). Hier ist ein Bild, das Ihnen eine bessere Vorstellung davon zu geben, wie die Gesamtstruktur wie folgt aussieht:
Meine erste Umsetzung dieser Idee ist folgende:
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;
}
Eine Beispielabfrage könnte wie folgt aussehen :
Zustand Selector:
All of the conditions are met
Bedingungen:
LogName Is "Application"
Message Contains "error"
Type IsNot "Information"
InstanceID IsLessThan 1934
Wie Sie sehen können, die SearchAll()
Methode ist ziemlich lang und nicht sehr wartbar aufgrund der verschachtelten switch
Aussagen. Der Code funktioniert, aber ich denke, das ist nicht die eleganteste Art, dieses Design zu implementieren. Gibt es einen besseren Weg, dieses Problem anzugehen? Vielleicht, indem Sie einen Weg finden, die Komplexität der switch
Hierarchie ODER den Code generischer zu machen? Jede Hilfe/Anregung wird geschätzt.
IMO, das sieht aus wie ein Kandidat für mehrere Versand (Besucher Muster). Ich überlasse die Antworten für die Menschen schlauer als ich :) –
Was ist Ihre Datenzugriffsstrategie? Wenn Sie LINQ to SQL oder LINQ to Entities verwenden, kann dies sehr einfach (und auch elegant) werden, wenn Sie die Schnittstelle 'IQueryable' verwenden. –
Yuck
@Yuck Die Ereignisprotokolle werden von 'System.Diagnostics.EventLog.GetEventLogs()' zurückgegeben, und daraus wird eine Liste von 'EventLogItem' (benutzerdefinierter Typ) erstellt und schließlich an ein' ListView'-Steuerelement gebunden. 'IQueryable' ist das, worüber ich eine Ahnung hatte, aber ich war nicht sicher, wie ich es implementieren/verwenden sollte. – PoweredByOrange