views:

379

answers:

1

I have an EDMX containing Sentences, and Words, say and a Sentence contains three Words, say. Appropriate FK relationships exist between the tables.

I create some words: Word word1 = new Word(); Word word2 = ...

I build a Sentence: Sentence x = new Sentence (word1, word2, word3);

I build another Sentence: Sentence y = new Sentence (word1, word4, word5);

I try to save x to the database, but EF builds a change set that includes everything, including y, word4 and word5 that aren't ready to save to the database. When SaveChanges() happens it throws an exception: Unable to determine the principal end of the ... relationship. Multiple added entities may have the same primary key.

I think it does this because Word has an EntityCollection<Sentence> on it from the FK relationship between the two tables, and thus Sentence y is inextricably linked to Sentence x through word1.

So I remove the Navigation Property Sentences from Word and try again. It still tries to put the entire graph into the change set.

What suggestions do the Entity Framework experts have for ways to break this connection. Essentially what I want is a one-way mapping from Sentence to Word; I don't want an EntityCollection<Sentence> on Word and I don't want the object graph to get intertwined like this.

Code sample: This puts two sentences into the database because Verb1 links them and EF explores the entire graph of existing objects and added objects when you do Add/SaveChanges.

    Word subject1 = new Word(){ Text = "Subject1"};
    Word subject2 = new Word(){ Text = "Subject2"};
    Word verb1 = new Word(){ Text = "Verb11"};
    Word object1 = new Word(){ Text = "Object1"};
    Word object2 = new Word(){ Text = "Object2"};


    Sentence s1 = new Sentence(){Subject = subject1, Verb=verb1, Object=object1};
    Sentence s2 = new Sentence(){Subject=subject2, Verb=verb1, Object=object2};

    context.AddToSentences(s1);
    context.SaveChanges();

    foreach (var s in context.Sentences)
    {
        Console.WriteLine(s.Subject + " " + s.Verb + " " + s.Object);
    }
+1  A: 

One alternative that does seem to work is to rename and hide the database generated properties (e.g. SubjectP), maintain a separate private copy of the Word that is set, and then fix it up during save:

in Sentence:

    private Word subject;
    ...

    public Word Subject { get {return this.subject ?? this.SubjectP;} set {this.subject = value; }}

    public void FixSelfUp()
    {
        if (this.subject != null && subject != this.SubjectP) this.SubjectP = this.subject;
    ...

And in SaveChanges() ...

    var items = this.context.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added | System.Data.EntityState.Modified);
    // now iterate over them, find any Sentences and call to fix them up.

This makes the Sentence behave just like it was intended to behave, allows setting and getting of each Word on it and doesn't allow EF to join them all together into one big graph.

But surely there's a better way than this!

Hightechrider