Membuat filter Linq yang dapat digunakan kembali (pembuat predikat untuk Di mana) yang dapat diterapkan ke berbagai jenis

Cara untuk membuat filter Linq yang dapat digunakan kembali (pembuat predikat untuk klausa Where) yang dapat diterapkan ke berbagai jenis objek. Bidang objek yang akan difilter ditentukan menggunakan MemberExpression.





Metode ini cocok untuk Entity Framework, termasuk operasi Async.





Ide utama. Apa itu filter yang dapat digunakan kembali?

Misalnya ada pesanan:





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



Biarkan Anda perlu menemukan semua pesanan yang akan berlaku dalam 7 hari ke depan.





Dengan menggunakan pembuat filter yang dapat digunakan kembali (jika diterapkan), Anda dapat menemukan pesanan seperti ini:





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.





Untuk membuat filter Anda sendiri yang dapat digunakan kembali, Anda hanya memerlukan PredicateBuilder dan MemberExpressionExtensions . Cukup salin ke proyek Anda. Filter yang dapat digunakan kembali dapat diberi gaya sebagai ekstensi (seperti di WhereOverlap), sebagai pembantu statis, atau sebagai kelas (seperti di UnderControlPayFilter).





Saya telah membuat beberapa filter yang dapat digunakan kembali - GitHub , NuGet (termasuk PredicateBuilder dan MemberExpressionExtensions).








All Articles