Monday, February 18, 2008

In this post I'd like to introduce version 0.1 of the first Entity Framework contribution project: Automatically implement the IPoco interfaces.
The project is aimed at helping you build your domain layer in a more persistence ignorant way, than is possible at this moment.

[official codeplex location of the project is here]

The full table of contents:

The Problem: baseclass needed

Microsoft is on the brink of releasing the Entity Framework. It is at beta 3 at this moment. If you are reading this blog, you are probably familiar with it, but let's do a quick summary:
The Entity Framework is a framework that maps between a database and your domain objects. It's grand vision is to easily allow you to (with a funky design-experience) create (multiple) conceptual models that know how to talk to the database. Although more than an OR-mapper, most people like to position it as such anyway.
EF is an abstraction layer on top of your datastore and will allow you to work with business objects that actually make sense from an object-oriented perspective, instead of making you work with datarows, tables and sets.

One part of the criticism that the Entity Framework gets at this moment, is the lack of persistence ignorance. This means that, when you use the Entity Framework, you will have to create business entities that are aware of the Entity Framework (they need to derive from a Entity Framework baseclass).
This goes against too many principles to mention, and the ADO.Net team have gotten quite a bit of comments about it (other more mature frameworks, like nHibernate do not force you into this). Rightfully so!
In the end, Daniel Simmons blogged about the criticism here: Persistence Ignorance: OK, I think I get it now.

The suggested Solution: implement interfaces

In order to take away the need to implement a base-class, the EF-team created a few interfaces that need to be implemented. That is as far as they can go in the first release.

So, you can implement 3 interfaces on your business objects, and no baseclass is needed. 
Although much better, I feel I should not have to spend time on, or burden my domain layer with, code to facilitate data access. My domain layer should be able to focus on one thing: solving the business problems of the client.
By introducing other code to my domain layer, developers will be distracted.

Bill McCafferty posts about DDD (Domain Driven Design) and EF here. He concludes:

In short, and at the risk of being laconic, I feel that the ADO.NET Entity Framework does for data communications what the ASP.NET page life cycle did for the presentation layer.  In trying to introduce simplification and increased productivity, it's actually going to result in higher complexity and decreased maintainability in the long run.  I appreciate what Microsoft is trying to do, and absolutely love some of their other ideas, but, for now, I'm going to pass on the ADO.NET Entity Framework.

Billy McCafferty

He is quite right!!

EF-Contrib: Easing the implementation of these interfaces

The 3 interfaces we are talking about are:

  • IEntityWithChangeTracker
  • IEntityWithKey
  • IEntityWithRelationships

Implementing these interfaces is sometimes called "IPoco": Poco stands for Plain Old C# (or Code) Object, and the I in IPoco means that you can still use your Poco object but have to implement these interfaces. (so, not Poco at all... but still!)

The current checked in project (find it here) uses Postsharp to actually change the IL-code of your assembly and implements these interfaces. That means that you can build a domain layer with a class like this:

    [Poco("ConnectionName")]
    public class Person
    {
        public int PersonID { get; set; }
        public string Firstname { get; set; }
        public string Lastname { get; set; }
    }

After compilation, the class will actually look a bit different on disk:

    public class Person : IEntityWithChangeTracker, IEntityWithKey, IEntityWithRelationships
    {
...
    }

So you can use this Person class, like you would use the classes that EF generates.

It is important to understand that there will be very little runtime performance costs involved. The code transformation is done at compile-time, once. At runtime, there is no magic AOP or whatever involved.

This approach is used by several other OR-Mappers and is very common in the Java world.

Is this Persistence Ignorance?

Obviously, it's not. Hopefully, in version 2.0 of the Entity Framework, full ignorance is achieved. However, if you want to use EF at your datalayer today, this approach will let you focus on the important stuff, instead of data access code.

Imagine changing your conceptual model. When implementing IPoco yourself, you will have to take care to change all kinds of attributes on top of your properties. This will quickly become a burden.

How does it work?
  • You will need to download and install Postsharp on all the machines that will build your application (developer machines and teambuild machine(s)).
  • Your domain layer will have to reference the EntityFrameworkContrib.PostSharp4EF assembly, and the PostSharp.Laos and PostSharp.Public assemblies. By referencing these, Postsharp will know to do a post-compilation phase on your assemblies.
  • You will need to supply a 'psproj' file in your assembly, to let our attribute know where it should look to actually do the implementation. This allows me to seperate the implementation assembly from what you need at runtime!
  • You have already created your edmx file, which EF will dissect into the individual .csdl, .msl and .ssdl files and place them in your bin/debug folder.
  • The project for now assumes a connection string to be present in your app.config
  • You can create your own simple business object.
  • That connection string is needed during the postcompilation phase to get to the individual mapping files, so use the attribute [Poco("")] to let us know you need to change this class.
  • The interfaces are implemented and the setters of your properties are modified to actually do changetracking
  • Actually, at this moment: INotifyPropertyChanged is implemented as well (let me know if you actually want this).

So, let's first look at the psproj file you need. In the Test-project, there is one already:

<Project xmlns="http://schemas.postsharp.org/1.0/configuration">
	<SearchPath Directory="../EntityFrameworkContrib.PostSharp4EF.Weaver/bin/{$Configuration}"/>
	<SearchPath Directory="{$SearchPath}" />
	<Tasks>
		<AutoDetect />
		<Compile TargetFile="{$Output}" IntermediateDirectory="{$IntermediateDirectory}"  CleanIntermediate="false" />
	</Tasks>
</Project>

The referenced assembly EntityFrameworkContrib.PostSharp4EF only defines the Poco attribute, but does not contain the actually 'code-weaving'. If we would have placed the code-weaving in the same assembly as the Poco-attribute, you would have a much larger assembly to reference and you could get into licensing problems. By separating them, you only need to reference a tiny assembly.

The weaving assembly should not be distributed with your final product!

However, during the build, PostSharp does need to find the weaving assembly. Therefor, you need to create a psproj file that extends it's normal searchpath to also include the weaving dll.
Take care in naming the file: it should be named "projectname.psproj".

When the project is more mature, you might find it best to actually just place the weaving assembly into one of the default searchpaths for postsharp to find, and you will not need this psproj file.

Now, let's look at our attribute:
In it's constructor, it takes the name of the EDMcontainer, which should match your connection string. I have also added a few properties: Name, NamespaceName, PathToConfigFile. I'll get back to these in a later post. In the future, others will be added.

During the weaving, I have to do quite a bit of work to actually get to the correct mapping files. So, I try to load in your app.config and extract the file path's from it. The Testproject has the following app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="OneSimpleTypeConnection" connectionString="metadata=.\bin\debug\OneSimpleType\OneSimpleType.csdl|.\bin\debug\OneSimpleType\OneSimpleType.ssdl|.\bin\debug\OneSimpleType\OneSimpleType.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=VISTAX64\SQLEXPRESS;Initial Catalog=EntityFrameworkTest;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
</configuration>

So, after loading that app.config, I use the supplied ConnectionContainer to get that connectionstring, and then use some simple regex work to get the path's to the mapping files. Then I try to load these to create a MetadataWorkspace.

When I finally have a MetadataWorkspace, stuff get's easier: I can iterate the properties in our original class and find the property in the metadataworkspace. Then I create the correct EDMScalar Attributes on top of those.

Implementing the interfaces is done by PostSharp, where it will look at an interface and just use a class I provide to call when one of the interface methods is called.

The result

Let's look through reflector at how the end result looks like. I won't show the methods, to keep things short and sweet.

  1 [EdmEntityType(Name="Person", NamespaceName="EntityFrameworkContrib.PostSharp4EF.Tests.OneSimpleType")]
  2 public class Person : INotifyPropertyChanged, IComposed<INotifyPropertyChanged>, IProtectedInterface<IFirePropertyChanged>, IPocoFacade, IComposed<IPocoFacade>
  3 {
  4     // Fields
  5     private IPocoFacade ~EntityFrameworkContrib.PostSharp4EF.IPocoFacade;
  6     private readonly InstanceCredentials ~instanceCredentials;
  7     private INotifyPropertyChanged ~System.ComponentModel.INotifyPropertyChanged;
  8     [CompilerGenerated]
  9     private string <Firstname>k__BackingField;
10     [CompilerGenerated]
11     private string <Lastname>k__BackingField;
12     [CompilerGenerated]
13     private int <PersonID>k__BackingField;
14
15     // Methods
16     static Person();
17     public Person();
18     void INotifyPropertyChanged.add_PropertyChanged(PropertyChangedEventHandler value);
19     EntityKey IEntityWithKey.get_EntityKey();
20     RelationshipManager IEntityWithRelationships.get_RelationshipManager();
21     void INotifyPropertyChanged.remove_PropertyChanged(PropertyChangedEventHandler value);
22     void IEntityWithKey.set_EntityKey(EntityKey value);
23     void IEntityWithChangeTracker.SetChangeTracker(IEntityChangeTracker changeTracker);
24     protected InstanceCredentials GetInstanceCredentials();
25     [DebuggerNonUserCode]
26     IPocoFacade IComposed<IPocoFacade>.GetImplementation(InstanceCredentials credentials);
27     [DebuggerNonUserCode]
28     INotifyPropertyChanged IComposed<INotifyPropertyChanged>.GetImplementation(InstanceCredentials credentials);
29     [DebuggerNonUserCode]
30     IFirePropertyChanged IProtectedInterface<IFirePropertyChanged>.GetInterface(InstanceCredentials credentials);
31
32     // Properties
33     [EdmScalarProperty(IsNullable=true)]
34     public string Firstname { [CompilerGenerated] get; [CompilerGenerated] set; }
35     [EdmScalarProperty(IsNullable=false)]
36     public string Lastname { [CompilerGenerated] get; [CompilerGenerated] set; }
37     [EdmScalarProperty(IsNullable=false, EntityKeyProperty=true)]
38     public int PersonID { [CompilerGenerated] get; [CompilerGenerated] set; }
39 }
40
41

Line 1 implements the needed EntityType attribute for EDM to work.
Line 2 shows that INotifyPropertyChanged and IPocoFacade is implemented. The facade interface just hides the 3 IPoco interfaces, so that's them! PostSharps adds IComposed interfaces as well.
Line 26 shows a call to the GetImplementation method of that interface. This way, a class I have added is returned where the actual work of the interface is done.
Line 33, 35 and 38 show the EDMScalarProperties being set.

What it does not do at this moment

I do not set default values for fields and I haven't spend any time on complex types and relations.

I first want to gauge community interest before spending more time on this project. So let me know if you would use this approach if it would be complete. I'm quite sure these things aren't too hard to accomplish, but they will take some time.

The Future

I'd like the EntityFramework Contrib project to provide easy tools to use EF in an enterprise system. I'm mostly interested in client/server SOA solutions. Other projects that might help in that aspect:

  • A custom changetracker that can be used on the client. This way the client will not have to reference Entity Framework at all.
  • Better serialization possibilities. Note that I do not automatically place datacontract attributes on top of the properties. I think it was a mistake for the ADO.Net team to implement their codegen to do this. (although I understand why).
    When I serialize a EF entity at this moment, I see all kinds of references to EF in the xml. I do not like that, and would like a beautiful clean xml representation of my business objects. (I don't want to be forced to use DTO's.).
  • Serializing original values. I can see a representation of the value with a xml attribute that shows what the original value was.

 

Feel free to contact me, or leave a comment here or at the projects home to let me know if you are interested!

kick it on DotNetKicks.com

Tuesday, May 27, 2008 7:05:21 PM (Romance Standard Time, UTC+01:00)
Hi,

It is a very interesting use of PostSharp. But, Isn't more simple to develop an addin that takes care at "design time" of the plumbing, by generating code? I am not talking about Custom tools. It is far more simpler from testing and development point of view, to generate code than to use reflection emit and low level stuff in order to achieve the same thing.
Liviu Uba
Tuesday, May 27, 2008 7:17:44 PM (Romance Standard Time, UTC+01:00)
That would mean you would have to keep track of that generated code, manage it, version it.. etc..
Also, you would have to actually design your architecture around the generated code. That is not smart.
Look for more reasons to the AOP community. In many cases there is much to be said against generated code.
Ruurd
Thursday, August 14, 2008 8:34:41 PM (Romance Standard Time, UTC+01:00)
Hi, very good work
I am working on multi-language web applications. How can your tool help me write multi-language business entities. for example I like to have a News entity in English, Arabic, Hebrow, etc languages. but have just one entity such that I can load, save, edit and delete the entity in each of the languages, ie. load news 1 in English, load news 1 in Arabic, delete Hebrow version of news 1 ,etc.
Thanks
Javad
Javad
Wednesday, August 20, 2008 2:33:38 PM (Romance Standard Time, UTC+01:00)
You should model your business entities such that this is an innate capability.
How you would go about this, is up to you. My tool will not help you with this explicitly, it is only meant to make working with classes simpler! ;-)
Ruurd
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):