bugfix> c# > 投稿

ASP.NET Core 2 MVCアプリケーションを構築しています。多くの場合、ユーザー入力を検証するために依存関係を利用する必要があります。検証メソッドをユニットテスト可能にし、モック化された依存関係をそれらに注入できるようにしたいと考えています。これは私が以前にMVC5で大成功を収めたものですが、ASP.NET Core 2に相当するものは解決できません。

これは私がMVC5でそれを行う方法です:

// the view model to be validated
public class MyViewModel {
  public string Username { get; set; }
}
// the model validator that will have dependencies injected into it
public class MyViewModelValidator : ModelValidator
{
  private IUserService users;
  private MyViewModel model;
  public MyViewModelValidator(ModelMetadata metadata, ControllerContext controllerContext, IUserService users)
    : base(metadata, controllerContext)
    {
      this.users = users;
      this.model = base.Metadata.Model as MyViewModel;
    }
  public override IEnumerable<ModelValidationResult> Validate(object container)
  {
      List<ModelValidationResult> errors = new List<ModelValidationResult>();
      if (this.users.CheckExists(this.model.Username))
      {
          errors.Add(new ModelValidationResult() { MemberName = nameof(MyViewModel.Username), Message = "Username is not available" });
      }
      return errors;
  }
}
// this class works out which validator is required for a given model and 
// injects the appropriate dependencies that is resolves using unity in my
// in my case
public class ViewModelValidatorProvider : ModelValidatorProvider
{
  private IUnityContainer container;
  public ViewModelValidatorProvider() => this.container = DependencyResolver.Current.GetService<IUnityContainer>();
  public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
  {
    if (metadata.ModelType == typeof(MyViewModel))
      yield return new MyViewModelValidator(metadata, context, this.container.Resolve<IUserService>());
  }
}
// the provider is hooked up in the app start in Global.asax.cs file
public class MvcApplication : System.Web.HttpApplication
{
  protected void Application_Start()
  {
    ModelValidatorProviders.Providers.Add(new ViewModelValidatorProvider());
  }
}

これで、モックされた依存関係を持つバリデーターのインスタンスを作成することができます。残念ながらASP.NET Core 2には ModelValidator がありませんクラスと私がこれまでに見つけたすべてのものは、コントローラを介して依存関係を注入するか、 IValidatableObject で依存関係を解決したいようです s Validate() 関数。

MVCコアでこれを行うことは可能ですか?

回答 1 件
  • だから、@ Nkosiの投稿に続いて、私は正しい道を進んで(私は思う)質問に関するコメントを残し、タイプフィルターに基づく検証システムを実装することになりました。

    開始するには、型フィルターに実装する必要がある基本検証モデルがあります。

       public abstract class BaseViewModelValidator<TModel> : IAsyncActionFilter
        where TModel : class
    {
        public async virtual Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // get the model to validate
            if (context.ActionArguments["model"] is TModel model)
                await this.ValidateAsync(model, context.ModelState);
            else
                throw new Exception($"View model of type `{context.ActionArguments["model"].GetType()}` found, type of `{typeof(TModel)}` is required.");
            await next();
        }
        public abstract Task ValidateAsync(TModel model, ModelStateDictionary state);        
    }
    
    

    次に、 [TypeFilter(typeof(SomeActionFilter))] よりも名前付き属性として使用する方がはるかに優れているため 、私は TypeFilterAttribute を作成します  これは、次のようにベースバリデータの実装をラップします。

    public class DemoViewModelValidatorAttribute : TypeFilterAttribute
    {
        public DemoViewModelValidatorAttribute() 
            : base(typeof(DemoViewModelValidator))
        {
        }
        internal class DemoViewModelValidator : BaseViewModelValidator<DemoViewModel>
        {
            private readonly ISomeService service;
            // dependencies are injected here (assuming you've registered them in the start up)
            public DemoViewModelValidator(ISomeService service) => this.service = service;
            public async override Task ValidateAsync(DemoViewModel model, ModelStateDictionary state)
            {
                if (await this.service.CheckSomethingAsync(model))
                    state.AddModelError(nameof(model.SomeProperty), $"Whoops!!!");
            }
        }
    }
    
    

    その後、 DemoViewModelValidator を単体テストできます  あなたの心のコンテンツに!うまくいけば、誰かがこれを役に立つと思います!

あなたの答え