ASP.NET MVC 4,EF5,模型中的独特属性 – 最佳实践?

ASP.NET MVC 4,EF5, Code First ,SQL Server 2012 Express

在模型中强制使用唯一值的最佳做法是什么? 我有一个具有’url’属性的places类,对于每个地方都应该是唯一的。

public class Place { [ScaffoldColumn(false)] public virtual int PlaceID { get; set; } [DisplayName("Date Added")] public virtual DateTime DateAdded { get; set; } [Required(ErrorMessage = "Place Name is required")] [StringLength(100)] public virtual string Name { get; set; } public virtual string URL { get; set; } }; 

为什么不存在可以放置的[Unique]数据注释?

我已经看过1或2次讨论,但没有谈论最佳实践。 使用Code First可以以某种方式告诉数据库在数据库中的字段上设置唯一约束吗?

什么是最简单的方法 – 什么是最佳实践?

尽管听起来很疯狂,但现在最好的做法是使用内置validation,而是使用FluentValidation 。 然后代码将非常易于阅读和超级可维护,因为validation将在单独的类上进行管理,这意味着更少的意大利面条代码。

您正在尝试实现的伪示例。

 [Validator(typeof(PlaceValidator))] class Place { public int Id { get; set; } public DateTime DateAdded { get; set; } public string Name { get; set; } public string Url { get; set; } } public class PlaceValidator : AbstractValidator { public PlaceValidator() { RuleFor(x => x.Name).NotEmpty().WithMessage("Place Name is required").Length(0, 100); RuleFor(x => x.Url).Must(BeUniqueUrl).WithMessage("Url already exists"); } private bool BeUniqueUrl(string url) { return new DataContext().Places.FirstOrDefault(x => x.Url == url) == null } } 

此链接可能有所帮助: https : //github.com/fatihBulbul/UniqueAttribute

 [Table("TestModels")] public class TestModel { [Key] public int Id { get; set; } [Display(Name = "Some", Description = "desc")] [Unique(ErrorMessage = "This already exist !!")] public string SomeThing { get; set; } } 

唯一的方法是在生成迁移后更新迁移(假设您正在使用它们),以便它对列强制执行唯一约束。

 public override void Up() { // create table CreateTable("dbo.MyTable", ...; Sql("ALTER TABLE MyTable ADD CONSTRAINT U_MyUniqueColumn UNIQUE(MyUniqueColumn)"); } public override void Down() { Sql("ALTER TABLE MyTable DROP CONSTRAINT U_MyUniqueColumn"); } 

但是,硬件位是在到达数据库之前在代码级强制执行约束。 为此,您可能需要一个包含唯一值的完整列表的存储库,并确保新实体不会通过工厂方法违反该实体。

 // Repository for illustration only public class Repo { SortedList uniqueKey1 = ...; // assuming a unique string column public Entity1 NewEntity1(string keyValue) { if (uniqueKey1.ContainsKey(keyValue) throw new ArgumentException ... ; return new Entity1 { MyUniqueKeyValue = keyValue }; } } 

参考文献:

  • 存储库 – Fowler ( 存储库的原始来源)
  • Repostory – MSDN
  • 教程:MVC中的存储库 (www.asp.net)
  • 单身在C# – SO

脚注:

代码中有很多对[Unique]的请求,但它看起来甚至没有版本6: http : //entityframework.codeplex.com/wikipage?title = Routemap

你可以尝试在这里投票: http : //data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1050579-unique-constraint-ie-candidate-key-support

在将数据保存到数据库表之前,您可以在代码级别进行此检查。

您可以尝试在viewmodel上使用Remote数据注释进行异步validation,以使UI更具响应性。

 public class CreatePlaceVM { [Required] public string PlaceName { set;get;} [Required] [Remote("IsExist", "Place", ErrorMessage = "URL exist!") public virtual string URL { get; set; } } 

确保在IsExists有一个IsExists操作方法,该方法接受URL参数并再次检查它并返回true或false。

此msdn链接有一个示例程序,用于显示如何实现Remote属性以进行即时validation。

此外,如果您使用的是存储过程( 由于某种原因 ),您可以在INSERT查询之前执行EXISTS检查。

我解决了在validation流程中启用构造函数注入的一般问题,将其集成到正常的DataAnnotations机制中,而不需要在此答案中使用框架,从而可以编写:

 class MyModel { ... [Required, StringLength(42)] [ValidatorService(typeof(MyDiDependentValidator), ErrorMessage = "It's simply unacceptable")] public string MyProperty { get; set; } .... } public class MyDiDependentValidator : Validator { readonly IUnitOfWork _iLoveWrappingStuff; public MyDiDependentValidator(IUnitOfWork iLoveWrappingStuff) { _iLoveWrappingStuff = iLoveWrappingStuff; } protected override bool IsValid(MyModel instance, object value) { var attempted = (string)value; return _iLoveWrappingStuff.SaysCanHazCheez(instance, attempted); } } 

有了一些帮助类(看看那里),你可以在例如ASP.NET MVC中连接它,就像在Global.asax : –

 DataAnnotationsModelValidatorProvider.RegisterAdapterFactory( typeof(ValidatorServiceAttribute), (metadata, context, attribute) => new DataAnnotationsModelValidatorEx(metadata, context, attribute, true));