views:

1222

answers:

2

I am attempting to add some entities that I have created. When I try and add the entity in question to the set (see code below) I get the following error:

"The object could not be added or attached because its EntityReference has an EntityKey property value that does not match the EntityKey for this object."

I can't tell what entitykey it's referring to though. Here is the code, there is probably a much better way to pull this off as well:

public Internship CreateInternship(Internship internshipToCreate)
{
    try
    {
        Contact contactToCreate = new Contact();

        contactToCreate.Fax = internshipToCreate.contacts.Fax;
        contactToCreate.Extension = internshipToCreate.contacts.Extension;
        contactToCreate.FirstName = internshipToCreate.contacts.FirstName;
        contactToCreate.MiddleName = internshipToCreate.contacts.MiddleName;
        contactToCreate.LastName = internshipToCreate.contacts.LastName;
        contactToCreate.PhoneNumber = internshipToCreate.contacts.PhoneNumber;
        contactToCreate.StreetAddress = internshipToCreate.contacts.StreetAddress;
        contactToCreate.PostalCode = internshipToCreate.contacts.PostalCode;
        contactToCreate.ContactEmail = internshipToCreate.contacts.ContactEmail;

        contactToCreate.statesReference.EntityKey =
                    new EntityKey("InternshipEntities.StateSet", "ID", internshipToCreate.contacts.states.ID);
        contactToCreate.countriesReference.EntityKey =
                    new EntityKey("InternshipEntities.CountrySet", "ID", internshipToCreate.contacts.countries.ID);

        _internshipEntities.AddToContactSet(contactToCreate);
        _internshipEntities.SaveChanges();

        try
        {
            Availability availabilityToCreate = new Availability();

            availabilityToCreate.StartDate = internshipToCreate.availability.StartDate;
            availabilityToCreate.EndDate = internshipToCreate.availability.EndDate;
            availabilityToCreate.Negotiable = internshipToCreate.availability.Negotiable;

            _internshipEntities.AddToAvailabilitySet(availabilityToCreate);
            _internshipEntities.SaveChanges();

            try
            {
                internshipToCreate.contactsReference.EntityKey =
                    new EntityKey("InternshipEntities.ContactSet", "ID", contactToCreate.ID);
                internshipToCreate.availabilityReference.EntityKey =
                    new EntityKey("InternshipEntities.AvailabilitySet", "ID", availabilityToCreate.ID);
                internshipToCreate.classificationsReference.EntityKey =
                    new EntityKey("InternshipEntities.ClassificationSet", "ID", internshipToCreate.classifications.ID);
                internshipToCreate.educationReference.EntityKey =
                    new EntityKey("InternshipEntities.EducationSet", "ID", internshipToCreate.education.ID);

                _internshipEntities.AddToInternshipSet(internshipToCreate); //exception here
                _internshipEntities.SaveChanges();

                return internshipToCreate;
            }
            catch(Exception e)
            {
                throw e; 
            }
        }
        catch(Exception e)
        {
            throw e;
        }
    }
    catch(Exception e)
    {
        throw e;
    }

}

There is no other information given besides the error when I trace through so I'm not even sure which Key is the issue.

EDIT: Here is the version that ended up working:

using (TransactionScope scope = new TransactionScope())
{
    try
    {
        Contact contactToCreate = new Contact();
        Availability availabilityToCreate = new Availability();
        Internship i = new Internship();

        // Set the contact entity values;

        contactToCreate.Fax = internshipToCreate.contacts.Fax;
        //...
        //ommited for brevity
        //...
        contactToCreate.ContactEmail = internshipToCreate.contacts.ContactEmail;

        // Set the contact entity references to existing tables

        contactToCreate.statesReference.EntityKey =
                    new EntityKey("InternshipEntities.StateSet", "ID", internshipToCreate.contacts.states.ID);
        contactToCreate.countriesReference.EntityKey =
                    new EntityKey("InternshipEntities.CountrySet", "ID", internshipToCreate.contacts.countries.ID);

        // Add contact

        _internshipEntities.AddToContactSet(contactToCreate);

        // Set the availability entity values;

        availabilityToCreate.StartDate = internshipToCreate.availability.StartDate;
        availabilityToCreate.EndDate = internshipToCreate.availability.EndDate;
        availabilityToCreate.Negotiable = internshipToCreate.availability.Negotiable;

        // Add availability

        _internshipEntities.AddToAvailabilitySet(availabilityToCreate);

        //Add contact and availability entities to new internship entity

        i.contacts = contactToCreate;
        i.availability = availabilityToCreate;

        // Set internship entity values;

        i.UserID = internshipToCreate.UserID;
        //...
        //ommited for brevity
        //...
        i.Created = DateTime.Now;

        // Set the internship entity references to existing tables

        i.classificationsReference.EntityKey =
            new EntityKey("InternshipEntities.ClassificationSet", "ID", internshipToCreate.classifications.ID);
        i.educationReference.EntityKey =
            new EntityKey("InternshipEntities.EducationSet", "ID", internshipToCreate.education.ID);

        // Add internship and save

        _internshipEntities.AddToInternshipSet(i);
        _internshipEntities.SaveChanges();

        //commit transaction
        scope.Complete();

        return internshipToCreate;

    }
    catch (Exception e)
    {
        throw e;
    }
}
A: 

This code isn't making a lot of sense to me. In two cases, you're going through an EntityKey when you could just assign an object reference. I.e, change this:

internshipToCreate.contactsReference.EntityKey =
    new EntityKey("InternshipEntities.ContactSet", "ID", contactToCreate.ID);
internshipToCreate.availabilityReference.EntityKey =
    new EntityKey("InternshipEntities.AvailabilitySet", "ID", availabilityToCreate.ID);

...to:

internshipToCreate.contacts = contactToCreate;
internshipToCreate.availability = availabilityToCreate;

In the other two cases you seem to be attempting to assign the ID of the object which is already there. These two lines, even if successful, it seems to me, would do nothing:

internshipToCreate.classificationsReference.EntityKey =
    new EntityKey("InternshipEntities.ClassificationSet", "ID", internshipToCreate.classifications.ID);
internshipToCreate.educationReference.EntityKey =
    new EntityKey("InternshipEntities.EducationSet", "ID", internshipToCreate.education.ID);

So you can just get rid of them.

What happens when you make these two changes?

Craig Stuntz
Thanks for the response Craig. It's clear now that I don't need to set those keys manually when I'm creating entities.I mad the changes you posted and I now receive: a generic:"An error occurred while updating the entries. See the InnerException for details."The stack trace shows (see next comment):
Graham
at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache) at System.Data.Objects.ObjectContext.SaveChanges(Boolean acceptChangesDuringSave) at System.Data.Objects.ObjectContext.SaveChanges() at Internship.Models.EntityInternshipRepository.CreateInternship(Internship internshipToCreate) in \Projects\Internship\InternshipTest\Models\EntityInternshipRepository.cs:line 331
Graham
What does the InnerException say?
Craig Stuntz
On my way home now, I will check tomorrow when I get back to work. Thanks for your help so far.
Graham
The exception I was getting was that a column on the classifications table was null, implying I was trying to create a new classification, which was not what I thought I was doing. The classifications table is a look up table, I just want to set the foreignkey/entitykey of the internship entity to one of them, but it looks like I'm trying to create one instead. Does that make sense?
Graham
Yes, that makes sense, but without seeing the code which actually assigns the classification it's hard to say for certain why. My wild guess is that the classification you are assigning is not part of the ObjectContext which contains the new Internship. When you add an object to a context (even implicitly, by assigning it to a refernce on another object you add to the context), that will always become an insert. Make sure you select the classification from the same context to which you will add the internship (or just assign the EntityKey).
Craig Stuntz
That seems to be exactly what was happening. The classification values were getting assigned back in the controller when the form was processed, but this was adding a new classification object to the object context when I just wanted to assign an entity key value. I posted my revised version above, it might still not be the best solution, maybe you can see a way to simplify it since I basically make a new internship object and add that. Thank you for your help, not only here but your responses in other threads have been invaluable while I've been learning this.
Graham
Also I see you also live in Columbus too, I'll buy you a beer sometime.
Graham
Glad to be of help. My take on ObjectContexts is that they're a bit like transactions or DB connections: You create one for the whole scope of whatever you're doing and don't mix two of them together. You also split your methods into two categories: The single method which makes/disposes the OC, and everything else, which it calls, and which does not make a new OC, but just assumes that anything they're passed is already in a context. Multiple OCs concurrently nearly always cause confusion. [And, FWIW, I never turn down a drink offer. :) ]
Craig Stuntz
+2  A: 

Hallo,

although I'm not sure what the problem is I have a suggestion. The Internship object that you are passing into method (internshipToCreate) is used to transfer values to other entities (Contact, Availability) that you instantiated inside of the method, and their creation works just fine.

Maybe you should try to do the same with Internship? Create new Internship object and set all values you have by taking them from internshipToCreate object, and than that newly created object pass to the _internshipEntities.AddToInternshipSet method.

It is possible that you've set some values on internshipToCreate object that you needed for other purposes, and that some of those is actually causing the exception.

And, I don't know what you business logic is, but it would be better if you put all under one transaction, because like this it may happen that first two entities are created, and third one not.

Misha N.
I'm going to try this now, thanks for the suggestions.
Graham
Thanks Misha, I used both your suggestion and Craig's to make it work. Cheers!
Graham