Print Page | Close Window

Assigning properties returning ComplexObjects

Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce 2009
Forum Discription: For .NET 3.5
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=1574
Printed Date: 18-Apr-2024 at 7:46am


Topic: Assigning properties returning ComplexObjects
Posted By: WardBell
Subject: Assigning properties returning ComplexObjects
Date Posted: 02-Dec-2009 at 4:57pm
A DevForce developer asked me the following:
I have an error when I set a complex entity to another complex entity in DevForce . The error says “System.ArgumentException: ComplexObject instances cannot be shared between Entitites. Clone them first.”
So if I have code like deliveryFile.Department = someWorkOrder.Department;
Where the department is a complex type and I used the same Entity Manager to load both the objects.
 
Could you please let me know why this is not working.
The short answer is that you must clone the Department ComplexObject... just as the exception says.

I think your problem will go away if you write:

deliveryFile.Department = someWorkOrder.Department.Clone();

"Clone" is a method on the DevForce base ComplexObject type from which Department derives.

--

I'm sure you are wondering why.

Complex types are not entities. It doesn't matter whether the "Department" parents are in the same EntityManager or different managers.

Complex types are DevForce objects that map to fields in a table row. They do not map to their own rows in their own tables. They have no independent existence as entities. They exist only as complex representations of data stored within their parent entities.

Aside: For simplicity I omit the possibility of nesting complex types.

Complex types are a convenient way to represent multiple fields of a table as a single value object of an entity mapped to that table. Consider a Person table with {First, Last, Street, City, State, Zip, Country} columns. We could represent it as an entity with {First, Last, Street, City, State, Zip, Country} properties. Or we could gather together the {Street, City, State, Zip, Country} as an Address complex type; the Person then sports {First, Last, Address} properties and the Address property returns an Address object. The Address object is not an entity. It has no key. It lives solely to encapsulate access to the underlying Address-related fields of the Person entity.

Aside: it will also exhibit Address behavior if you extend it with custom properties, events, and members of your own.

Address is a convenience from the persistence perspective. Ultimately, its fields must be written back to the corresponding columns in the Person table row.

In your example, the values in the "Department" object map to fields in the table that supports your "deliveryFile" entity; the "Department" is not an entity that can be retrieved or saved independently of the "deliveryFile". It encapsulates whatever "Department" means in your "deliveryFile" entity.

The expression "someWorkOrder.Department" returns a Department value object that is specific to the WorkOrder. It belongs to that WorkOrder ... and to no other entity.

It follows that "deliveryFile.Department" and "someWorkOrder.Department" can never refer to the same object instance. They may have the same inner values - they may describe the same department in every respect - but they cannot be the same object. Because they belong to their parent entities and cannot be shared with other entities. That's the nature of a Complex Type object.

With this in mind, imagine what would happen if we permitted assignment of Complex Type objects in the manner you attempted. What would happen if you could pass a Complex Type object from one entity to another? Consider the following code:

deliveryFile.Department = someWorkOrder.Department; // illegal in DevForce
deliveryFile.Department.City = "xxx";

What should happen when we run this next line?

if (deliveryFile.Department.City == someWorkOrder.Department.City) {
  throw new InvalidOperationException("Oops ... unintentionally changed WorkOrder department city.");
}


That's right; we'll get the exception. We intended to change the deliveryFile's Department, but we changed the WorkOrder's department as well. That's incorrect for Complex Types which are utterly captive to their parent entities.

That's to be expected if Department were an entity. If you want the two Departments to be the same object instance, then you want an entity, not a value object ... not a complex type.

You might wonder (as I did briefly) why DevForce can't be smart about this. When we see "deliveryFile.Department = someWorkOrder.Department;" we could auto-clone for you; we could do implicitly the equivalent of "deliveryFile.Department = someWorkOrder.Department.Clone();".

However, such implicit behavior risks being misunderstood when read. It is best to write the code explicitly.



Print Page | Close Window