さまざまなタイプに適用できる再利用可能なLinqフィルター(Whereの述語ビルダー)の作成

さまざまなタイプのオブジェクトに適用できる再利用可能なLinqフィルター(Where句の述語ビルダー)を作成する方法。フィルタリングされるオブジェクトのフィールドは、MemberExpressionを使用して指定されます。





この方法は、非同期操作を含むEntityFrameworkに適しています。





本旨。再利用可能なフィルターとは何ですか?

たとえば、次のような注文があります。





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



今後7日間で有効になるすべての注文を見つける必要があります。





再利用可能なフィルタービルダー(実装されている場合)を使用すると、次のような注文を見つけることができます。





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.





独自の再利用可能なフィルターを作成するには、PredicateBuilderMemberExpressionExtensionsのみが必要ですそれらをプロジェクトにコピーするだけです。再利用可能なフィルターは、拡張機能(WhereOverlapのように)、静的ヘルパー、またはクラス(UnderControlPayFilterのように)としてスタイルを設定できます。





-私は、再利用可能なフィルタのカップル作ったGitHubにNuGetを(PredicateBuilderとMemberExpressionExtensionsを含みます)。








All Articles