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));