CrĂ©ation de filtres Linq rĂ©utilisables (gĂ©nĂ©rateurs de prĂ©dicats pour Where) pouvant ĂȘtre appliquĂ©s Ă  diffĂ©rents types

Un moyen de crĂ©er des filtres Linq rĂ©utilisables (gĂ©nĂ©rateurs de prĂ©dicat pour les clauses Where) qui peuvent ĂȘtre appliquĂ©s Ă  diffĂ©rents types d'objets. Les champs des objets Ă  filtrer sont spĂ©cifiĂ©s Ă  l'aide de MemberExpression.





La méthode convient à Entity Framework, y compris les opérations Async.





Idée principale. Qu'est-ce qu'un filtre réutilisable?

Par exemple, il y a des commandes:





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



Laissez-vous besoin de trouver toutes les commandes qui seront valables dans les 7 prochains jours.





En utilisant un générateur de filtre réutilisable (s'il a été implémenté), vous pouvez trouver des commandes comme celle-ci:





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.





Pour crĂ©er vos propres filtres rĂ©utilisables, vous n'avez besoin que de PredicateBuilder et MemberExpressionExtensions . Copiez-les simplement dans votre projet. Les filtres rĂ©utilisables peuvent ĂȘtre stylisĂ©s comme une extension (comme dans WhereOverlap), comme une aide statique ou comme une classe (comme dans UnderControlPayFilter).





J'ai créé quelques filtres réutilisables - GitHub , NuGet (comprend PredicateBuilder et MemberExpressionExtensions).








All Articles