9

私はC#で最初にEntity Framework Codeを使用していますが、追跡に使用されるのと同じ列を持つエンティティが多数あります。私が使用している列は、Active、IsDeleted、CreatedBy、ModifiedBy、DateCreated、およびDateUpdatedです。追跡したいすべてのエンティティにこれらのプロパティを追加するのは面倒なようです。以下のような、エンティティが継承できる基本クラスが必要です。

public abstract class TrackableEntity
{
    public bool Active { get; set; }
    public bool IsDeleted { get; set; }
    public virtual User CreatedBy { get; set; }
    public virtual User ModifiedBy { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateModified { get; set; }
}

次に、エンティティのこのクラスから継承してこれらのプロパティを取得し、データベースが生成されると、エンティティごとにこれらの列が作成されます。

public class UserProfile : TrackableEntity, IValidatableObject
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }

    public bool IsValid { get { return this.Validate(null).Count() == 0; } }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (String.IsNullOrEmpty(FirstName))
            yield return new ValidationResult("First name cannot be blank", new[] { "Username" });

        if (String.IsNullOrEmpty(LastName))
            yield return new ValidationResult("Last cannot be blank", new[] { "Password" });

        //Finish Validation Rules
    }
}

私は本当にコードの重複を減らし、このようなある種のメソッドを使用したいのですが、これを機能させることができません。私は以下のエラーを受け取り続けます:

タイプ'Namespace.Models.User'と'Namespace.Models.User'の間の関連付けの主な終わりを判別できません。この関連付けの主な目的は、リレーションシップフルーエントAPIまたはデータアノテーションのいずれかを使用して明示的に構成する必要があります。

私は数日間探し回っていますが、やりたいことの答えが見つかりません。すべてがリンクする別のテーブルを作成したくありません。TPT、TPH、TPCについて読みました。TPCは私が望むものにかなり近いように見えましたが、ほとんどすべてのテーブルでPKを共有することは、私が絶対にやりたくないことです。

これが私のテーブルをどのように見せたいかの例です。これらのテーブルは、TrackableEntityから継承するエンティティによって作成されます。

[UserProfile]
    [Id] [int] IDENTITY(1,1) NOT NULL
    [FirstName] [varchar](50) NOT NULL
    [LastName] [varchar](50) NOT NULL
    [Email] [varchar](255) NOT NULL
    [Phone] [varchar](20) NOT NULL
    [CreatedBy] [int] NOT NULL
    [ModifiedBy] [int] NOT NULL
    [DateCreated] [datetime] NOT NULL
    [DateModified] [datetime] NOT NULL
    [Active] [bit] NOT NULL
    [IsDeleted] [bit] NOT NULL

[CaseType]
    [Id] [int] IDENTITY(1,1) NOT NULL
    [Name] [varchar](50) NOT NULL
    [CreatedBy] [int] NOT NULL
    [ModifiedBy] [int] NOT NULL
    [DateCreated] [datetime] NOT NULL
    [DateModified] [datetime] NOT NULL
    [Active] [bit] NOT NULL
    [IsDeleted] [bit] NOT NULL
4

1 に答える 1

10

Userクラスも派生しているため、この例外が発生している可能性がありますTrackableEntity

public class User : TrackableEntity

その結果、Userエンティティには2つの継承されたプロパティが含まれるようになります

public virtual User CreatedBy { get; set; }
public virtual User ModifiedBy { get; set; }

Entity Frameworkは、慣例により、2つのプロパティ間の関係、つまり、これら2つのナビゲーションプロパティを両端として持つ1つの関係を想定しています。ナビゲーションプロパティは参照であり、コレクションではないため、EFは1対1の関係を推測します。モデルでは、2つのナビゲーションプロパティのどちらが関係に関連する外部キーを持っているかが指定されていないため、EFは、関係のプリンシパルと依存関係を判別できません。これにより、例外が発生します。

現在、この問題は、例外が示すように、プリンシパルとディペンデントを明示的に定義することで解決できます。しかし、あなたのモデルでは、慣習、つまり1対1の関係を想定することは正しくありません。実際には、 2つの関係が必要です。1つはCreatedBy(独自の外部キーを使用して)から、もう1つはModifiedBy(別の外部キーを使用して)からです。両方の関係は1対多でありUser他の多くのユーザーの作成者または変更者になる可能性があるため)、関係のもう一方の端にナビゲーションコレクションがありません。規則をオーバーライドし、これら2つの1対多の関係を定義するには、FluentAPIを使用してマッピングを提供する必要があります。

public class UnicornsContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<UserProfile> UserProfiles { get; set; }
    public DbSet<CaseType> CaseTypes { get; set; }
    // ... etc.

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .HasOptional(u => u.CreatedBy)
            .WithMany()
            .Map(m => m.MapKey("CreatedBy")); // FK column name in DB table

        modelBuilder.Entity<User>()
            .HasOptional(u => u.ModifiedBy)
            .WithMany()
            .Map(m => m.MapKey("ModifiedBy")); // FK column name in DB table
    }
}

HasOptional代わりに使用したことに注意してくださいHasRequired(つまり、データベース内のFKはnull許容になります)。これは、少なくとも自動インクリメントされたIDでは、CreatedByまたはModifiedByが設定された最初のユーザーを作成できないためです。それ自体ですが、最初のPKがまだ作成されていないため、使用する有効なFK値はありません。

CreatedBy(監査と追跡の目的では、一意の個人のIDを保存するだけで十分な場合があるため、関係と参照の制約を回避し、代わりにユーザーの名前のみを作成者と修飾子として保存することも検討できModifiedByます。本当にナビゲートする必要がありますか?すべてのエンティティからその作成者と修飾子に?例外的な場合に必要な場合でも、手動でユーザーテーブルに参加できます。名前が残っている限り、ユーザーがユーザーテーブルから削除されると問題になります。またはに保存されますCreatedByModifiedBy?)

DbSet抽象基本クラスにを導入しない限り、継承マッピングを処理する必要はありません...

public DbSet<TrackableEntity> TrackableEntities { get; set; } // NO!

...またはそのクラスのFluentAPIでのマッピング...

modelBuilder.Entity<TrackableEntity>()... // NO!

...または、クラスを他のクラスのナビゲーションプロパティとして使用します。

public class SomeEntity
{
    //...
    public virtual TrackableEntity Something { get; set; } // NO!
    public virtual ICollection<TrackableEntity> Somethings { get; set; } // NO!
}

これらのいずれかを使用すると、EFはTrackableEntityエンティティとして推論し、モデルテーブルとデータベーステーブル(デフォルトではTPH)の間に継承マッピングを導入します。それ以外の場合TrackableEntityは単なる基本クラスであり、基本クラス内のすべてのプロパティは、派生エンティティのプロパティであるかのように見なされ、データベーステーブルにマッピングされます。

于 2012-10-31T22:42:15.797 に答える