[英]ASP.NET MVC 4, EF5, Unique property in model - best practice?
ASP.NET MVC 4,EF5,代码优先,SQL Server 2012 Express
在 model 中实施唯一值的最佳做法是什么? 我有一个地方 class,它有一个“url”属性,每个地方都应该是唯一的。
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,您能以某种方式告诉数据库在数据库中的字段上设置唯一约束吗?
什么是最简单的方法 - 什么是最佳实践?
尽管听起来很疯狂,但现在的最佳实践是不使用内置验证,而是使用FluentValidation 。 然后代码将非常易于阅读和超级可维护,因为验证将在单独的类上进行管理,这意味着更少的意大利面条式代码。
您要实现的目标的伪示例。
[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<Place>
{
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<string, Entity1> uniqueKey1 = ...; // assuming a unique string column
public Entity1 NewEntity1(string keyValue) {
if (uniqueKey1.ContainsKey(keyValue) throw new ArgumentException ... ;
return new Entity1 { MyUniqueKeyValue = keyValue };
}
}
参考:
脚注:
首先在代码中有很多对 [Unique] 的请求,但看起来它甚至没有制作第 6 版: http : //entityframework.codeplex.com/wikipage?title=Roadmap
您可以尝试在这里投票: http : //data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1050579-unique-constraint-ie-candidate-key-support
您可以在将数据保存到数据库表之前在代码级别执行此检查。
您可以尝试在您的视图模型上使用Remote
数据注释来进行异步验证,以使 UI 更具响应性。
public class CreatePlaceVM
{
[Required]
public string PlaceName { set;get;}
[Required]
[Remote("IsExist", "Place", ErrorMessage = "URL exist!")
public virtual string URL { get; set; }
}
请确保你有一个IsExists
在你的操作方法Placecontroller
它接受一个URL
paramtere并检查它againist你的表并返回true或false。
这个msdn 链接有一个示例程序来展示如何实现远程属性来进行即时验证。
此外,如果您正在使用存储过程(出于某种原因),您可以在INSERT
查询之前在那里进行EXISTS
检查。
我解决了在验证流程中启用构造函数注入的一般问题,集成到正常的 DataAnnotations 机制中,而无需在此答案中求助于框架,从而可以编写:
class MyModel
{
...
[Required, StringLength(42)]
[ValidatorService(typeof(MyDiDependentValidator), ErrorMessage = "It's simply unacceptable")]
public string MyProperty { get; set; }
....
}
public class MyDiDependentValidator : Validator<MyModel>
{
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));
在我的 ASP.NET Razor Page 项目中遇到了类似的问题。 创建自定义 UniqueDataAttribute 不起作用,因为在编辑时,如果您不更改唯一字段,则会引发错误。
我需要独特的书名。 我是这样解决的:
代码:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Book>()
.HasIndex(u => u.Name)
.IsUnique();
}
代码:
// Validate uniqueness of Name field in database.
// If validation is done on existing record, pass the id of the record.
// Else, if validating new record Name, then id is set to dummy key integer -1
public static bool UniqueNameInDb(this string data, ApplicationDbContext db, int id = -1)
{
var duplicateData = from o in db.Book
where o.Name == data && o.Id != id
select o;
if(duplicateData.Any())
{
return false;
}
return true;
}
}
创建模型:
public async Task<IActionResult> OnPost()
{
if(ModelState.IsValid)
{
if (!Book.Name.UniqueNameInDb(_db)) //<--Uniqueness validation
{
ModelState.AddModelError("Book.Name", "Name already exist"); //<-- Add error to the ModelState, that would be displayed in view.
return Page();
}
await _db.Book.AddAsync(Book);
await _db.SaveChangesAsync();
return RedirectToPage("Index");
}
else
{
return Page();
}
}
编辑模型:
public async Task<IActionResult> OnPost()
{
if(ModelState.IsValid)
{
var bookFromDb = await _db.Book.FindAsync(Book.Id);
if (!Book.Name.UniqueNameInDb(_db, Book.Id)) //<--Uniqueness validation
{
ModelState.AddModelError("Book.Name", "Name already exist"); //<-- Add error to the ModelState, that would be displayed in view.
return Page();
}
bookFromDb.Name = Book.Name;
bookFromDb.Author = Book.Author;
await _db.SaveChangesAsync();
return RedirectToPage("Index");
}
return Page();
}
PS:您的 Razor 视图应该在表单中设置模型验证以捕获和显示错误。
IE,
<div class="text-danger" asp-validation-summary="ModelOnly"></div>
和以下针对该领域的验证。
<span asp-validation-for="Book.Name" class="text-danger"></span>
好吧,这很简单,但不知道这是否有效。 在添加新用户之前检查电子邮件是否已经存在。
if (!db.Users.Any(x => x.Email == data.Email))
// your code for adding
else
// send a viewbag to the view
// ViewBag.error = "Email Already Exist";
实现多列Unique
modelBuilder.Entity<DataClass>()
.HasIndex(u => new
{
u.col1,
u.col2
})
.IsUnique();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.