Currently i am using a combination of switch operations to generate a linq query, and i am thinking the code is a little bloated.
Is there any way to optimize this code, maybe someway to dynamically build it?
public string[] GetPeopleAutoComplete(string filter, int maxResults, string searchType, string searchOption)
{
var query = from people in _context.People select people;
switch (searchOption)
{
case "StartsWith":
switch (searchType)
{
case "IdentityCode":
query = query.Where(o => o.IdentityCode.StartsWith(filter));
return query.Select(o => o.IdentityCode).Take(maxResults).ToArray();
case "Firstname":
query = query.Where(o => o.Firstname.StartsWith(filter));
return query.Select(o => o.Firstname).Distinct().Take(maxResults).ToArray();
case "Surname":
query = query.Where(o => o.Surname.StartsWith(filter));
return query.Select(o => o.Surname).Distinct().Take(maxResults).ToArray();
}
break;
case "EndsWith":
switch (searchType)
{
case "IdentityCode":
query = query.Where(o => o.IdentityCode.EndsWith(filter));
return query.Select(o => o.IdentityCode).Take(maxResults).ToArray();
case "Firstname":
query = query.Where(o => o.Firstname.EndsWith(filter));
return query.Select(o => o.Firstname).Distinct().Take(maxResults).ToArray();
case "Surname":
query = query.Where(o => o.Surname.EndsWith(filter));
return query.Select(o => o.Surname).Distinct().Take(maxResults).ToArray();
}
break;
case "Contains":
switch (searchType)
{
case "IdentityCode":
query = query.Where(o => o.IdentityCode.Contains(filter));
return query.Select(o => o.IdentityCode).Take(maxResults).ToArray();
case "Firstname":
query = query.Where(o => o.Firstname.Contains(filter));
return query.Select(o => o.Firstname).Distinct().Take(maxResults).ToArray();
case "Surname":
query = query.Where(o => o.Surname.Contains(filter));
return query.Select(o => o.Surname).Distinct().Take(maxResults).ToArray();
}
break;
}
return query.Select(o => o.IdentityCode).Take(maxResults).ToArray();
}
This is exactly where dynamically building expressions is useful:
public string[] GetPeopleAutoComplete(
string filter, int maxResults, string searchType, string searchOption)
{
IQueryable<Person> query = _context.People;
var property = typeof(Person).GetProperty(searchType);
var method = typeof(string).GetMethod(searchOption, new[] { typeof(string) });
query = query.Where(WhereExpression(property, method, filter));
var resultQuery = query.Select(SelectExpression(property));
if (searchType == "Firstname" || searchType == "Lastname")
resultQuery = resultQuery.Distinct();
return resultQuery.Take(maxResults).ToArray();
}
Expression<Func<Person, bool>> WhereExpression(
PropertyInfo property, MethodInfo method, string filter)
{
var param = Expression.Parameter(typeof(Person), "o");
var propExpr = Expression.Property(param, property);
var methodExpr = Expression.Call(propExpr, method, Expression.Constant(filter));
return Expression.Lambda<Func<Person, bool>>(methodExpr, param);
}
Expression<Func<Person, string>> SelectExpression(PropertyInfo property)
{
var param = Expression.Parameter(typeof(Person), "o");
var propExpr = Expression.Property(param, property);
return Expression.Lambda<Func<Person, string>>(propExpr, param);
}
This doesn't solve your default case, but that should be relatively easy to add. Also, using reflection like this might be slow, so you might want to cache the results of GetProperty()
and GetMethod()
.
Another thing to note is that the part that chooses whether to use Distinct()
still depends on the property names, but maybe you have a better condition for that (or you could use attributes on the properties).
And the two helper methods don't need to know anything about Person
, so it would be trivial to make them generic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With