c# - Nested Switch Statements: Architectural Design Issue -
i'm working on project , task add advanced search , filtering option allows users query desired results list of windows events by specifying many conditions want.
the idea each windows event log has several properties such logname
, source
, createddate
, message
, number
, etc. (part of fielditem enum). in total, there four possbile data types: string
, datetime
, integral (int/long)
, , evententrytype
. each of these 4 data types has own collection of selector operands (part of selectoroperator enum). here picture give better idea of how overall structure looks like:
my initial implementation of idea this:
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 string)).tolist(); break; case selectoroperator.endswith: currentlogs = currentlogs.where(item => item.category.tolower().endswith(condition.fieldvalue string)).tolist(); break; case selectoroperator.is: currentlogs = currentlogs.where(item => string.equals(item.category, condition.fieldvalue string, stringcomparison.ordinalignorecase)).tolist(); break; case selectoroperator.startswith: currentlogs = currentlogs.where(item => item.category.tolower().startswith(condition.fieldvalue string)).tolist(); break; } break; case fielditem.instanceid: switch (condition.selectoroperator) { case selectoroperator.equals: currentlogs = currentlogs.where(item => item.instanceid == long.parse(condition.fieldvalue string)).tolist(); break; case selectoroperator.isgreaterthan: currentlogs = currentlogs.where(item => item.instanceid > long.parse(condition.fieldvalue string)).tolist(); break; case selectoroperator.islessthan: currentlogs = currentlogs.where(item => item.instanceid < long.parse(condition.fieldvalue string)).tolist(); break; } break; case fielditem.logname: switch (condition.selectoroperator) { case selectoroperator.contains: currentlogs = currentlogs.where(item => item.logname.tolower().contains(condition.fieldvalue string)).tolist(); break; case selectoroperator.endswith: currentlogs = currentlogs.where(item => item.logname.tolower().endswith(condition.fieldvalue string)).tolist(); break; case selectoroperator.is: currentlogs = currentlogs.where(item => string.equals(item.logname, condition.fieldvalue string, stringcomparison.ordinalignorecase)).tolist(); break; case selectoroperator.startswith: currentlogs = currentlogs.where(item => item.logname.tolower().startswith(condition.fieldvalue string)).tolist(); break; } break; case fielditem.message: switch (condition.selectoroperator) { case selectoroperator.contains: currentlogs = currentlogs.where(item => item.message.tolower().contains(condition.fieldvalue string)).tolist(); break; case selectoroperator.endswith: currentlogs = currentlogs.where(item => item.message.tolower().endswith(condition.fieldvalue string)).tolist(); break; case selectoroperator.is: currentlogs = currentlogs.where(item => string.equals(item.message, condition.fieldvalue string, stringcomparison.ordinalignorecase)).tolist(); break; case selectoroperator.startswith: currentlogs = currentlogs.where(item => item.message.tolower().startswith(condition.fieldvalue string)).tolist(); break; } break; case fielditem.number: switch (condition.selectoroperator) { case selectoroperator.equals: currentlogs = currentlogs.where(item => item.number == int.parse(condition.fieldvalue string)).tolist(); break; case selectoroperator.isgreaterthan: currentlogs = currentlogs.where(item => item.number > int.parse(condition.fieldvalue string)).tolist(); break; case selectoroperator.islessthan: currentlogs = currentlogs.where(item => item.number < int.parse(condition.fieldvalue string)).tolist(); break; } break; case fielditem.source: switch (condition.selectoroperator) { case selectoroperator.contains: currentlogs = currentlogs.where(item => item.source.tolower().contains(condition.fieldvalue string)).tolist(); break; case selectoroperator.endswith: currentlogs = currentlogs.where(item => item.source.tolower().endswith(condition.fieldvalue string)).tolist(); break; case selectoroperator.is: currentlogs = currentlogs.where(item => string.equals(item.source, condition.fieldvalue string, stringcomparison.ordinalignorecase)).tolist(); break; case selectoroperator.startswith: currentlogs = currentlogs.where(item => item.source.tolower().startswith(condition.fieldvalue 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 string)).tolist(); break; case selectoroperator.isnot: currentlogs = currentlogs.where(item => item.type != (eventlogentrytype)enum.parse(typeof(eventlogentrytype), condition.fieldvalue string)).tolist(); break; } break; } } return currentlogs; }
a sample query might this:
condition selector:
all of conditions met
conditions:
logname "application" message contains "error" type isnot "information" instanceid islessthan 1934
as can see, searchall()
method quite long , not maintainable due nested switch
statements. code works, however, feel not elegant way implement design. there better way approach problem? maybe figuring out way reduce complexity of switch
hierarchy or making code more generic? help/suggestion appreciated.
i think need 2 switch statements, don't need nested. can separate out operations work generically on kind of object, , pass in object searching on @ runtime.
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 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; } }
note posted late because server crashed, went bed :(
hence it's similar @dasblinkenlight's answer.
Comments
Post a Comment