web-dev-qa-db-ja.com

ASP.NET MVC 4、EF5、モデル内の一意のプロパティ-ベストプラクティス?

ASP.NET MVC 4、EF5、コードファースト、SQL Server 2012 Express

モデルに一意の値を適用するためのベストプラクティスは何ですか?すべての場所で一意である必要がある「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; }
};

配置できる[一意の]データ注釈だけがないのはなぜですか?

私はこれについて1つか2つの議論を見てきましたが、ベストプラクティスの話はありません。 Code Firstを使用して、データベースのフィールドに一意の制約を設定するようにデータベースに指示できますか?

最も簡単な方法は何ですか?そしてベストプラクティスは何ですか?

17
niico

今日のベストプラクティスに聞こえるかもしれませんが、クレイジーなのは、組み込み検証をしない代わりに 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
    }
}
28
Stan

このリンクは役立つかもしれません: 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; }
}
7
Fatih Bülbül

唯一の方法は、それを使用していると想定して、生成したマイグレーションを更新して、列に一意の制約を適用することです。

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

5
Andy Brown

データをデータベーステーブルに保存する前に、コードレベルでこのチェックを行うことができます。

ビューモデルで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パラメータに対応するPlacecontrollerURLアクションメソッドがあることを確認し、テーブルで再度確認して、trueまたはfalseを返します。

この msdn link には、Remote属性を実装して即時検証を行う方法を示すサンプルプログラムがあります。

また、ストアドプロシージャを使用している場合(何らかの理由で)、EXISTSを実行できますINSERTクエリの前に確認してください。

3
Shyju

検証フローでコンストラクター注入を有効にするという一般的な問題を解決し、フレームワークに頼ることなく通常の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));
2
Ruben Bartelink