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