简体   繁体   English

将数据注释应用于MVC中View模型的子属性?

[英]Applying Data Annotations to sub properties of the View Model in MVC?

Putting simple Data Annotations on properties is great, 在属性上放置简单的数据注释很棒,

public class UnicornViewModel
{
   [Required]
   public string Name { get; set; }

But lets say I'm have something like this: 但是让我说我​​有这样的事情:

public class SuperPower
{
   public class Name { get; set; }
}

public class UnicornViewModel
{
   [Required]
   public string Name { get; set; }

   public SuperPower PrimarySuperPower { get; set; }

   public SuperPower SecondarySuperPower { get; set; }

How do I apply the Required attribute on PrimarySuperPower.Name while leaving it optional for SecondarySuperPower.Name? 如何在PrimarySuperPower.Name上应用Required属性,同时为SecondarySuperPower.Name保留可选属性? Preferably 1. something that ties into client side validation and 2. with out any special handling like checking the value of PrimarySuperPower.Name in the Action/Custom validator and add a ModelState error if it's empty. 最好是1.与客户端验证相关的东西,2。没有任何特殊处理,比如在Action / Custom验证器中检查PrimarySuperPower.Name的值,如果它是空的,则添加ModelState错误。 It would be great if there was something like: 如果有类似的话会很棒:

   [Required(p => p.Name)]
   public SuperPower PrimarySuperPower { get; set; }

   public SuperPower SecondarySuperPower { get; set; }

Generally this isn't supported: ASP.NET MVC3 Validation of nested view model object fields 通常,这不受支持: ASP.NET MVC3验证嵌套视图模型对象字段

But you can implement custom model validation, but doing so for both client and server side gets pretty complicated. 但是您可以实现自定义模型验证,但是对于客户端和服务器端这样做会变得非常复杂。

If you have your own template for the SuperPower object, it could look for an attribute of your own making: 如果您拥有自己的SuperPower对象模板,它可以查找您自己制作的属性:

   [RequiredSubProperty("Name")]
   public SuperPower PrimarySuperPower { get; set; }

And in the template just past the unobtrusive validation attributes into the htmlAttributes parameter of the TextBoxFor or whatever input helper you use. 在模板中,刚刚通过不显眼的验证属性进入TextBoxFor的htmlAttributes参数或您使用的任何输入助手。

If you are not using a template, I would forgo all that and just pass the unobtrusive validation attributes into the htmlAttributes parameter when displaying the first name but not for the second. 如果你没有使用模板,我会放弃所有这些,只是在显示名字时将不显眼的验证属性传递给htmlAttributes参数,而不是第二个名称。

Another option is for the UnicornViewModel to be flattened like 另一种选择是将UnicornViewModel扁平化

public class UnicornViewModel
{
   [Required]
   public string Name { get; set; }

   [Required]
   public string PrimarySuperPowerName { get; set; }

   public string SecondarySuperPowerName { get; set; }

It all depends on how much reuse you might get from more complicated approaches. 这一切都取决于您可以从更复杂的方法中获得多少重用。 When I tried to use templating alot, I found that in different contexts certain things about templates didn't make sense, and such I'd need lots of variations on an object template(when a child template is displayed on a parent's page, it doesn't make sense for the child to have a URL linking to the parent's detail, since you're already on that page, but everywhere else the child template is used, it should display that link to parent). 当我尝试使用模板很多时,我发现在不同的上下文中,某些关于模板的事情没有意义,而且我需要在对象模板上有很多变化(当父模板上显示子模板时,它没有意义让孩子有一个链接到父母细节的URL,因为你已经在那个页面上,但是在其他地方使用了儿童模板,它应该显示到父母的链接)。 Ultimately I stopped using templates, and occasionally use partials where there is a good case for lots of reuse. 最终我停止使用模板,并偶尔使用部分,其中有很多重用的好例子。 The UI is where the rubber meets the road and ViewModels won't be structured as nicely as your entity/business models might be. 用户界面是橡胶与道路相遇的地方,而ViewModels的结构与实体/商业模式不同。

You cannot do this with the standard data attributes. 您无法使用标准数据属性执行此操作。 The Required syntax you mention would not be possible either in a custom implementation, as there is no reference to the object you're trying to use the lambda against. 您提到的必需语法在自定义实现中是不可能的,因为没有引用您尝试使用lambda的对象。

You may be better off using a third party validation library, such as FluentValidation. 您可能最好使用第三方验证库,例如FluentValidation。 It gives considerable flexibility to your validation requirements. 它为验证要求提供了相当大的灵活性。

I, personally, am a fan of using the ModelMetadataClass to directorate my ViewModels. 我个人非常喜欢使用ModelMetadataClass来管理我的ViewModel。 If you are willing to go on step extra and use AutoMapper you could create a viewmodel as follows: 如果您愿意继续使用AutoMapper ,可以创建一个viewmodel,如下所示:

public class SuperPower
{
    public string Name { get; set; }
}

[MetadataType(typeof(UnicornViewModel.UnicornViewModelMetaData))]
public class UnicornViewModel
{
    public string Name { get; set; }

    public RequiredSuperPowerViewModel PrimarySuperPower { get; set; }

    public SuperPower SecondarySuperPower { get; set; }

    public class UnicornViewModelMetaData
    {
        [Required]
        public string Name { get; set; }

    }
}

[MetadataType(typeof(UnicornViewModel.UnicornViewModelMetaData))]
public class RequiredSuperPowerViewModel : SuperPower
{
    public class RequiredSuperPowerModelMetaData
    {
        [Required]
        public string Name { get; set; }

    }
}

This will allow you to pick which fields you would like required for a given model class without impacting your model. 这将允许您选择您希望给定模型类所需的字段,而不会影响您的模型。

If you are using AutoMapper you can rehydrate the original SuperPower as follows: 如果您使用的是AutoMapper,您可以按如下方式重新水化原始SuperPower:

SuperPower reqSuperPower = AutoMapper.Mapper.Map<RequiredSuperPowerViewModel, SuperPower>(Data.PrimarySuperPower);

This might be a late answer, but I found this question when searching for the same thing. 这可能是一个迟到的答案,但我在寻找同样的事情时发现了这个问题。 This is how I solved my particular situation: 这就是我解决我的特殊情况的方法:

Before I had this: 在我这之前:

public class ProductVm
{
    //+ some other properties        

    public Category Category {get; set;}
    public Category ParentCategory {get; set;}
}

For which I wanted to have something in the likes of: 为此,我希望在以下方面有一些东西:

public class ProductVm
{
    //some other properties        

    [DisplayName("Product Category", e => e.Description)]
    public Category Category {get; set;}
    [DisplayName("Parent Category", e => e.Description)]
    public Category ParentCategory {get; set;}
}

I couldn't enter this in the model itself since both are the same object class. 我无法在模型中输入它,因为两者都是相同的对象类。

I solved it like this (since I only needed to read the Description value in this case and not write it): 我解决了这个问题(因为在这种情况下我只需要读取描述值而不是写它):

public class ProductVm
{
    //some other properties        

    public Category Category {get; set;}
    public Category ParentCategory {get; set;}

    [DisplayName("Product Category")]
    public string Category => Category.Description;

    [DisplayName("Main Category")]
    public string ParentCategory => ParentCategory.Description;
}

You could possible just rewrite it a bit more to keep the remaining private backing fields and remove the property encapsulation of the Category objects, but in my case I still needed them to be public for other uses. 您可以稍微重写它以保留剩余的私有支持字段并删除Category对象的属性封装,但在我的情况下,我仍然需要将它们公开用于其他用途。

Concerning the above question I would do the following: 关于上述问题,我会做以下事项:

public class UnicornViewModel
{
    [Required]
    public string Name { get; set; }

    public SuperPower PrimarySuperPower { get; set; }

    public SuperPower SecondarySuperPower { get; set; }

    [Required]
    public string PrimarySuperPowerName 
    {
        get { return PrimarySuperPower.Name; }
        set { PrimarySuperPower.Name = value; }
    }

    public string SecondarySuperPowerName 
    {
        get { return SecondarySuperPower.Name; }
        set { SecondarySuperPower.Name = value; }
    }
}

And then I'd bind my View to the string properties and exclude the SuperPower properties. 然后我将我的View绑定到字符串属性并排除SuperPower属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM