5

In an n-tier application linq-to-sql doesn't seem to have a clear cut solution for updating a disconnected entity that has child EntitySets.

I have some linq-to-sql entities...

public partial class Location : INotifyPropertyChanging, INotifyPropertyChanged
{       
    public int id;      
    public System.Nullable<int> idLocation;     
    public string brandingName;     
    public System.Data.Linq.Binary timeStamp;       
    public EntitySet<LocationZipCode> LocationZipCodes;
}

public partial class LocationZipCode : INotifyPropertyChanging, INotifyPropertyChanged
{
    public string zipcode;      
    public string state;        
    public int idLocationDetail;        
    public int id;      
    public System.Data.Linq.Binary timeStamp;       
    public EntityRef<Location> Location;
}

So a Location entity would have an EntitySet of LocationZipCodes.

The Location domain model gets mapped to a view model which the presentation layer consumes and then ultimately sends back the changed view model entity where it's mapped back to the Location domain model. From there I update the entity and save the changes. Here's the handler:

public class ProgramZipCodeManagerHandler : IHttpHandler {
    private LocationsZipCodeUnitOfWork _locationsZipCodeUnitOfWork = new LocationsZipCodeUnitOfWork();

    public void ProcessRequest(HttpContext context) {
        if (context.Request.HttpMethod == "POST") {
            string json = Json.getFromInputStream(context.Request.InputStream);

            if (!string.IsNullOrEmpty(json)) {
                Location newLocation = Json.deserialize<Location>(json);
                if (newLocation != null) {
                    //this maps the location view model from the client to the location domain model
                    var newDomainLocation = new Mapper<Location, DomainLocation>(new DomainLocationMapTemplate()).map(newLocation);

                    if (newDomainLocation.id == 0)
                        _locationsZipCodeUnitOfWork.locationRepository.insert(newDomainLocation);
                    else
                        _locationsZipCodeUnitOfWork.locationRepository.update(newDomainLocation);

                    _locationsZipCodeUnitOfWork.saveChanges(ConflictMode.ContinueOnConflict);

                    var viewModel = new Mapper<DomainLocation, Location>(new LocationMapTemplate()).map(newDomainLocation);
                    context.Response.ContentType = "application/json";
                    context.Response.Write(Json.serialize(viewModel);
                }
            }
        }       
    }
}

Here is the update method within my locationRepository:

protected System.Data.Linq.Table<T> _table;

public void update(T entity) {
    _table.Attach(entity, true);
    _context.Refresh(RefreshMode.KeepCurrentValues, entity);
}

public void update(T newEntity, T oldEntity) {
    _table.Attach(newEntity, oldEntity);
    _context.Refresh(RefreshMode.KeepCurrentValues, newEntity);
}

I can see that all records directly associated with the Location entity are being updated but the child collection (public EntitySet<LocationZipCode> LocationZipCodes) is not being updated.

Is there a clear cut way of updating a disconnected entity that has a child EntitySet that also needs to be updated? In other words, I have a detached entity that has a collection of another entity. That collection has changed and I need to update that in the database.

bflemi3
  • 6,698
  • 20
  • 88
  • 155

2 Answers2

2

No.. you cannot do that.

Attaching and Detaching objects is per the object you working with and will not affect related objects (entities).

You can read further information from here:
Attaching and Detaching Objects

Consider a situation where object A is related to a collection B which is filled with 1000 values. You detach A and send it to some remote processing - A is sent with null in the B relation.. and now A is brought back to your program - there is no way to know that A.B null is a result of the remote processing or was it given to the remote processing already with null.

In the link posted with this answer - please scroll down to read carefully at the section titled:
Considerations for Detaching Objects.

G.Y
  • 6,042
  • 2
  • 37
  • 54
  • I've found this helper class which looks like will do what I'm looking for http://linq2sqleb.codeplex.com/. You are correct though, very frustrating – bflemi3 Mar 28 '13 at 17:26
0

Not quite sure I understand your question but here goes... Something like the following entity object extension perhaps where you reattach the entity to the context etc will work by reattaching the entity to the object context and setting the entitystate appropriately.

    /// <summary>
    /// AttachEntityToObjectContext attaches an EntityObject to an ObjectContext
    /// </summary>
    /// <param name="entityWithRelationships">An EntityObject that has relationships</param>
    /// <param name="newContext">The ObjectContext to attach the entity to</param>
    /// <returns>True if the entity has relationships (and therefore the method could succeed). Otherwise false.</returns>
    /// <remarks>Objects are retrieved using one ObjectContext, stored in ViewState and then
    /// an attempt to save them is then made. The save attempt does not save the object. This is because it is a different context which is saving the object.
    /// So the object needs to be detached from its old context, added to the new context and have its EntityState maintained so that it gets saved.</remarks>
    public static bool AttachEntityToObjectContext(this IEntityWithRelationships entityWithRelationships, ObjectContext newContext)
    {
        EntityObject entity = entityWithRelationships as EntityObject;
        if (entity == null)
        {
            return false;
        }

        if (entity.EntityState != EntityState.Detached)
        {
            ObjectContext oldContext = entity.GetContext();
            if (oldContext == null)
            {
                return false;
            }

            if (oldContext != newContext)
            {
                EntityState oldEntityState = entity.EntityState;
                oldContext.Detach(entity);
                newContext.Attach(entity);
                newContext.ObjectStateManager.ChangeObjectState(entity, oldEntityState);
            }
        }

        return true;
    }

    /// <summary>
    /// GetContext gets the ObjectContext currently associated with an entity
    /// </summary>
    /// <param name="entity">An EntityObject</param>
    /// <returns>The ObjectContext which the entity is currently attached to</returns>
    private static ObjectContext GetContext(this IEntityWithRelationships entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        var relationshipManager = entity.RelationshipManager;

        var relatedEnd = relationshipManager.GetAllRelatedEnds().FirstOrDefault();

        if (relatedEnd == null)
        {
            // No relationships found
            return null;
        }

        var query = relatedEnd.CreateSourceQuery() as ObjectQuery;

        if (query == null)
        {
            // The Entity is Detached
            return null;
        }

        return query.Context;
    }
Paul Zahra
  • 9,522
  • 8
  • 54
  • 76
  • I've updated my question to hopefully better reflect my problem. – bflemi3 Mar 26 '13 at 14:25
  • linq-to-sql contexts don't have a detach method. Is this for EF? – bflemi3 Mar 27 '13 at 16:25
  • Yes it's L2E, although I did say 'something' like this, hoping to point you in the right direction. We use this code for reattaching entities that are stored in the cache in the view, to the current context. – Paul Zahra Mar 28 '13 at 08:50