使用Entity Framework数据模型添加验证属性

24

前言 2015年2月 如果您仍在使用Entity Framework EDMX,请自己一个人检查一下,改用Entity Framework Code First。区别在于您的表是从模型类创建的,而不是在EDMX中创建模型类和表。这是一个更容易解决问题的方案,而且这个问题甚至不存在!

使用MVC 5开始使用Entity Framework 6 Code First

我有一个现有的SQL数据库,并且我正在使用ADO.NET Entity Data Model作为模型。我正在尝试将一些CRUD功能构建到我的MVC应用程序中。

在所有我找到的教程中,他们都是从头开始构建模型并向模型类添加属性。例如:

    [Required]
    [StringLength(10)]
    public string Name { get; set; }

但是,模型类是自动生成的,因此我认为更改它们是不好的想法(而且如果刷新数据库模型,它们将被覆盖)。

我该如何添加验证属性?


1
你应该将数据注释放在viewModel属性上。 - Forty-Two
现在正在阅读相关内容,谢谢。 - Mason240
这里展示了如何实现它:https://dev59.com/3WQn5IYBdhLWcg3wj3zz#16737247 - Frank Myat Thu
5个回答

36

您可以创建一个部分类,与 EF 生成的类分开,用来存储元数据。

//Contact.cs - The original auto-generated file 
[System.ComponentModel.DataAnnotations.MetadataType(typeof(ContactMetadata))]
public partial class Contact
{
    public int ContactID { get; set; }
    public string ContactName { get; set; }
    public string ContactCell { get; set; }
}

//ContactMetadata.cs - New, seperate class

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
internal sealed class ContactMetadata
{
    [Required(ErrorMessage = "Name is required.")]
    [StringLength(5)]  
    public string ContactName;
}

14
实际上,您不应尝试修改原始的自动生成文件,只需创建另一个文件,因为“partial”关键字将使其完美运行。 - Simon Wang
1
编译器会产生警告,指出ContactName“从未被分配,并且始终具有其默认值null”;像这样默认属性是安全的吗? - Brad
4
它显示“此成员定义超过一次” :( - SteveCav
1
我注意到,一旦你为EF模型创建了一个元数据类(例如添加额外的验证),似乎就会失去EF模型已经提供的任何现有验证。元数据是否作为属性的替代而不是扩展现有属性集合的方式来起作用? - iCollect.it Ltd
3
@SteveCav 我也遇到了这个错误。你可能已经解决了,但如果没有,你需要将元数据类与具有相同命名空间的模型类放在同一个程序集中。 - John Verco
显示剩余2条评论

19

Mason240的答案很好,我将尝试改进它:您可以创建一个新的ContactDataAnnotations.cs类,其中包含:

//ContactDataAnnotations.cs - A new file 
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

[MetadataType(typeof(ContactMetadata))]
public partial class Contact
{
    // No field here
}

internal sealed class ContactMetadata
{
    [Required(ErrorMessage = "Name is required.")]
    [StringLength(5)]  
    public string ContactName {get; set; }
}

这样,您就可以通过EF重新生成Contact类,而无需触及DataAnnotations, 而且顺便说一句,也不会有警告。


4

这个问题已经得到正确回答,但我想补充一点的是,我总觉得将元数据嵌套在一起更加清晰易懂,仅代表个人看法。

[MetadataType(typeof(ProductDescription.Metadata))]
public partial class ProductDescription
{
    sealed class Metadata
    {
        [Key]
        public long id { get; set; }
        [Display(Name = "Title")]
        public string title { get; set; }
        // ...
    }
}

我还注意到将元数据保留为类的私有属性还有一个额外的好处。该属性仅适用于正确的类,防止在复制类(创建相似类)时可能出现的错误。如果重命名复制的类时忘记在属性中更改类名,则可能会出现错误。


4

我知道这个问题已经被标记为已回答,但我想澄清一些事情。

@SteveCav说:“此成员被定义了多次”。我也遇到了完全相同的错误。花了几个小时来尝试解决它。

最终纠正它的方法是,在同一个程序集中创建一个单独的文件类(我认为这已经在这里提到了)。但我想强调的是,这个类应该嵌套在表示内部类的部分类中。

然后,你可以用注释类来装饰这个内部类。就像这样:

//ContactMap.cs - Present in the same namespace as Contact.cs
[System.ComponentModel.DataAnnotations.MetadataType(typeof(ContactMap))]
partial class Contact // Present in the ContactMap class. This represent the Inner Class
{
}

//ContactMap.cs - This represent the outer class

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
public class ContactMetadata
{
    [Required(ErrorMessage = "Name is required.")]
    [StringLength(5)]  
    public string ContactName;
}

希望这样更清晰易懂。

3

这里有一个变化的建议答案,允许您在不同的程序集和命名空间中使用类。我尚未真正使用EF进行测试,但我正在为Swagger codegen API模型类使用此功能。

简而言之:从模型类继承并添加元数据到继承类。另一个好处是,使用Swagger codegen,您可以直接使用API模型,无需映射,并且对于初始表单,您可以使用受保护的默认构造函数。

[MetadataType(typeof(LocalAssemblyModelMetadata))]
public class LocalAssemblyModel : IO.Swagger.Model.OtherAssemblyModel 
{
    public LocalAssemblyModel() : base ()     { }
}



public sealed class LocalAssemblyModelMetadata
{
    [Required(ErrorMessage = "BaseclassProperty is mandatory.")]
    public string BaseclassProperty { get; set; }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接