13

ASP.NET MVC 4,EF5,代码第一次,SQL Server 2012点中快速ASP.NET MVC 4,EF5,模型中的独特属性 - 最佳实践?

什么是执行模型中的独特价值的最佳实践?我有一个地方类,它有一个'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可以以某种方式告诉数据库在数据库中的字段上设置唯一的约束?

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

回答

23

那样疯狂,因为它听起来最好的做法现在是使用内置的验证,而是使用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 
    } 
} 
+0

有趣。所以我只需要将它用于任何独特的属性 - 其他任何东西都可以保持不变,传统的Code First正确吗?这是否涵盖了所有的基础,竞争条件,数据库的100%可靠的唯一性等? – niico

+0

我是否也需要安装fluentvalidation nuget包? – niico

+0

这个例子将与POCO一起工作,没有任何问题,我在我的所有项目中都使用它。正如你所看到的,它会检查'TryValidateModel()'在数据库中的唯一性,之后你通常会调用'_db.SaveChanges();'所以基本上没有任何延迟。是的,当然你需要nuget包。 – sed

4

唯一的方法是在生成它之后更新您的迁移,假设您正在使用它们,以便对该列实施唯一性约束。

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

参考文献:

Footnot E:

有很多代码[独特]要求的第一,但它看起来像它甚至没有让版本6:http://entityframework.codeplex.com/wikipage?title=Roadmap

你可以尝试这里表决:http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1050579-unique-constraint-i-e-candidate-key-support

+0

我从来没有使用知识库或工厂 - 任何链接/教程的建议吗? 我认为我也可以手动验证新位置的用户输入以确保URL是唯一的?如果验证存在缺陷,可以在对象级别强制执行 - 例如在SQL Server中设置唯一约束(可能您的建议涵盖了这一点) 似乎可以有更好的解决方案,因为这是一个常见要求? – niico

+0

注意 - 这是一个相对较小规模的应用程序,现在有一个开发人员 - 所以一个简单/快速的解决方案会很好。我还需要为用户强制使用唯一的字段 - 比如在他们的Twitter句柄或电子邮件中。 – niico

+0

@ user2254951。通过提供的代码和链接编辑。您需要一个共同的地方来检查唯一性,因此我的单身人士建议,因为它将存储在您的应用程序域在IIS中的持续时间内,您只需在第一次查询时将其种子,但这很容易。 –

3

在将数据保存到数据库表之前,您可以在代码级别执行此操作。

您可以尝试在视图模型上使用Remote数据注释来执行异步验证,以使UI更具响应性。

public class CreatePlaceVM 
{ 
    [Required] 
    public string PlaceName { set;get;} 

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

确保你有你的PlacecontrollerIsExists操作方法,它接受一个URL paramtere并检查它againist你的表并返回true或false。

这个msdn link有一个示例程序来展示如何实现Remote属性来做即时验证。

此外,如果使用存储过程(出于某种原因),你可以在INSERT查询前做一个EXISTS检查那里。

+0

这看起来很酷 - 我必须执行其他任何事情才能使它工作吗? 如果另一个用户同时执行相同的查询会怎么样 - 是否有任何方法可以防止更深层次/数据库级别的重复? – niico

+0

在保存之前(使用上述方法(指示用户更改重复值)和第二次HttpPost操作方法执行两次操作(检查数据库以查看是否存在该项目)。没有任何问题,除非你有一些巨大的性能问题(我猜你没有这个问题) – Shyju

+0

是的,这样的检查应该在插入新URL的代码的事务中,以确保没有竞争条件。交易,是否存在?,插入,提交 – AaronLS

6

此链接可能帮助: 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; } 
} 
1

我解决了让你的验证流程构造器注入,整合到正常的DataAnnotations机制的普遍问题,而在this answer诉诸框架,使一个写:

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