4

EF が PK/FK 関係で「循環参照」を許可しない理由を理解しています。次のシナリオを機能させるためにモデルを変更する方法についてのアドバイスを探しています。

シナリオ

3 つのエンティティ: EmployeeAgencyWorkRecord。その目的は、従業員が作業に費やした時間を記録することです。Employee次に、Agency彼/彼女が雇用されていることへの参照を含み、彼/彼女には、その仕事が行われたことWorkRecordへの参照が含まれています。Agency

public class Employee
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public int AgencyId { get; set; }
    public virtual Agency Agency { get; set; }

    public virtual IEnumerable<WorkRecord> WorkRecords { get; set; }
}

public class Agency
{
    [Key]
    public int Id { get; set; } 
}

public class WorkRecord
{
    [Key]
    public int Id { get; set; } 

    public int Hours { get; set; } 

    public int AgencyId { get; set; } 
    public virtual Agency Agency { get; set; } 

    public int EmployeeId { get; set; }
    public virtual Employee { get; set; }
}

このように、愚痴をこぼします:FK_dbo.WorkRecords_dbo.Employees_EmployeeId循環参照を引き起こします。

実験

私が最初に考えたのは、双方向の仮想プロパティが原因だったので、2 つのうちの 1 つを一方向の関係を持つ最上位エンティティに指定することにしました。

まず、WorkRecord最上位エンティティとして指定し、エンティティWorkRecordsから仮想参照参照を削除しEmployeeます...同じメッセージが生成されます。

次に、Employeeトップレベルのエンティティを作成し、その仮想コレクションを残して、エンティティから仮想参照プロパティWorkRecordsを削除しました...正常に動作しますが、私の目標は達成されません。EmployeeWorkRecord

さらに調査した結果、両方のエンティティのエージェンシー仮想参照プロパティが循環参照の原因であることがわかりました。1 つのエンティティがこれを削除すると、Employee/WorkRecordエンティティの関係が全方向に作用します。

質問:

ですから、私が尋ねることができるように明確に - WorkRecordEF5 を混乱させることなく、トップレベルのエンティティとして使用して、このビジネス モデルを表現するにはどうすればよいでしょうか?

4

2 に答える 2

2

単に EF を回避したいだけのように聞こえますが、実際には、データの結合における有効な問題を表していると思います。たとえば、AgencyId を WorkRecord と Employee の両方にバインドすると、WorkRecord で AgencyId を更新すると、Employee にカスケードされます。これは WorkRecord などにカスケードします。したがって、「循環参照」です。これらのデータ オブジェクトのどれがエージェンシーとの関係を「所有」するかを指定する必要があります。

個人的には、WorkRecord から Agency を参照するのが最も自然なバインディングだと思います。Employee があるエージェンシーから別のエージェンシーに移動する可能性があるシナリオを見ることができますが、WorkRecord があるエージェンシーから別のエージェンシーに移動するのははるかに困難です。また、WorkRecord を持たない Employee は、実際には Employee とは言えません。これが事実であると判断した場合は、Employee から Agency 参照を削除します。Employee から Agency にアクセスする必要がある場合は、とにかく WorkRecord を経由する必要があります。

ただし、これらはすべて概念的なものにすぎません。AgencyId が Employee で null になることを可能にすると、EF はもはや文句を言わなくなると思います (そして、両方でオプションにしたいかもしれません)。これにより、WorkRecord による循環更新を必要とせずに Employee を更新できるようになります。確認するためにそれをテストする必要がありますが、それが当てはまると思います。

public class Employee
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public int? AgencyId { get; set; }
    public virtual Agency Agency { get; set; }

    public virtual IEnumerable<WorkRecord> WorkRecords { get; set; }
}
于 2012-11-06T22:55:37.080 に答える
1

次のような、Entity Framework ではなく、SQL Server から例外が発生する可能性があります。

テーブル 'XYZ' に FOREIGN KEY 制約 'ABC' を導入すると、サイクルまたは複数のカスケード パスが発生する可能性があります。ON DELETE NO ACTION または ON UPDATE NO ACTION を指定するか、他の FOREIGN KEY 制約を変更します。

この例外は基本的に、問題を解決するために何をする必要があるかを示しています。「ON DELETE NO ACTION を指定する」とは、関係の少なくとも 1 つに対してカスケード削除を無効にすることを意味します。問題は、外部キー プロパティとが null 非許容であるため、 3 つの関係すべてが必要なことです。この場合、EF は削除を有効にしてデータベースにリレーションシップを作成します。を削除すると、結果は複数の削除パスになります。WorkRecords と Employees が削除されますが、Employees も Workrecords を削除するため、WorkRecords には 2 つの複数の削除パスがあります。AgencyIdEmployeeIdAgency

Fluent API でのみカスケード削除を無効にできます。

modelBuilder.Entity<Employee>()
    .HasRequired(e => e.Agency)
    .WithMany()
    .HasForeignKey(e => e.AgencyId);

modelBuilder.Entity<WorkRecord>()
    .HasRequired(w => w.Agency)
    .WithMany()
    .HasForeignKey(w => w.AgencyId)
    .WillCascadeOnDelete(false); // or for one or more of the other relationships

modelBuilder.Entity<WorkRecord>()
    .HasRequired(w => w.Employee)
    .WithMany(e => e.WorkRecords)
    .HasForeignKey(w => w.EmployeeId);

を削除するAgencyと、関連する従業員が削除され、削除された従業員によって関連する作業記録が削除されます。しかし、エージェンシーはもはやワークレコードを直接削除しないため、2 番目の削除パスは削除されます。

代わりに、関係の 1 つをオプションにすることもできます。これにより、慣習によりカスケード削除が自動的に無効になります (Jacob Proffitt の回答を参照)。

ところで:IEnumerable<T>ナビゲーション プロパティに を使用することはできませんICollection<T>。派生したインターフェイスまたは実装を使用する必要があります。

于 2012-11-06T23:04:51.683 に答える