2017-08-29 43 views
0

我正在关注这个示例:https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation并试图实现我自己的自定义属性进行验证。在AddValidation方法中访问模型数据asp.net核心自定义验证

现在,viewmodel有两个字段,我希望从该方法内部访问,以便可以使用“data-val”属性呈现它们。我的问题是,我怎么能从上下文在这里说一个名为“Myprop”的属性?当我调试时,我可以选择context.ActionContext.ViewData.Model下的信息,但在调试时,我无法获得该信息,因为我使用Visual Studio“快速监视”功能。自定义属性位于视图模型上的属性上。

public void AddValidation(ClientModelValidationContext context) 
{ 
    if (context == null) 
    { 
     throw new ArgumentNullException(nameof(context)); 
    } 

    MergeAttribute(context.Attributes, "data-val", "true"); 
    MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage()); 

    var year = _year.ToString(CultureInfo.InvariantCulture); 
    MergeAttribute(context.Attributes, "data-val-classicmovie-year", year); 
} 

回答

1

我遇到了类似的问题。简短的回答是,你不能从那里得到。您只能从那里访问元数据,而不是实际的模型。原因是您的模型元数据和基于它的验证是在首次使用时完成的,然后进行缓存。所以你永远无法通过属性装饰器来改变基于模型返回的验证规则。

如果您需要动态地决定哪些客户端data-val-*属性来渲染过的基于模型的实例/内容,则需要从DefaultValidationHtmlAttributeProvider继承,而不是使用属性,并重写AddValidationAttributes方法。这是迄今为止我发现的唯一途径。这是因为这种方法里面,你可以访问ModelExplorer

public class CustomValidationHtmlAttributeProvider : DefaultValidationHtmlAttributeProvider 
{ 
    private readonly IModelMetadataProvider metadataProvider; 

    public CustomValidationHtmlAttributeProvider(IOptions<MvcViewOptions> optionsAccessor, IModelMetadataProvider metadataProvider, ClientValidatorCache clientValidatorCache) 
     : base(optionsAccessor, metadataProvider, clientValidatorCache) 
    { 
     this.metadataProvider = metadataProvider; 
    } 

    public override void AddValidationAttributes(ViewContext viewContext, ModelExplorer modelExplorer, IDictionary<string, string> attributes) 
    { 
     //base implimentation 
     base.AddValidationAttributes(viewContext, modelExplorer, attributes); 

     //re-create the validation context (since it's encapsulated inside of the base implimentation) 
     var context = new ClientModelValidationContext(viewContext, modelExplorer.Metadata, metadataProvider, attributes); 

     //Only proceed if it's the model you need to do custom logic for 
     if (!(modelExplorer.Container.Model is MyViewModelClass model) || !modelExplorer.Metadata.PropertyName == "Myprop") return; 

     //Do stuff! 
     var validationAttributeAdapterProvider = viewContext.HttpContext.RequestServices.GetRequiredService<IValidationAttributeAdapterProvider>(); 

     if (model.Myprop) 
     { 
      var validationAdapter = (RequiredAttributeAdapter)validationAttributeAdapterProvider.GetAttributeAdapter(new RequiredAttribute(), null); 
      validationAdapter.Attribute.ErrorMessage = "You not enter right stuff!"; 
      validationAdapter.AddValidation(context); 
     } 
    } 
} 

,然后在Startup

public void ConfigureServices(IServiceCollection services) 
{ 
    //All your other DI stuff here 

    //register the new ValidationHtmlAttributeProvider 
    services.AddSingleton<ValidationHtmlAttributeProvider, PseudoAttributeValidationHtmlAttributeProvider>(); 
} 

缺点的ConfigureServices()注册这个类是,如果你有多个型号你需要这样做,它真的很难看。如果有人找到了更好的方法,我很乐意听到它:-)

+0

太棒了!在我接受答案之前,我会稍微尝试一下,然后回复给你回答 –

+0

我可以问问你的其他实现是怎么样的?在制作自定义属性时,您需要继承ValidationAttribute,那么您如何从DefaultValidationHtmlAttributeProvider继承并定制它并使用自定义逻辑进行重写? –

+0

您无法从属性中执行此操作。您无法访问模型,并且触发装饰器属性验证的逻辑不会传递该模型。这是设计的原因,因为迭代您的属性以构建元数据和验证规则的结果只会被解雇一次然后缓存。所以它不能依赖于模型。我会在答案中充实我的例子。 –

相关问题