views:

539

answers:

4

When I try to add a new item to my EntitySet, I'm getting this exception:

An entity with the same identity already exists in this EntitySet

However, when I inspect the EntitySet its count is 0.

Any ideas why I would be getting this error when the set is empty? How could an entity already exist in the set if the set has no items in it?

UPDATE

I've narrowed this down a bit more. This is only happening if I add the item to the set, remove it, then re-add it. Even tho the item isn't in the EntitySet any more it is still remembering it somehow. What do I need to do to make it forget?

UPDATE: Here are some code snippets for the classes and logic involved.

Server Entities:

public class PhotoDto
{
    [Key]
    [Editable(false)]
    public int Id { get; set; }

    /* clip */

    [Include]
    [Association("Photo_Destination", "Id", "PhotoId")]
    public EntitySet<PhotoDestinationDto> Destinations { get; set; }
}

public class PhotoDestinationDto : BaseDestionationDto
{
    [Key]
    [Editable(false, AllowInitialValue = true)]
    public int PhotoId { get; set; }

    [Key]
    [Editable(false, AllowInitialValue = true)]
    public bool IsAnnotated { get; set; }

    [Key]
    [Editable(false, AllowInitialValue = true)]
    public int DropZoneId { get; set; }
}

public class BaseDestinationDto
{
    [Key]
    [Editable(false, AllowInitialValue = true)]
    public Guid DatabaseUid { get; set; }

    [Key]
    [Editable(false, AllowInitialValue = true)]
    public string Unit { get; set; }

    [Key]
    [Editable(false, AllowInitialValue = true)]
    public string EqCircId { get; set; }

    [Key]
    [Editable(false, AllowInitialValue = true)]
    public string EqType { get; set; }
}

Client side GetIdentity() for PhotoDestinationDto:

/// <summary>
/// Computes a value from the key fields that uniquely identifies this entity instance.
/// </summary>
/// <returns>An object instance that uniquely identifies this entity instance.</returns>
public override object GetIdentity()
{
    if ((((this._eqCircId == null) 
                || (this._eqType == null)) 
                || (this._unit == null)))
    {
        return null;
    }
    return EntityKey.Create(this._dropZoneId, this._eqCircId, this._eqType, this._isAnnotated, this._photoId, this._unit, this._databaseUid);
}

To remove photo destination client side:

PhotoDto photo = GetPhotoDto();
PhotoDestinationDto destToRemove = photo.Destinations.First(x => x.DropZoneId == 1);
photo.Destinations.Remove(destToRemove);

To add photo destination client side:

var dest = new PhotoDestinationDto
{
    DropZoneId = zoneId,
    EqCircId = selectedEqCircId,
    EqType = selectedEqType,
    Unit = selectedUnit,
    PhotoId = id,
    DatabaseUid = selectedDatabaseId
};

p.Destinations.Add(dest); // this is where exception is thrown. p.Destinations.Count is 0 here.
A: 

Have you made sure you have a PK/identity properly defined in your db on that table, and it is reflected in your model? Try overriding OnError in your DomainService to get more info on the server-side exception. (You're getting this error on the client/Silverlight side, right?)

Bobby
I'm using my own DTO objects (not objects generated by EF or Linq2Sql). I do have [Key] properties defined in the DTO objects.I am getting the error client-side on the line of code that adds the new item to the EntitySet. (EntitySet.Add()).I'll try overriding OnError in the domain service to see what that tells me, however since the Add() method doesn't communicate with the server (until you call SaveChanges() which hasn't been called at this point) I'm not confident this will reveal anything.
Joshua Poehls
I overrode the OnError method in the domains service but it isn't being called for this error. Must all be client-side.
Joshua Poehls
A: 

Hmmm, I was unable to repro. Can you provide more details on the key members of your entity, as well as the codegenned GetIdentity implementation? Also, lets see the Add/Remove/Add code you're using.

Mathew
I've added examples of all the above.
Joshua Poehls
+1  A: 

Ok, after seeing your code I see the issue. When removing from an EntityCollection the entity isn't actually removed (as in a pending delete) - the association is only severed, with FKs set to their default values. So your photo.Destinations.Remove() call doesn't actually remove the entity from the DomainContext (ctxt.PhotoDestinationDtos EntitySet). That's why you're getting the cache collision.

To actually remove it, you should be calling ctxt.PhotoDestinationDtos.Remove.

Mathew
Hmm. That makes sense, however I don't have a ctx.PhotoDestinationDtos property. Best I can figure this is because I don't have any methods on my domain service to get a list of PhotoDestinations, they just come down when you get a list of Photos.
Joshua Poehls
A: 

Juding by your code Unit = selectedUnit and p.Destinations.Add(dest); and the other symptoms you describe you seem to have the exact same situation. My findings below have fixed the problem for me.

Interested also if you ended up with any duplicated entities in your database as a result of this. i was getting rows already previously saved magically turning to New and then becoming dupe records. Again this simple change below fixed it.


There is a race condition i discovered that can give this error if you create an entity and immediately assign foreign key relationships to it before adding the parent entity to its entity set.

Turns out the very act of setting the foreign key relationship actually adds the entity to its corresponding entity set - but sometimes causes a race condition, especially if your UI is loading and saving and clearing entity sets.

bad:

var todo = new TODO();
todo.Status = TODOContext.Statuses.Single(x=>x.Status == "Unknown");
todo.AssignedTo = TODOContext.Users.Single(x=>x.UserName == "JSMITH");
TODOContext.TODOs.Add(todo); // adding entity after setting FK

good:

var todo = new TODO();
TODOContext.TODOs.Add(todo); // add entity before setting FK
todo.Status = TODOContext.Statuses.Single(x=>x.Status == "Unknown");
todo.AssignedTo = TODOContext.Users.Single(x=>x.UserName == "JSMITH");
Simon_Weaver