Architect or Cobbler?
Good code starts with good design

Deleting objects

Friday, March 07, 2008

Before I start to talk about the Clear method, a little correction to the code I published yesterday.  I am effectively obtaining and closing an ObjectContext with each DAO call.  This is extremely inefficient, and i probably want to change it in future so that the Context is opened once per thread, but I'm going to leave it as it is for now.  It does mean that I should Detach any entities from the ObjectContext before returning them, as an entity keeps a back pointer to it's ObjectContext, and it can't be attached to more than one at a time my DAO would start throwing exceptions the minute you started using it properly.  

So why is deleting objects so difficult?   Essentially it isn't if the object graph is attached and fully built, you issue a DeleteObject and the entity and the relationships are flagged as deleted, any related entities that have Cascade delete rules will also be flagged as deleted(and when you issue SaveChanges the rows will be removed from the underlying store).  But in our DAO the objects are being sent to is in a Detached state, which means you have to rebuild the object graph in order for the object to be deleted - If you don't you'll end up with either orphaned records in the database, or you'll throw Constraint exceptions.

There are many approaches to building the Object Graph back up, for instance DannySimmons has a blog posting about this.   Unfortunately, while Danny's method is cool it doesn't work in a disconnected scenario very well (try to reason why.  Here's a big hint, will the ObjectSateManager contain values for detached objects?), or for my scenario, which is to work with any entity (I do have a generic DAO after all). 

So I know to get hold of the relationship information for any entity, and while I'm sure there may be easier ways i just grabbed the attribute values from the EdmRelationshipAttribute which is added by the EDM generator.  This lists all the relationships across all Entity Containers and all Entity Sets, so i need to know which Entity container and Set I'm working with, so I've modified the constructor to pass these in.

Once I have the attribute names it's a fairly straightforward matter to test if the entity I'm passing in is involved in the relationship.  I was even toying with writing a Linq query, then decided against it ;-)

RelationshipManager rm = relations.RelationshipManager;
foreach (EdmRelationshipAttribute attr in attributes)
{
     // The relationship may not exist for this entity
     // as it may be for another entity set
     try
     {
         rm.GetRelatedEnd(attr.RelationshipName, attr.Role2Name);
     }
     catch
     {
         // do nothing
     }
}
re = rm.GetAllRelatedEnds();

Now all you have to do is load the related objects into the ObjectContext.  If you have these entities, you could simply attach them to the context, and then add them back in to the related end, in my case I went for the expensive option (but always guaranteed to work) of loading the object graph by issuing Load().  Now you can safely delete the object

foreach (IRelatedEnd related in re)  
{
     related.Load(); }

There is only one problem to solve now, how do I get every object in the EntitySet?

I'll leave you with that little puzzle, but think Linq :-)


# posted by James @ 12:26 PM   5 comments

Generic DAO and the Entity Framework

Wednesday, March 05, 2008

I've been a big Hibernate user over the years, and I've really got used to the ease of development and the way that Hibernate insulates me form the underlying database.  Over the years I've had a few occasions to port databases and the Data Access Object has always stood me in good stead, in particular the Generic DAO, which acts as the CRUD layer for all my queries.  Hibernate makes it incredibly easy to do, and the persistence ignorance and Session management mean I can really make the domain model completely unaware of the persistence scheme.

So next week I'm talking about the Entity Framework and patterns at DevWeek, and as I use the DAO pattern so much I thought I'd implement it in the EF.  And after tearing my hair out for a few hours today, I can safely say that although there are some bits of the EF I really like, the context management and lack of persistence ignorance are real downers.

Anyway here goes:

I basically started off creating my usual CRUD interface, but there are a few wrinkles.  I really should have used IPOCO, and I probably will in the future, but for now I just made sure that all my generic types are constrained to be subtypes of EntityObject, giving an interface like:

public interface GenericDAO<TEntity> where TEntity : EntityObject

    TEntity Create(TEntity entity); 
    TEntity Delete(TEntity entity); 
    TEntity Update(TEntity entity); 
    List<TEntity> FindAll(); 
    void Clear();
}

I'm a bit unhappy about exposing the EntityObject to the business layer, but I spent too many years with EJBs making Data Transfer Objects (DTOs) to even contemplate using them.  I started out using Microsoft's quaintly named IPOCOs.  But after I had to implement 3 interfaces and manually do all the change tracking, I nearly threw the laptop at the wall.  There are some efforts to make it a bit easier by using code generation and Postsharp and you can read Ruurd Boekes blog here.  But to be honest, you can't polish a ...

The implementation of the CUD methods were fairly straightforward.  To illustrate I'll start by showing the constructor and the Create method.

public EFGenericDao(string containerName, string entitySet, string connectionString)
{

         this._containerName = containerName;
         this._entitySet = entitySet;
         this._connectionString = connectionString;
         this._fullEntitySetName = containerName + "." + entitySet; }

public virtual TEntity Create(TEntity entity) {    
using (ObjectContext ctx = new ObjectContext(_connectionString))
   {    
     ctx.AddObject(_fullEntitySetName, entity);
      ctx.SaveChanges();
   }
   return entity; }

As I'm going to use ObjectContext rather than the code generated context that the EDM designer gives me, you need to pass in the connection string, entity container name and the entity set that contains the entities you want to work with.  The Create is very straightforward, simply add the object to the context and then commit using SaveChanges.

Delete and Update are a little more nasty, although you could avoid my nasty little refresh from the database if you set the EntityKey and called attach to add the object into the context.  I chose the easy way out, I simply load the object into the context from the database and then delete or update as appropriate.

public TEntity Update(TEntity entity)
{
    using (ObjectContext ctx = new ObjectContext(_connectionString))
    {
        object entityToUpdate = GetObjectByKey(entity.EntityKey);
        if (entityToUpdate != null)
        {
            ctx.ApplyPropertyChanges(_fullEntitySetName, entity);
        }
    }
    return entity;
}

That's enough to whet the appetite. Tomorrow I'll look at how to do the Clear, because it's quite a tricky little bit of code, with reflection in a few places. (For a while I nearly just thought damn it, I'll write a stored proc, but I persevered)

# posted by James @ 11:13 PM   2 comments

The end of an era

Or that's what it feels like. After 8 years I've decided it's time for me to move on, so I've left QA-IQ and branched out into the big bad world of freelancing. So my blog is moving as well, you'll now find it at http://www.jghd.co.uk/blogs/JamesWinters. For my loyal band of 2 readers, that means you'll have to update your bookmarks. It will also hopefully mean that I'll start blogging more (but previous evidence doesn't actively support me here)

# posted by James @ 7:43 PM   15 comments

Hubris ...

Saturday, November 24, 2007
is a funny word, look it up here if you don't know it :-) This afternoon's presentation on Facebook brought me to earth with a bang. My Internet connection crashed, then I attached to the wrong process , and finally I forgot how to code. The audience were kind, they clapped, but they should have booed. And to top it all off, this is the presentation that got video'd. Still, the slides are here, and the source is here.

# posted by James @ 8:59 PM   1 comments

DDD6 - One down, one to go

Well that's the first talk on the Entity Framework done. Had a full room, people sitting on the floor, and overflowing into the halls, so that was good. The demos all worked, so that was even better. And the practice at last week's user group was extremely useful, because I didn't even overrun. The slides are here, and the source code with appropriate comments is here. If you have any questions, just ask them in the comments section.

# posted by James @ 1:35 PM   4 comments
This page is powered by Blogger. Isn't yours?