Unwanted Cascading Change

Posts   
 
    
Harry
User
Posts: 73
Joined: 26-Jun-2007
# Posted on: 15-Jan-2008 14:40:40   

Hello,

I am having some undesired behavior and would like to know if there is a way to prevent it from happening.

I have a Company and Companies have Addresses. In our datamodel we have this normalized out to an Address table that is joined to Company via a CompanyAddress table with composite keys and the like.

So what we have is: Company : CompanyId Name

Address : AddressId Street1 Street2 etc.....

AddressType : AddressTypeId Name

CompanyAddress : CompanyId AddressId AddressTypeId

What we are seeing is when a Company has 2 CompanyAddressEntities that share a common AddressEntity, changes made to one affect the other as well.

This is understandable given the default behavior and no modifications to the relations or entities in the project on our end.

So we know that a CompanyAddress has an Address and that an Address has a collection of CompanyAddress. What I would like to know is is there any way to prevent the update in the address entity of 1 from affecting the other without modifying the project and hiding the CompanyAddress field that is mapped onto the 1:n relation of the Address entity?

Thanks in advance!

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 16-Jan-2008 08:33:18   

is there any way to prevent the update in the address entity of 1 from affecting the other

Aren't they the same entity? Would you please provide some code snippets that shows how you fetch the graph of objects and how you make changes to it.

An irrelevant question, why don't you use a simple design as the following:

Company : CompanyId Name

Address : AddressId AddressTypeId CompanyId Street1 Street2 etc.....

AddressType : AddressTypeId Name

And if the company uses the same address more than once with different types, then more than one entry should take place in the AddressEntity. This would make sure that modifying one of them won't affect the other.

Harry
User
Posts: 73
Joined: 26-Jun-2007
# Posted on: 16-Jan-2008 12:26:13   

Here is a snippet of how we are doing the fetches: Single Entity:

CompanyEntity company = new CompanyEntity();            
PrefetchPath2 prefetch = new PrefetchPath2(EntityType.CompanyEntity);
prefetch.Add(CompanyEntity.PrefetchPathCompanyAddress).SubPath.Add(CompanyAddressEntity.PrefetchPathAddress);       
DataAccessAdapter adapter = new DataAccessAdapter();
adapter.FetchEntity(Company, prefetch );

EntityCollection:

EntityCollection<CompanyEntity> companies= new EntityCollection<CompanyEntity>();
IRelationPredicateBucket filter = new RelationPredicateBucket();
filter.PredicateExpression.Add(CompanyFields.CompanyId == companyID);
PrefetchPath2 prefetch = new PrefetchPath2(EntityType.CompanyEntity);
prefetch.Add(CompanyEntity.PrefetchPathCompanyAddress).SubPath.Add(CompanyAddressEntity.PrefetchPathAddress); 
DataAccessAdapter adapter = new DataAccessAdapter();
adapter.FetchEntityCollection(companies, filter, PrefetchPath);

As for the entity being the same, yes and no. The underlying address entry is the same but when it is changed we do not want it to be changed everywhere.

We cannot change the design at this point as we are too far along.

In your simple design you miss the association of the company with addresses. We originally had the address type as a property of the address until we realized that the data the client was using would cause issues with the intention of the design. The design is to allow for Addresses to be stored as single atomic units that will never change. For example: 123 Main Street, Florida, USA The above entry should only exist in the database 1 time.

Any time we need the address we associate an entity with it, such as company. If the company needs to modify their address, we take the information they entered and determine if an entry already exists in the database, if it does we use that ID, if it does not we insert and use the new ID.

This system will eventually have many many entities that will need address information. This design allows us to more easily use this information in code as well as keeping down on redundancy in the database.

Consider this, each company will have Employees, each Employee will have a list of Addresses assoicated with them.

The default address for a new employee will be the companies main address. To do this we simply say:

Employee.EmployeeAddress.Address = Company.CompanyAddress.Address
;

But if we need to make a change to the Employees address we do not want it to affect the companies address.

I hope that what I have posted helps. Thanks.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 16-Jan-2008 14:42:00   

As for the entity being the same, yes and no. The underlying address entry is the same but when it is changed we do not want it to be changed everywhere

I don't quite understand the above part, entities in code should reflect database entities. And in this case (by your design) the Graph would look like the following:

                             Company1
                                  /  \
                                 /     \
                                /        \
    CompanyAddress1      CompanyAddress2
                                \         /
                                 \      /
                                  \   /
                               Address1

In your simple design you miss the association of the company with addresses

I did not simple_smile please re-check the following quote.

Walaa wrote:

Address : AddressId AddressTypeId CompanyId Street1 Street2 etc.....

But if we need to make a change to the Employees address we do not want it to affect the companies address.

That's why I've suggested to separate the addresses. But if you still need to use your design as is, then you should disable Address modification (no Updates should take place), otherwise if an employee want to change his address which is referenced by others, then you should create a new address for him.

So using your design no updates should take place in the Address entity but rather in the CompanyAddress entity which is safe enough.

Harry
User
Posts: 73
Joined: 26-Jun-2007
# Posted on: 16-Jan-2008 15:05:00   

Address : AddressId AddressTypeId CompanyId Street1 Street2 etc.....

Your right, I did miss that! Sorry. flushed

But if you still need to use your design as is, then you should disable Address modification (no Updates should take place), otherwise if an employee want to change his address which is referenced by others, then you should create a new address for him.

How would I do that? Is there a way to make an entity readonly?

I still need to be able to pull the data from the database and pass the object across the wire (WCF).

How would I disable Address modification?

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 16-Jan-2008 15:30:09   

Is there a way to make an entity readonly?

I still need to be able to pull the data from the database and pass the object across the wire (WCF).

How would I disable Address modification?

You must be using the Adapter model, right? You can use EntityValidation -> ValidateEntityBeforeSave and raise an ORMEntityValidationException if involvedEntity.IsNew was false. This would prevent an Address entity to be updated. That's the low level solution. But also you may handle this at a higher level if you use a business layer and/or at the WCF service.

Harry
User
Posts: 73
Joined: 26-Jun-2007
# Posted on: 16-Jan-2008 16:35:02   

Yes I am using Adapter model.

The suggestion you made really does not apply. It would throw an error at the point of the save but the entities involved in that save would still be incorrect, as far as what I am trying to accomplish.

If you go back to my original scenario where the company has 2 CompanyAddresses and they both share a common Address when 1 of them is changed both CompanyAddresses will become dirty.

Is there any way to prevent that from happening?

Harry
User
Posts: 73
Joined: 26-Jun-2007
# Posted on: 16-Jan-2008 18:50:05   

I have come up with a solution. I make all of the fields of the AdressEntity ReadOnly in the Project.

Then in CompanyAddress I am implementing an Interface, IAddress, that has all of the fields of the Address. In the implementation of the Interface in the CompanyAddress I manage the internal AddressEntity whenever a property is accessed via the Interface implementation.

So far it appears to work well.

I will update this post if I encounter any issues with the approach.

Thanks for all the help!