さまざまなタイプのオブジェクトに適用できる再利用可能な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.
独自の再利用可能なフィルターを作成するには、PredicateBuilderとMemberExpressionExtensionsのみが必要です。それらをプロジェクトにコピーするだけです。再利用可能なフィルターは、拡張機能(WhereOverlapのように)、静的ヘルパー、またはクラス(UnderControlPayFilterのように)としてスタイルを設定できます。
-私は、再利用可能なフィルタのカップル作ったGitHubに、NuGetを(PredicateBuilderとMemberExpressionExtensionsを含みます)。