Creación de filtros Linq reutilizables (constructores de predicados para Dónde) que se pueden aplicar a diferentes tipos

Una forma de crear filtros Linq reutilizables (constructores de predicados para cláusulas Where) que se pueden aplicar a diferentes tipos de objetos. Los campos de los objetos que se filtrarán se especifican mediante MemberExpression.





El método es adecuado para Entity Framework, incluidas las operaciones asincrónicas.





Idea principal. ¿Qué es un filtro reutilizable?

Por ejemplo, hay pedidos:





class Order { 
	public DateTime Start { get; set; }
	public DateTime? End { get; set; }
}
      
      



Deja que necesites encontrar todos los pedidos que serán válidos en los próximos 7 días.





Con un generador de filtros reutilizable (si estuviera implementado), puede encontrar pedidos como este:





var ordersFiltred = orders
	.WhereOverlap(
		//   MemberExpressions
		//      
		fromField: oo => oo.Start,
		toField: oo => oo.End,

		//   
		from: DateTime.Now,
		to: DateTime.Now.AddDays(7))
	.ToList();
      
      



WhereOverlap . , :





class Trip { 
	public DateTime? From { get; set; }
	public DateTime? To { get; set; }
}
      
      



var tripsFiltred = trips
	.WhereOverlap(
		//   MemberExpressions
		//      
		fromField: oo => oo.From,
		toField: oo => oo.To,

		from: DateTime.Now,
		to: DateTime.Now.AddDays(7))
	.ToList();
      
      



- , , -. ( ) WhereOverlap.





.





WhereOverlap, . , WhereOverlap, “”, “”. .





:





class Payout { 
	public decimal Total { get; set; }
	public bool UnderControl { get; set; }
}

class Premium {
	public decimal Sum { get; set; }
	public bool RequiresConfirmation { get; set; }
}
      
      



:





class UnderControlPayFilter {
	readonly decimal Limit;
	public UnderControlPayFilter(decimal limit) {
		Limit = limit;
	}

	public Expression<Func<TEnt, bool>> Create<TEnt>(
		Expression<Func<TEnt, decimal>> sumField) {

		// GreaterOrEqual -  
		// GreaterOrEqual -  extension,  
		//  -    (Expression sumField)
		//  -       (Limit)
		return sumField.GreaterOrEqual(Limit);
	}
}
      
      



UnderControlPayFilter :





//      
//
//   ( 1000)    ,
//  UnderControlPayFilter   IoC-.
//    (  )
//   
var underControlPayFilter = new UnderControlPayFilter(1000);


//
//     

var payoutPredicate =
	underControlPayFilter.Create<Payout>(pp => pp.Total);

// ,  , payouts -  ,
//       Entity Framework DbSet 
var payouts = new[] {
	new Payout{ Total = 100 },
	new Payout{ Total = 50, UnderControl = true },
	new Payout{ Total = 25.5m },
	new Payout{ Total = 1050.67m }
}
.AsQueryable()
.Where(payoutPredicate)
.ToList();


//
//     

var premiumPredicate =
	underControlPayFilter.Create<Premium>(pp => pp.Sum);

// ,  , premiums -  ,
//       Entity Framework DbSet 
var premiums = new[] {
	new Premium{ Sum = 2000 },
	new Premium{ Sum = 50.08m },
	new Premium{ Sum = 25.5m, RequiresConfirmation = true },
	new Premium{ Sum = 1070.07m }
}
.AsQueryable()
.Where(premiumPredicate)
.ToList();
      
      



, GreaterOrEqual extension:





public static class MemberExpressionExtensions {
    public static Expression<Func<TEnt, bool>> GreaterOrEqual<TEnt, TProp>(
        this Expression<Func<TEnt, TProp>> field, TProp val)
            => Expression.Lambda<Func<TEnt, bool>>(
                Expression.GreaterThanOrEqual(field.Body, Expression.Constant(val, typeof(TProp))), 
                field.Parameters);
}
      
      



extension- LessOrEqual, Equal, HasNoVal .





“” “”

, , , .





UnderControlPayFilter:





class UnderControlPayFilter {
	readonly decimal Limit;
	public UnderControlPayFilter(decimal limit) {
		Limit = limit;
	}

	public Expression<Func<TEnt, bool>> Create<TEnt>(
		Expression<Func<TEnt, decimal>> sumField,
		Expression<Func<TEnt, bool>> controlMarkField) {

			// PredicateBuilder   (. )
			return PredicateBuilder.Or(
				sumField.GreaterOrEqual(Limit),
				controlMarkField.Equal(true));
	}
}
      
      



:





//  

var payoutPredicate =
	underControlPayFilter.Create<Payout>(
		sumField: pp => pp.Total,
		controlMarkField: pp => pp.UnderControl);

//  

var premiumPredicate = 
	underControlPayFilter.Create<Premium>(
		sumField: pp => pp.Sum,
		controlMarkField: pp => pp.RequiresConfirmation);
      
      



PredicateBuilder “A universal PredicateBuilder” Pete Montgomery.





Para crear sus propios filtros reutilizables, solo necesita PredicateBuilder y MemberExpressionExtensions . Simplemente cópielos a su proyecto. Los filtros reutilizables se pueden diseñar como una extensión (como en WhereOverlap), como un ayudante estático o como una clase (como en UnderControlPayFilter).





Hice un par de filtros reutilizables: GitHub , NuGet (incluye PredicateBuilder y MemberExpressionExtensions).








All Articles