Encapsulating Sets of Properties in Complex Types, convenient or not?

With EF we can group properties into Complex Properties. For example I'm using audit information on all my entities, like CreatedOn, UpdatedOn, Deleted and Enabled and I grouped them into a ComplexType named Audit. A customer can have an InvoiceAddress and a DeliveryAddress of type Address(Street1, Street2, County, Town Postcode).




This is nice, but I encountered some problems with the updates.
As MSDN says "When any property is changed anywhere in the object graph of a complex type, the property of the parent type is marked as changed and all properties in the object graph of the complex type are updated when SaveChanges is called."
So if the UpdatedOn is changed only, the EF will "update" and CreatedOn, Enabled, Deleted properties when the SaveChanges is called. This means the update statement will update UpdatedOn column with the new value and the CreatedOn, Enabled and Deleted with the values present in the Object Graph:
Let's create first a customer

var customer = new Customer
{
Email="adrian@domain.com",
AuditInfo = new Audit
{
CreatedOn = DateTime.Now,
UpdatedOn = DateTime.Now,
Enabled = true,
Deleted = false
}
};
Customers.AddObject(customer);
SaveChanges();

Now let's see how the update behaves:

var customer = Customers.First(c=>c.Email=="adrian@domain.com");
customer.AuditInfo.Enabled = false;
customer.AuditInfo.UpdatedOn = DateTime.Now;
SaveChanges();


SELECT TOP (1)
[Extent1].[CustomerId] AS [CustomerId],
[Extent1].[Email] AS [Email],
[Extent1].[Enabled] AS [Enabled],
[Extent1].[Deleted] AS [Deleted],
[Extent1].[CreatedOn] AS [CreatedOn],
[Extent1].[UpdatedOn] AS [UpdatedOn]
FROM [dbo].[Customer] AS [Extent1]
WHERE 'adrian@domain.com' = [Extent1].[Email]
GO

-- Region Parameters
DECLARE @0 Bit = 0
DECLARE @1 Bit = 0
DECLARE @2 DateTime2 = '2011-07-28 14:46:42.1570000'
DECLARE @3 DateTime2 = '2011-07-28 15:14:09.2868054'
DECLARE @4 Int = 5
-- EndRegion
update [dbo].[Customer]
set [Enabled] = @0, [Deleted] = @1, [CreatedOn] = @2, [UpdatedOn] = @3
where ([CustomerId] = @4)

So the update used the new values for UpdatedOn and Column and for the Deleted and CreatedOn values it used the ones from database, via Customers.First call.

Now the question is: how the update will behave when using Attach method? Attach method will save a query to the database when the Key value is known (ie. Id).

var customer = new Customer { CustomerId = 5 };
Customers.Attach(customer);
customer.AuditInfo.Enabled = false;
customer.AuditInfo.UpdatedOn = DateTime.Now;
SaveChanges();


-- Region Parameters
DECLARE @0 Bit = 0
DECLARE @1 Bit = 0
DECLARE @2 DateTime2 = '0001-01-01 00:00:00.0000000'
DECLARE @3 DateTime2 = '2011-07-28 15:20:42.4672941'
DECLARE @4 Int = 5
-- EndRegion
update [dbo].[Customer]
set [Enabled] = @0, [Deleted] = @1, [CreatedOn] = @2, [UpdatedOn] = @3
where ([CustomerId] = @4)

As MSDN says, it updated all properties. Of course, it used the ones present in memory, in this case the default values '0001-01-01 00:00:00.0000000' for CreatedOn and 0 for Deleted.

Fortunately, this update raised an error "The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.", but in the case of the Address type, where the properties are Strings, it would set on nulls, and if there is no constraint on database, there would be no error..

The things were very very strange, when I was updating the Entity via a FormView and an EntityDataSource. The issue was the EntityDataSource had the property EnableFlattening="false". In this case, the update behaved like the Attach method, putting the default values instead of the original ones. With the wrapper around the entity, the update worked correctly. MSDN says "If your conceptual model includes foreign key properties and does not contain complex types, the EntityDataSource designer will set EnableFlattening to false at design-time to avoid creating entity wrapper objects."

Did you build your model based on Complex Types? Is it convenient? Think again :)

Comments

Popular posts from this blog

IIS 7.5, HTTPS Bindings and ERR_CONNECTION_RESET

Table Per Hierarchy Inheritance with Column Discriminator and Associations used in Derived Entity Types

Verify ILogger calls with Moq.ILogger