LINQ to Entities and Polymorphism

Let's say I have a simple model:

public class Movie
{
    public int ID { get; set; }

    public string Name { get; set; }
}

And DbContext:

public class MoviesContext : DbContext
{
    ...
    public DbSet<Movie> Movies { get; set; }
}

I also have a method in the MovieContext class that filters movies by substring as follows:

return Movies.Where(m => m.Name.Contains(filterString)).Select(m => m);

Now suppose I would like to add a new model, say:

public class Person
{
    public int ID { get; set; }

    public string FirstName { get; set; }

    public string MiddleName { get; set; }

    public string LastName { get; set; }

    public string FullName { get { return FirstName + (MiddleName?.Length > 0 ? $" {MiddleName}" : "") + $" {LastName}"; } }
}

I also want to filter people (DbSet Persons) by name (i.e. FullName). I would like DRY, so it’s preferable to generalize the MovieContext filtering method. And, importantly, I would like to do filtering at the database level. So I have to deal with LINQ for entities.

, . , " ". , . , - LINQ Entities FullName ( , ), - :

return dbset.Where(ent => ent.NameContains(filterString)).Select(ent => ent);

, ? - ( ), . , , .

+4
4

, NameFilterable, - :

public interface IHasPredicateGetter<T> {
   [NotNull] Expression<Func<T, bool>> GetPredicateFromString([NotNull] string pValue);
}

public class Movie : IHasPredicateGetter<Movie> {
   public int ID { get; set; }
   public string Name { get; set; }

   public Expression<Func<Movie, bool>> GetPredicateFromString(string pValue) {
      return m => m.Name.Contains(pValue);
   }
}

, , . , , , . , , , , .

+1

.

[1] :

public abstract class NameFilterable
{
    protected static Expression<Func<T, bool>> False<T>() { return f => false; }

    public virtual Expression<Func<T, bool>> GetNameContainsPredicate<T>(string filterString)
    {
        return False<T>();
    }
}

[2] Person ( Movie, ):

public class Person : NameFilterable
{
    ...
    public override Expression<Func<T, bool>> GetNameContainsPredicate<T>(string filterString)
    {
        return entity => 
            String.IsNullOrEmpty(filterString) ||
            (entity as Person).LastName.Contains(filterString) ||
            (entity as Person).FirstName.Contains(filterString) ||
            (((entity as Person).MiddleName != null) && (entity as Person).MiddleName.Contains(filterString))
        ;
    }
}

[3] MovieContext:

    private static IQueryable<T> _filterDbSet<T>(DbSet<T> set, Expression<Func<T, bool>> filterPredicate) where T : class
    {
        return set
            .Where(filterPredicate)
            .Select(ent => ent);
    }

    private static IQueryable<T> _filterDbSet<T>(DbSet<T> set, string search = null) where T : NameFilterable, new()
    {
        T ent = new T();
        return _filterDbSet<T>(set, (ent as NameFilterable).GetNameContainsPredicate<T>(search));
    }

    public static ICollection<T> Filter<T>(DbSet<T> set, string search = null) where T : NameFilterable, new()
    {
        return _filterDbSet(set, search).ToList();
    }

, . , .

[1] T, Person Person ( ). T Person ( Person).

[2] GetNameContainsPredicate (- LINQ ):

        return entity => 
        {
            Person p = entity as Person;
            String.IsNullOrEmpty(filterString) ||
            p.LastName.Contains(filterString) ||
            p.FirstName.Contains(filterString) ||
            ((p.MiddleName != null) && p.MiddleName.Contains(filterString))
        };

[3] ( ), T (T ent = new T();).

[4] FullName.Contains(filterString)

, : , - , ?

+1

, , , , null. ,

//gets the property info of the property with the giving name
     public static PropertyInfo GetPropetyInfo<T>(string name)
        {
            var type = typeof(T);
            var property = type.GetProperty(name);
            return property;
        }
//Creates an expression thats represents the query
public static Func<T, bool> GetFilterExpression<T>( string propertyName, object propertyValue)
        {
            var prop = GetPropetyInfo<T>(propertyName);
            if(prop==null)return t=>false;
            var parameter = Expression.Parameter(typeof(T), "t");
            Expression expression = parameter;
            var left = Expression.Property(expression, prop);
            if (prop.PropertyType == typeof(string))
            {
                var toLower = typeof(string).GetMethods().FirstOrDefault(t => t.Name.Equals("ToLower"));
                var tlCall = Expression.Call(left, toLower);
                var right = Expression.Constant(propertyValue.ToString().ToLower());
                var contains = Expression.Call(tlCall, typeof(string).GetMethod("Contains"), right);
                var containsCall = Expression.IsTrue(contains);
                expression = Expression.AndAlso(Expression.NotEqual(left, Expression.Constant(null)), containsCall);                
            }
            else
            {
                if (prop.PropertyType.ToString().ToLower().Contains("nullable"))
                {
                    var getValue = prop.PropertyType.GetMethods().FirstOrDefault(t => t.Name.Equals("GetValueOrDefault"));
                    var getValueCall = Expression.Call(left, getValue);
                    var right = Expression.Constant(propertyValue);
                    expression = Expression.Equal(getValueCall, right);
                }
                else
                {                   
                    var value = Convert.ChangeType(propertyValue,prop.PropertyType);
                    var right = Expression.Constant(value);
                    expression = Expression.Equal(left, right);                  
                }
            }
            return Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { parameter }).Compile();
        }

,

 var expression = YOURCLASS.GetFilterExpression<Person>("LastName", "Jhon");
var result=dbset.Where(expression);
0

, , EF, , , , . , , , . : , , Where ?

. , , , (, INamedObject). , . , , ? , , , .

, ? , , ? , ? Where , . , x => x.Name == value , , - .

0

All Articles