I am trying to write an universal search to use for all objects. I have this code, which is working fine to search in just one object's properties, but I would also like to search also in properties in related objects.
Eg. I have these Models/Objects
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address{ get; set; }
public ICollection<Contract> Contracts { get; set; }
}
public class Contract
{
public int Id { get; set; }
public DateTime From{ get; set; }
public DateTime To{ get; set; }
public string Comment{ get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
}
and I want to search if any of properties contains some a string eg. "Peter", I will call it this way:
string searchString = "Peter";
var customers = db.Customers
.Include(x => x.Contracts)
.WhereAnyPropertiesOfSimilarTypeContains(searchString);
this code will check if any properties of 'Customer' contains string "Peter". But I would also need to check if the related model 'Contract' contains "Peter.
public static class EntityHelper
{
public static IQueryable<TEntity> WhereAnyPropertiesOfSimilarTypeContains<TEntity, TProperty>(this IQueryable<TEntity> query, TProperty value)
{
var param = Expression.Parameter(typeof(TEntity));
var predicate = PredicateBuilder.False<TEntity>(); //--- True to equal
var entityFields = GetEntityFieldsToCompareTo<TEntity, TProperty>();
foreach (var fieldName in entityFields)
{
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var predicateToAdd = Expression.Lambda<Func<TEntity, bool>>(
Expression.Call(
Expression.PropertyOrField(param, fieldName), method,
Expression.Constant(value)), param);
predicate = predicate.Or(predicateToAdd); //--- And to equal
}
return query.Where(predicate);
}
// TODO: You'll need to find out what fields are actually ones you would want to compare on.
// This might involve stripping out properties marked with [NotMapped] attributes, for
// for example.
public static IEnumerable<string> GetEntityFieldsToCompareTo<TEntity, TProperty>()
{
Type entityType = typeof(TEntity);
Type propertyType = typeof(TProperty);
var fields = entityType.GetFields()
.Where(f => f.FieldType == propertyType)
.Select(f => f.Name);
var properties = entityType.GetProperties()
.Where(p => p.PropertyType == propertyType)
.Select(p => p.Name);
return fields.Concat(properties);
}
}
Thanks.