繁体   English   中英

ASP.NET MVC 4,EF5,model 中的独特属性 - 最佳实践?

[英]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 不起作用,因为在编辑时,如果您不更改唯一字段,则会引发错误。

我需要独特的书名。 我是这样解决的:

  1. 我通过 EF Core 迁移向数据库中的字段添加了唯一约束。 在 ApplicationDbContext 类中添加以下内容,然后运行迁移。

代码:

protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<Book>()
                .HasIndex(u => u.Name)
                .IsUnique();
        }
  1. 接下来,创建助手/扩展方法如下。

代码:

        // 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;
        }
    }
  1. 然后在 OnPost() 方法中的创建和编辑页面模型中使用它,如下所示。

创建模型:

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.

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