You are using entity framework, which can access your table of Persons
. You'll probably have a DbContext
that has a property to access this table:
public DbSet<Person> Persons {get; set;}
You also have two strings. One string represents the name of the Person
property that you want to filter on, the other string represents the value that this property should have.
To make you code easier to read, easier to reuse, easier to maintain and unit test, my advice would be to split your problem into two subproblem:
- Translate the string
columnToSearch
(which in fact is the name of the column that you want to search), into a propertySelector, similar to the keySelector in GroupBy.
- Create an extension method that uses this KeySelector to select the property, and uses the
searchTerm
and Where
to keep only those Persons
that have equal value.
The nice thing about this separation, is that you can detect incorrect values for columnToSearch before you access the database. If you don't want to support certain properties, like foreign keys, you can leave them out. The method is easy to understand, easy to reuse, easy to unit test, and easy to change, for instance if you want to add a new column.
Expression<Func<Person, string>> ToPropertySelector(string columnName)
{
switch (columName)
{
case nameof(Person.Name):
return person => person.Name;
case nameof(Person.FirstName):
return person => person.FirstName;
...
default: // unsupported columnName
throw new InvalidArgumentException(...);
}
}
If your columns have other names than the ones that you want to communicate with your users, for instance, for instance if extenal uses think of LastName
instead of name, you can easily change the procedure:
case "Name":
case "LastName":
return person => person.Name;
Also if later the colum name changes, external users won't have to change.
Usage:
string columName = "Email";
string value = "MyName.Gmail.Com";
using (var dbContext = new MyDbContext(...))
{
var propertySelector = ToPropertySelector(columnName);
return dbContext.Persons
.Where(person => propertySelector(person) == value)
.ToList();
}
Of course, this will only work if your properties have a string as value. If you also want to support other property types, you need to create an extension method that has the proper type:
Expression<Func<Person, TProperty>> ToPropertySelector<TProperty>(string columnName)
{
... see above
}
public static IQueryable<Person> Where<TProperty>(IQueryable<Person> persons,
string columName,
TProperty value)
{
var propertySelector = ToPropertySelector<TProperty>(columnName);
return persons.Where(person => propertySelector(person) == value);
}
public static IQueryable<Person> Where<TProperty>(IQueryable<Person> persons,
string columName,
string valueTxt)
{
Type propertyType = typeof(TProperty);
TypeConverter converter = propertyType.GetConverter();
TProperty value = converter.ConvertFromString(valueTxt)
return persons.Where(columnName, value);
}
Usage:
DateTime birthDay = new DateTime(1993, 11, 23);
IQueryable<Person> persons = ...
var personsBornOnDate = persons.Where("BirthDay", birthDay);
Example of reuse: you don't have to use BirthDay as a string, you can use a property selector:
var personsBornOnDate = persons.Where(person => person.BirthDay, birthDay);
Nice thing of the latter is that the compiler will warn you if you use a non-existing property. The string version will only warn you at run time.