18

FluentValidation を使用してオブジェクト内のコレクションを検証し、コレクション項目の要素を親オブジェクトの要素と比較しています。

目標の出力は、コレクションを失敗させるだけでなく、コレクション内の失敗したアイテムごとに ValidationFailures を受け取ることです。

ソフトウェア項目のリストを含むソフトウェアの注文があります。注文がレガシー システムの場合、選択されたソフトウェアはレガシー ソフトウェアのみであり、逆の場合、非レガシー システムは非レガシー ソフトウェアのみを持つことができます。

私のモデル:

public class SoftwareOrder
{
   public bool IsLegacySystem;
   public List<SoftwareItem> Software;
   (...other fields...)
}
public class SoftwareItem
{
   public bool Selected;
   public bool IsLegacySoftware;
   public int SoftwareId;
}

バリデーター:

public class SoftwareOrderValidator : AbstractValidator<SoftwareOrder>
{
   public SoftwareOrderValidator()
   {
     (..other rules..)

     When(order => order.IsLegacySystem == true, () =>
     {
        RuleForEach(order => order.SoftwareItem)
           .SetValidator(new SoftwareItemValidator(true));
     });
     When(order => order.IsLegacySystem == false, () =>
     {
        RuleForEach(order => order.SoftwareItem)
           .SetValidator(new SoftwareItemValidator(false));
     });
   }
}
public class SoftwareItemValidator : AbstractValidator<SoftwareItem>
{
   public SoftwareItemValidator(bool IsLegacySystem)
   {
     When(item => item.Selected, () =>
     {
        RuleFor(item => item.IsLegacySoftware)
            .Equal(IsLegacySystem).WithMessage("Software is incompatible with system");
     });
   }
}

ご覧のとおり、条件ごとに When を設定することでこれを実現しています。機能しますが、DRY に違反しており、条件が 2 つ以上ある状況で使用するのは実用的ではありません。

理想的には、これを実行できる単一の RuleForEach が必要です。When は必要ありません。次のようなものです。

RuleForEach(order => order.SoftwareItem)
   .SetValidator(new SoftwareItemValidator(order => order.IsLegacySystem));

しかし、IsLegacySystem をそのコンストラクターに渡す方法がわかりません。

4

3 に答える 3

28

2年後、この未回答の質問がどれだけ多くのビューを獲得したかを見て、私はこれをもう一度試してみることにしました. 私は2つの答えを思いつきました。

最初の回答は、質問で説明されている状況に対する最善の解決策です。

public class SoftwareOrderValidator : AbstractValidator<SoftwareOrder>
{
   public SoftwareOrderValidator()
   {
      RuleForEach(order => order.SoftwareItem)
         .Must(BeCompatibleWithSystem)
         .WithMessage("Software is incompatible with system");

   }

   private bool BeCompatibleWithSystem(SoftwareOrder order, SoftwareItem item)
   {
      if (item.Selected)
         return (order.IsLegacySystem == item.IsLegacySoftware);
      else
         return true;
   }
}

Predicate Validators (別名 Must) は、オブジェクトとプロパティの両方を引数として取ることができます。これにより、IsLegacySystem、または親オブジェクトのその他のプロパティと直接比較できます。

おそらく、この 2 番目の回答は使用しないでください。AbstractValidator のコンストラクターに引数を渡す必要があると思われる場合は、再評価して別のアプローチを見つけることをお勧めします。その警告が述べられているので、これを達成する1つの方法があります。

基本的に、ダミーの Must() を使用して、ラムダの外、コンストラクターの外で変数を設定できるようにします。次に、それを使用して、その値を 2 番目のバリデーターのコンストラクターに取得できます。

public class SoftwareOrderValidator : AbstractValidator<SoftwareOrder>
{
   private bool _isLegacySystem;

   public SoftwareOrderValidator()
   {
      RuleFor(order => order.IsLegacySystem)
         .Must(SetUpSoftwareItemValidatorConstructorArg);

      RuleForEach(order => order.SoftwareItem)
         .SetValidator(new SoftwareItemValidator(_isLegacySystem));

   }

   private bool SetUpSoftwareItemValidatorConstructorArg(bool isLegacySystem)
   {
      _isLegacySystem = isLegacySystem;
      return true;
   }
}
public class SoftwareItemValidator : AbstractValidator<SoftwareItem>
{
   public SoftwareItemValidator(bool IsLegacySystem)
   {
     When(item => item.Selected, () =>
     {
        RuleFor(item => item.IsLegacySoftware)
            .Equal(IsLegacySystem).WithMessage("Software is incompatible with system");
     });
   }
}
于 2015-07-02T17:55:12.027 に答える
17

これは古い質問であり、回答はすでに与えられていることは知っていますが、今日この質問に出くわし、現在のバージョンの FluentValidation (私は6.2.1.0を使用しています) にSetValidatorの新しいオーバーロードがあることがわかりました。 Func をパラメーターとして渡します。

したがって、次のことができます。

RuleForEach(x => x.CollectionProperty)
    // x below will referente the Parent class
    .SetValidator(x => new CollectionItemValidator(x.ParentProperty);

うまくいけば、これは誰かを助けることができます。

于 2016-09-05T23:03:11.380 に答える
1

Custom メソッドを使用するこのアプローチはどうですか。

public class SoftwareOrder
{
    public bool IsLegacySystem;
    public List<SoftwareItem> Software;
}
public class SoftwareItem
{
    public bool Selected;
    public bool IsLegacySoftware;
    public int SoftwareId;
}

public class SoftwareOrderValidator : AbstractValidator<SoftwareOrder>
{
    public SoftwareOrderValidator()
    {
        Custom(order =>
        {
            if (order.Software == null)
                return null;

            return order.Software.Any(item => order.IsLegacySystem != item.IsLegacySoftware)
               ? new ValidationFailure("Software", "Software is incompatible with system")
               : null;
        });

        // Validations other than legacy check
        RuleFor(order => order.Software).SetCollectionValidator(new SoftwareItemValidator());
    }
}

public class SoftwareItemValidator : AbstractValidator<SoftwareItem>
{
    // Validation rules other than legacy check
    public SoftwareItemValidator()
    {
    }
}
于 2013-09-10T22:26:40.577 に答える