Thursday, March 11, 2010

Entity Framework 4 - Modified Self-Tracking Entity framework that supports Code-Only and Persistence Ignorance

Source Code (Updated):  Selftracking.zip

Overview

I like the Self-Tracking templates that come with the latest release of the Entity Framework in VS2010 RC. If you haven't read much about it before, have a look at the ADO .NET team blog.
In this post I'll share with you a customised version of the VS2010 RC Self-Tracking framework that I've built in my spare time and, although it is largely based on the Entity Framework Self-Tracking feature in VS2010 RC, it also contains a fair amount of enhancement. Specifically, this framework:
  1. Provides support for Code Only Self-Tracking entities and complex types that does not involve the EF designer. Don't get me wrong; I like the designer; but it doesn't scale well once you have more than a handful of classes. I guess one could possibly modify/tweak the existing templates so you can split your domain model in different edmx files - but I wanted a pure code-only approach...so instead I've created my own T4 template that uses your domain object classes as the source of its generation.
  2. Demonstrates how to implement a persistent ignorant data access layer for EF. Specifically, I expose IUnitOfWork, ISession and IRepository interfaces and register my EF-specific implementations via an IoC container - in this case Unity.
  3. Includes a few minor tweaks and bug fixes to improve common usage scenarios with Self-Tracking entities...more about this later.
  4. Shows how to use the self-tracking framework via the included sample domain model and unit tests. 
Note: I haven't incorporated WCF usage in the sample provided here - however I already have used it in this context with great success...perhaps I will blog about this in future…
Before I begin, I just want to re-iterate that the code directly related to implementing self-tracking support is primarily a copy/paste job from the code generated by the Self-Tracking templates feature of the VS2010 RC release. I have however wrapped most of the generated code into methods that can be succinctly invoked from your domain objects. Most of the work I’ve done was involved in the creation of a new T4 code generation template as well as making the data access layer persistent ignorant.  
I'd also like to mention that I found the following resource quite helpful when I started out with EF4 - especially the bits regarding mapping Poco objects and repository implementation for loading navigation properties - its certainly worth a read if you’re new to EF4.
Without further ado, lets get started and see how to use this framework.

Getting Started: The Sample Project

The image below shows the project structure.
image
The following provides more detail on the individual framework projects:
  • SelfTracking.Core. This project provides our IoC implementation (via Unity). It also contains a helper method to make validation with the Enterprise Library Validation block a bit easier when the type of the object being validated is not known at compile time. 
  • SelfTracking.DataAccessLayer contains the interfaces for our persistence ignorant data access layer.
  • SelfTracking.DataAccessLayer.EF is our Entity Framework-specific implementation of the Data Access Layer interfaces. There’s also a repository implementation for non-self-tracking entities.
  • SelfTracking.DomainModel contains the Self-Tracking framework. It also contains the T4 template you should copy to your own project and some code snippets you may find useful when writing your domain objects and/or properties.
The Sample projects demonstrate usage of this self-tracking framework by introducing a very simple domain model that includes one-to-many & one-to-one associations as well as a complex object.
  • Sample.DataAccessLayer introduces the IStandardTrackableObjectRepository interface for our sample project. I’ve chosen to only use one repository interface for this domain as it more than suffices – however larger projects will most likely require more. 
  • Sample.DataAccessLayer.EF contains the EF implementation of IStandardTrackableObjectRepository, mappings for our domain objects as well as a registrar class to assist with mapping/registration of our domain objects with our IoC (Inversion of Control) container.
  • Sample.DomainModel contains our simple domain model.
  • Sample.TestDataAccessLayer contains a few unit tests to keep me honest…mmm, since I'm talking about being honest here...these are actually integration tests, not unit tests - either way, you'll get some insight on how this all fits together by looking at these.
The example domain is depicted below - a few important points are worth mentioning:
  • There’s a many to many relationship between Company and CompanyDivision (e.g. “Marketing”, “Accounting”…). Notice how this is mapped as two one-to-many relationships rather than a single many-to-many relationship. This is deliberate and I would strongly encourage you do the same in your projects. Basically, one often needs to store additional data as part of a many-to-many relationship (e.g. “created by user” or “is active” if you do logical deletes) – this can’t be achieved via a single many-to-many mapping. Even if you do not initially think that you’ll need to store additional data with this type of relationship, you could be in for a bit of rework later when you need to change it. But don’t just take my word for it…NHibernate in Action gives a much better and more lengthy discussion on this matter.
  • Each Company can have a CEO (0-1 multiplicity)
  • Each Employee must be employed by a company, and each company can employ many employees (one-to-many)
  • For demonstration purposes, Address is a deliberate (bad) example of a complex object. Typically you’d make this an entity instead so that you can have Foreign keys for things like State and PostCode (i.e. Zip code for folks in the US)
image
 

Using the T4 Template

When creating a new domain model project, first copy the TrackableObjectCodeGen.tt T4 template file to your new project. Also ensure that you specify TextTemplatingFileGenerator for the Custom Tool property in the properties window for this file. You’ll also have to manually trigger the execution of this file whenever you modify your domain projects – however you can also get it to automatically generate code every time you build your project.
I’ll use the Employee class from the sample domain project as an example as it contains quite a few different types of properties. The following listing shows the code – i.e. this contains the code you have to write – not the code that’s generated.

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.Text;
  6. using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
  7. using Sample.DomainModel.Common;
  8. using SelfTracking.DomainModel;
  9. using SelfTracking.DomainModel.TrackableObject;
  10.  
  11. namespace Sample.DomainModel.Company
  12. {
  13.     [Serializable]
  14.     [DataContract(IsReference = true), KnownType(typeof(Address)), KnownType(typeof(Company))]
  15.     public partial class Employee : ActiveBasicObject, IConcurrencyObject
  16.     {
  17.         #region Fields
  18.         public const int MaxNameLength = 20;
  19.         private Address _HomeAddress;
  20.         private bool _HomeAddressInitialised;
  21.         private string _FirstName;
  22.         private string _MiddleName;
  23.         private string _LastName;
  24.         private Company _CeoOfCompany;
  25.         private Company _EmployedBy;
  26.         private byte[] _concurrencyToken;
  27.         #endregion
  28.  
  29.         #region Primitive Properties
  30.         [DataMember]
  31.         [StringLengthValidator(1, MaxNameLength)]
  32.         public string FirstName
  33.         {
  34.             get { return PrimitiveGet(_FirstName); }
  35.             set { PrimitiveSet("FirstName", ref _FirstName, value); }
  36.         }
  37.         
  38.         [DataMember]
  39.         [IgnoreNulls]
  40.         [StringLengthValidator(1, MaxNameLength)]
  41.         public string MiddleName
  42.         {
  43.             get { return PrimitiveGet(_MiddleName); }
  44.             set { PrimitiveSet("MiddleName", ref _MiddleName, value); }
  45.         }
  46.         
  47.         [DataMember]
  48.         [StringLengthValidator(1, MaxNameLength)]
  49.         public string LastName
  50.         {
  51.             get { return PrimitiveGet(_LastName); }
  52.             set { PrimitiveSet("LastName", ref _LastName, value); }
  53.         }
  54.  
  55.         /// <summary>
  56.         /// Gets or sets the concurrency token that is used in optimistic concurrency checks
  57.         /// (e.g. typically maps to timestamp column in sql server databases)
  58.         /// </summary>
  59.         /// <value>The concurrency token.</value>
  60.         [DataMember]
  61.         public byte[] ConcurrencyToken
  62.         {
  63.             get { return PrimitiveGet(_concurrencyToken); }
  64.             set
  65.             {
  66.                 // no point using a "proper" equality comparer here as this field is auto calculated and typically only be set by the persistance layer
  67.                 PrimitiveSet("ConcurrencyToken", ref _concurrencyToken, value, (a, b) => false);
  68.             }
  69.         }
  70.  
  71.         #endregion
  72.  
  73.         #region Complex Properties
  74.         [DataMember]
  75.         [ObjectValidator]
  76.         public Address HomeAddress
  77.         {
  78.             get { return ComplexGet(ref _HomeAddress, ref _HomeAddressInitialised, HandleHomeAddressChanging); }
  79.             set
  80.             {
  81.                 ComplexSet("HomeAddress", ref _HomeAddress, value,
  82.                            ref _HomeAddressInitialised, HandleHomeAddressChanging);
  83.             }
  84.         }
  85.         #endregion
  86.  
  87.         #region Navigation Properties
  88.         /// <summary>
  89.         /// If this employee is a CEO of a company, this property navigates to the associated company.
  90.         /// </summary>
  91.         /// <value>The ceo of company.</value>
  92.         [DataMember, Persist("Company-Ceo")]
  93.         public Company CeoOfCompany
  94.         {
  95.             get { return OneToOneNavigationGet(_CeoOfCompany); }
  96.             set
  97.             {
  98.                 FromChildOneToOneNavigationSet("CeoOfCompany", ref _CeoOfCompany, value,
  99.                                                root => root.Ceo, (root, newValue) => root.Ceo = newValue, true);
  100.             }
  101.         }
  102.  
  103.         /// <summary>
  104.         /// The company that currently employs this employee - mandatory (otherwise this isn't an employee!)
  105.         /// </summary>
  106.         /// <value>The employed by.</value>
  107.         [DataMember, Persist("Company-Employees")]
  108.         [NotNullValidator]
  109.         public Company EmployedBy
  110.         {
  111.             get { return ManyToOneNavigationGet(ref _EmployedBy); }
  112.             set
  113.             {
  114.                 ManyToOneNavigationSet("EmployedBy", ref _EmployedBy, value, oneside => oneside.Employees);
  115.             }
  116.         }
  117.  
  118.         #endregion
  119.     }
  120. }
   
This class inherits from ActiveBasicObject – although typically you would only need to derive from the TrackableObject class. I’ve used ActiveBasicObject as an easy way to provide the majority of my entities with an Id and IsActive Boolean property (for logical deletes).
Undoubtedly, all those Primitive/ManyToOne/Get/Set methods in the property implementations are going to look strange to you. These methods serve 2 important purposes:
  • It instructs the T4 template what type of property it is so that it can generate the right code (the T4 template inspects the setter property body implementations for the presence of these methods).
  • They contain all the code that is necessary for Self-Tracking to function – including raising property changed events and ensuring that navigation properties on both sides of the association are kept in sync. Basically, these methods contain the code as copied from the Self-Tracking VS2010 RC release – however they’ve been cleaned up a bit and neatly wrapped into one liner methods. Aside: in most cases, the getter methods don’t really do anything other than returning the supplied field value. As such, they aren’t really required and the template will continue to function without them. Nonetheless, I think this adds a nice symmetry to the property implementations by having both get/set methods – beside, you could extend the getter methods to implement some of your own functionality (aka similar to what one would do with Aspect Oriented Programming).
When running the T4 template, apart from generating a new corresponding partial class with the required self-tracking supporting code, the T4 template may also make a few minor changes to your original domain object class – this is certainly not something you’d normally want to do with code generation…and before you get all excited and scream foul play, consider that it will:
  • Make your domain object class partial, if it isn’t already;
  • Add any missing KnownType attributes at the top of your class for all the distinct reference and DateTimeOffset persistent property types contained in your self-tracking class. For example, if you have a Person property in a class X, and you also have a Customer class that derives from Person, both Customer and Person KnownType attributes will be added to class X. That said, due to the limitations of EnvDTE (which I used to drive the code gen), the T4 template will ignore interfaces. If however you consider the task of manually decorating your classes with KnownType attributes as one of the true pleasures of life, then by all means don’t let me stop you from having fun - simply set the AddMissingKnownTypeAttributes global parameter in the T4 template to false.
  • Notify you via warnings in the VS studio error list pane if it made any of the abovementioned changes to your classes.
In addition, you most likely also noticed the Persist attributes. In short, the T4 template would have a really hard time determining the pair of properties in the 2 different classes that partake in the same one-to-one and one-to-many association without these attributes. The text in the attribute contains the name of the association and the T4 template will notify you via errors if it is unable to match association properties due to incorrectly used/missing Persist attributes. Finally, I’ve also decorated the properties with attributes from the Enterprise library Validation block – use of these are completely optional – feel free to use whatever framework/mechanism you prefer.
The following code block shows the other end of the “Company-Employees” association that lives in the Company class. Note the use of the OneToManyNavigationGet/Set properties instead of ManyToOneNavigationGet/Set. An important thing to realise here too is that the FixupEmployees method is generated by the T4 template – it MUST be called FixupMyPropertyName although feel free to modify/enhance the T4 template if you’re not happy with this restriction. In addition, obviously you’re going to have a compile error unless you first trigger the t4 template to generate code. I’ll stress it again…don’t forget to regenerate your code before you compile.
Code Snippet
  1. [DataMember, Persist("Company-Employees")]
  2. public TrackableCollection<Employee> Employees
  3. {
  4.     get
  5.     {
  6.         return OneToManyNavigationGet(ref _Employees, FixupEmployees);
  7.     }
  8.     private set
  9.     {
  10.         OneToManyNavigationSet("Employees", ref _Employees, value, FixupEmployees);
  11.     }
  12. }

The following table provides examples of the different property types supported by this self-tracking framework.
Property Type
Code Snippet shortcut
Primitive Property
(code snippet: tpropp)
  1. [DataMember]
  2. public string FirstName
  3. {
  4.     get { return PrimitiveGet(_FirstName); }
  5.     set { PrimitiveSet("FirstName", ref _FirstName, value); }
  6. }
Complex Object Property
(code snippet: tpropc)
  1. private Address _HomeAddress;
  2. private bool _HomeAddressInitialised;
  3.  
  4. [DataMember]
  5. public Address HomeAddress
  6. {
  7.     get
  8.     {
  9.         return ComplexGet(ref _HomeAddress,
  10.             ref _HomeAddressInitialised, HandleHomeAddressChanging);
  11.     }
  12.     set
  13.     {
  14.         ComplexSet("HomeAddress", ref _HomeAddress, value,
  15.             ref _HomeAddressInitialised, HandleHomeAddressChanging);
  16.     }
  17. }
One to Many
(code snippet: tpropotm)
  1. private TrackableCollection<Employee> _Employees;
  2.  
  3. [DataMember, Persist("Company-Employees")]
  4. public TrackableCollection<Employee> Employees
  5. {
  6.     get
  7.     {
  8.         return OneToManyNavigationGet(ref _Employees, FixupEmployees);
  9.     }
  10.     private set
  11.     {
  12.         OneToManyNavigationSet("Employees", ref _Employees,
  13.             value, FixupEmployees);
  14.     }
  15. }
Many to One
(code snippet: tpropmto)
  1. private Company _EmployedBy;
  2.  
  3. [DataMember, Persist("Company-Employees")]
  4. public Company EmployedBy
  5. {
  6.     get { return ManyToOneNavigationGet(ref _EmployedBy); }
  7.     set
  8.     {
  9.         ManyToOneNavigationSet("EmployedBy", ref _EmployedBy,
  10.             value, oneside => oneside.Employees);
  11.     }
  12. }
Root One to One (use in class considered Primary/Root in the association)
(code snippet: tproproto)
  1. private Employee _Ceo;
  2.  
  3. [DataMember, Persist("Company-Ceo")]
  4. public Employee Ceo
  5. {
  6.     get { return OneToOneNavigationGet(_Ceo); }
  7.     set
  8.     {
  9.         FromRootOneToOneNavigationSet("Ceo", ref _Ceo, value,
  10.             child => child.CeoOfCompany,
  11.             (child, newValue) => child.CeoOfCompany = newValue);
  12.     }
  13. }
Child One to One (use in class considered the “Child” in the association)
(code snippet: tpropcoto)
  1. private Company _CeoOfCompany;
  2.  
  3. [DataMember, Persist("Company-Ceo")]
  4. public Company CeoOfCompany
  5. {
  6.     get { return OneToOneNavigationGet(_CeoOfCompany); }
  7.     set
  8.     {
  9.         FromChildOneToOneNavigationSet(
  10.            "CeoOfCompany", ref _CeoOfCompany, value,
  11.            root => root.Ceo,
  12.            (root, newValue) => root.Ceo = newValue, true);
  13.     }
  14. }

Unit of Work

In order to achieve persistence ignorance, the Unit Of Work implementation hides/wraps the details of the EF4 ObjectContext class. In addition, this encapsulation also has the added benefit of making it really easy to enhance/augment the capabilities of the object context so as to fit one’s particular project needs. In my case, I’ve incorporated the following useful aspects within my Unit of Work implementation:
  • When you commit changes, all self-tracking entities will “automagically” be reset to an unmodified state. The alternative would have been for you to manually reset all entities, including child entities referenced via navigation properties in your object graph back to unmodified.
  • The unit of work provides a GetManagedEntities method that provides easy access to all entities that are currently managed/attached to the Unit of Work (i.e. the EF4 ObjectContext). This method also makes it easy to filter managed entities by their current modification status (i.e. added/deleted,unmodified/modified).
Its worth mentioning that one *could* also easily extend the Unit Of work implementation to validate all domain objects upon commit (and/or when they are being attached to the Unit of Work). This would certainly provide an easy, automatic and centralised mechanism of ensuring that business logic rules are enforced in your domain. Sounds too easy…?? Well it probably is!! Personally I won’t recommend it as I think validation logic is typically best done at a higher tier in your architecture, such as at a service layer. For example, one would typically want to validate business rules for user supplied data BEFORE commencing any expensive operations – if validation is postponed until commit, then you’ve potentially just wasted a lot of cycles and made your service more susceptible to denial of service attacks. Also consider that sometimes you may not have enough operational contextual information available at the DAL layer to to properly (and efficiently) enforce business rules as the DAL is too low in your tiered architecture to make this easy (e.g. are there workflows/notifications that need to be considered in addition to this save operation?). Still, I mention this because for simple scenarios this may be all you need – see the example below of how one could go about this.
Code Snippet
  1. public void Commit(bool acceptChanges = true)
  2. {
  3.     /* One *could* validate modified objects here as follow... although typically this is better done at
  4.      * a service level or higher business tier in your application. */
  5.  
  6.     List<ValidationResults> validationResults = new List<ValidationResults>();
  7.     foreach (var entity in GetManagedEntities<IObjectWithChangeTracker>(ObjectStateConstants.Changed))
  8.     {
  9.         var result = ValidationHelper.Validate(entity);
  10.         if(!result.IsValid)
  11.         {
  12.             validationResults.Add(result);
  13.         }
  14.     }
  15.     if(validationResults.Count > 0)
  16.     {
  17.         throw new ValidationException(validationResults);
  18.     }
  19.     
  20.  
  21.     EntitySession.Context.SaveChanges(acceptChanges ? SaveOptions.AcceptAllChangesAfterSave : SaveOptions.None);
  22.     if(acceptChanges)
  23.     {
  24.         AcceptChanges();
  25.     }
  26. }
Notice the use of the ValidationHelper class in line 9. Basically, the Enterprise library validation block only provides a generic Validate<T>(T object) method. The problem with this is that if you don’t know the type of the object you want to validate at run-time, then this validate method will be of little use as it will only consider validation attributes for the type known at compile time. For example, if T is say BasicObject, and you supply an instance of Employee – only properties in the BasicObject class will be validated. The ValidationHelper class overcomes this limitation by simply using reflection to dynamically invoke the enterprise library validation method with the correct type for T.

 

Running the Tests

To run the included unit tests, please ensure that you modify app.config in Sample.TestDataAccessLayer project with a valid sql server 2005/8 connection (SQL Express is fine).
When running the test, it will automatically drop and re-create the schema as per the mappings defined in the Sample.DataAccessLayer.EF project. The creation of the schema is controlled via the following unit test class initializer - “Test” specifies the connection string name in app.config, and true indicates that the database should be dropped/recreated as opposed to simply ensuring that it does exist.
Code Snippet
  1. [ClassInitialize()]
  2. public static void MyClassInitialize(TestContext testContext)
  3. {
  4.     new DataAccessLayerRegistrar("Test").EnsureDatabase(true);
  5. }
   
I hope this will help you getting started with this customised version of the EF Self-Tracking framework. There’s certainly a lot more I could have blogged about…but this post is getting much longer than I originally anticipated. Please let me know if there are particular features you wish to discuss in more details in future posts.
Happy coding!
Adriaan

Hello World

This is my first post to blogspot!

Is it just me, or is this layout really thin…?? I’ve done some tweaking to widen it, but I’m not sure I’m happy with it yet.