ajbeaven
ajbeaven

Reputation: 9562

How to resolve this circular dependency in EF

In my application, an Agency has many Employees, one of which is delegated the owner of said agency:

public class Employee
{
    public int EmployeeId { get; set; }
    public string EmployeeName { get; set; }
    public int EmployerId { get; set; }

    [ForeignKey("EmployerId")]
    public virtual Agency Employer { get; set; }
}

public class Agency
{
    public int AgencyId { get; set; }
    public string AgencyName { get; set; }
    public int OwnerId { get; set; }

    [ForeignKey("OwnerId")]
    public virtual Employee Owner { get; set; }

    [InverseProperty("Employer")]
    public virtual ICollection<Employee> Employees { get; set; }
}

I attempt to enter a new Agency into the datebase using this code:

var agency = new Agency();
// ... 

context.Agencies.Add(agency);

var owner = new Employee();
// ...

context.Employees.Add(owner);

owner.Employer = agency;
agency.Owner = owner;

context.SaveChanges();

When I call SaveChanges, I receive the following error which I assume is due to the circular dependency I described above:

Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.

Is there a way in EF to specify the order for the "dependent operation"? Alternatively, is there a better way to write my database such that it gets around this problem but still models the data structure I want?

Upvotes: 2

Views: 3119

Answers (1)

Slauma
Slauma

Reputation: 177133

I'm not sure if it would be possible at all to create agency and owner in SQL because to store the agency you need a valid FK to the owner and to store the owner you need a valid FK to the agency. Due to the FK constraints (unless they wouldn't be enforced) you can't store any of these without violating a constraint.

A solution (and I don't know another one) is making one of the relationships optional, for example the Owner by defining the OwnerId as nullable:

public int? OwnerId { get; set; }

This won't solve the "valid ordering" exception immediately but now you can store an agency without owner and then store the owner with the relationship to that already stored agency. To make the whole operation "atomic" you can wrap the two necessary calls to SaveChanges into an outer transaction:

using (var tx = new TransactionScope())
{
    var agency = new Agency();

    context.Agencies.Add(agency);

    context.SaveChanges(); // inner transaction 1
    // saves agency with OwnerId = NULL, it wouldn't work with a required owner
    // agency has a primary key from the database now

    var owner = new Employee();

    context.Employees.Add(owner);

    owner.Employer = agency; // sets FK EmployerId to the now known PK of agency
    agency.Owner = owner;

    context.SaveChanges(); // inner transaction 2

    tx.Complete(); // commits the outer transaction
}

Upvotes: 3

Related Questions