3
votes

I have a common Repository with Add, Update, Delete. We'll name it CustomerRepository.

I have a entity (POCO) named Customer, which is an aggregate root, with Addresses.

public class Customer
{
     public Address Addresses { get; set; }
}

I am in a detached entity framework 5 scenario.

Now, let's say that after getting the customer, I choose to delete a client address. I submit the Customer aggregate root to the repository, by the Update method.

How can I save the modifications made on the addresses ?

  1. If the address id is 0, I can suppose that the address is new.
  2. For the rest of the address, I can chose to attach all the addresses, and mark it as updated no matter what.
  3. For deleted addresses I can see no workaround...

We could say this solution is incomplete and inefficient.

So how the updates of aggregate root childs should be done ?

Do I have to complete the CustomerRepository with methods like AddAddress, UpdateAddress, DeleteAddress ?

It seems like it would kind of break the pattern though...

Do I put a Persistence state on each POCO:

public enum PersistanceState
{
     Unchanged,
     New,
     Updated,
     Deleted
}

And then have only one method in my CustomerRepository, Save ?

In this case it seems that I am reinventing the Entity "Non-POCO" objects, and adding data access related attribute to a business object...

2
Are these EF entities? Correct me if I'm wrong, but if you load your aggregate root (Customer) and access its navigation properties (i.e. Addresses), shouldn't these be lazy loaded, attached to the context and thus, be saved/deleted automatically when calling SaveChanges? - khellang
In my case, Customer is detached, and then attached, not loaded from the DbContext. Poco entities are totally independant from EF, there could be no lazy loading. - Roubachof
OK, so what you are saying is that in your repository, you load the customer (ENTITY POCO) and map it to a DOMAIN POCO, Customer? Then you do some operations on it before you want to map it back to an ENTITY POCO and save it? - khellang
nope, the POCO is the entity, thanks to the POCO T4 template. - Roubachof
Jeez... Are you working with proxies (loaded from DbContext) or not? - khellang

2 Answers

2
votes

First, you should keep your repository with Add, Update, and Delete methods, although I personally prefer Add, indexer set, and Remove so that the repository looks like an in memory collection to the application code.

Secondly, the repository should be responsible for tracking persistence states. I don't even clutter up my domain objects with

object ID { get; }

like some people do. Instead, my repositories look like this:

public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository

The AggregateRootDataModel class is what I use to track the IDs of my in-memory objects as well as track any persistence information. In your case, I would put a property of

List<AddressDataModel> Addresses { get; }

on my CustomerDataModel class which would also hold the Customer domain object as well as the database ID for the customer. Then, when a customer is updated, I would have code like:

public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository
{
    public Customer this[int index]
    {
        set
        {
            //Lookup the data model
            AggregateRootDataModel model = (from AggregateRootDataModel dm in this
                                           where dm.Customer == value
                                           select dm).SingleOrDefault();
            //Inside the setter for this property, run your comparison 
            //and mark addresses as needing to be added, updated, or deleted.
            model.Customer = value;
            SaveModel(model); //Run your EF code to save the model back to the database.
        }
    }
}

The main caveat with this approach is that your Domain Model must be a reference type and you shouldn't be overriding GetHashCode(). The main reason for this is that when you perform the lookup for the matching data model, the hash code can't be dependent upon the values of any changeable properties because it needs to remain the same even if the application code has modified the values of properties on the instance of the domain model. Using this approach, the application code becomes:

IAggregateRootRepository rep = new ConcreteRepository([arguments that load the repository from the db]);
Customer customer = rep[0]; //or however you choose to select your Customer.
customer.Addresses = newAddresses;  //change the addresses
rep[0] = customer;
1
votes

The easy way is using Self Tracking entities What is the purpose of self tracking entities? (I don't like it, because tracking is different responsability).

The hard way, you take the original collection and you compare :-/

Update relationships when saving changes of EF4 POCO objects

Other way may be, event tracking ?