silkfire
silkfire

Reputation: 25955

Unable to determine a valid ordering for dependent operations circular reference

I've been wasting two days now to try solve this problem but have yet to find a solution.

In my code that saves an entity with a relationship, I get this error when reaching ctx.SaveChanges():

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

Shipment.cs

[ForeignKey("ShipmentNumber")]
public int? DefaultShipmentNumber { get; set; }

public virtual ShipmentNumber ShipmentNumber { get; set; }

ShipmentNumber.cs

[Column("shipment_id")]
[ForeignKey("Shipment")]
public byte ShipmentId { get; set; }

public virtual Shipment Shipment { get; set; }

To avoid circular references, ShipmentNumber belonging to Shipment is nullable (optional), whereas ShipmentNumber's dependency on Shipment is required.

I first create a Shipment, add it and then attach a ShipmentNumber to it and add it to table as well.

Here's the fluent API code:

modelBuilder.Entity<Shipment>()
    .HasOptional<ShipmentNumber>((shipment) => shipment.ShipmentNumber)
    .WithMany();

Shipment has one "true" ShipmentNumber, but many ShipmentNumbers can link to the same Shipment, hence the WithMany() call (relation without a navigator property). In theory, both relations should always return one entity, but I know EF won't allow me a 1:1 relation here, so I'm using optional.

Here's the actual code:

shipment = tracker.Shipment;

ctx.Shipments.Add(shipment);
shipment.ShipmentNumber = new ShipmentNumber { Number = tracker.ShipmentNumber };

ctx.ShipmentNumbers.Add(shipment.ShipmentNumber);

ctx.SaveChanges();

If someone knows how to make it properly save the entity along with the relation, please do tell. I'm totally stuck at the moment.

Upvotes: 0

Views: 971

Answers (1)

Fabio
Fabio

Reputation: 11990

Well, I don't know why you want a 1:n relationship in database and a 1:0.1 relationship in the model.

Case 1

If you want to make a 1:1 relationship, you should declare your model as follows:

public class Shipment
{
    public int ShipmentId { get; set; }  

    //NO FK here

    public virtual ShipmentNumber ShipmentNumber { get; set; }
}

public class ShipmentNumber
{
    public int ShipmentId { get; set; } //ShipmentNumber PK is Also Shipment FK

    public virtual Shipment Shipment { get; set; }
}

Mapping:

modelBuilder.Entity<Shipment>()
    .HasKey(i => i.ShipmentId);

modelBuilder.Entity<ShipmentNumber>()
    .HasKey(i => i.ShipmentId);

modelBuilder.Entity<Shipment>()
    .HasRequired(i => i.ShipmentNumber)
    .WithRequiredPrincipal(i => i.Shipment)
    .WillCascadeOnDelete(false);

Generated Migration:

CreateTable(
    "dbo.Shipments",
    c => new
        {
            ShipmentId = c.Int(nullable: false, identity: true),
        })
    .PrimaryKey(t => t.ShipmentId);

CreateTable(
    "dbo.ShipmentNumbers",
    c => new
        {
            ShipmentId = c.Int(nullable: false),
        })
    .PrimaryKey(t => t.ShipmentId)
    .ForeignKey("dbo.Shipments", t => t.ShipmentId)
    .Index(t => t.ShipmentId);

Case 2

If you want to make a 1:n relationship:

public class Shipment
{
    public int ShipmentId { get; set; }

    public virtual ICollection<ShipmentNumber> ShipmentNumbers { get; set; }
}

public class ShipmentNumber
{
    public int ShipmentNumberId { get; set; }

    public int ShipmentId { get; set; }

    public virtual Shipment Shipment { get; set; }
}

Mapping:

modelBuilder.Entity<Shipment>()
    .HasKey(i => i.ShipmentId);

modelBuilder.Entity<ShipmentNumber>()
    .HasKey(i => i.ShipmentNumberId);

modelBuilder.Entity<Shipment>()
    .HasMany(i => i.ShipmentNumbers)
    .WithRequired(i => i.Shipment)
    .HasForeignKey(i => i.ShipmentId)
    .WillCascadeOnDelete(false);

Generated Migration:

CreateTable(
    "dbo.Shipments",
    c => new
        {
            ShipmentId = c.Int(nullable: false, identity: true),
        })
    .PrimaryKey(t => t.ShipmentId);

CreateTable(
    "dbo.ShipmentNumbers",
    c => new
        {
            ShipmentNumberId = c.Int(nullable: false, identity: true),
            ShipmentId = c.Int(nullable: false),
        })
    .PrimaryKey(t => t.ShipmentNumberId)
    .ForeignKey("dbo.Shipments", t => t.ShipmentId)
    .Index(t => t.ShipmentId);

Another problem is the code you are using to add items to database.

ctx.Shipments.Add(shipment);
shipment.ShipmentNumber = new ShipmentNumber { Number = tracker.ShipmentNumber };

//this line is not necessary
ctx.ShipmentNumbers.Add(shipment.ShipmentNumber);

ctx.SaveChanges();

When you add a new Shipment all dependant objects will be inserted to database, if necessary.

Upvotes: 2

Related Questions