Aspen – A sample app using Silverlight 4 and .Net 4.0 – part 2 of X – MEF and EF 4 code-only
Note: The code examples in this blog post will be based on technical previews and betas of .Net 4.0 and VS2010. So changes can be made before it hits RTM.
If you haven’t read the first part about the Aspen project, you can find it here.
In this part I will focus on the Entity Framework 4 CTP2
Code-only feature and the use of POCOs. To import some
mapping configuration I have used MEF. I’m only working on
the skeleton on the backend at the moment. What I have done
so far is adding the first Entity, the Member! We want to
use a POCO for the Entity and here is the code for the
Member entity. No references to the EF assemblies, no
annotations, no inherits or implementation of interfaces,
just a plain old CLR object (POCO):
public class Member { public int ID { get; internal set; } public string FirstName { get; set; } public string LastName { get; set; } }
This Entity is far from completed we will add more properties later, but now I wanted to test the new code-only support, so I just added a few properties. The Entity exists in its own Class Library where we will add our Domain Model. I haven’t implemented a Repository for this Entity yet. I will be done when I’m satisfied with the code-only part and helper classes.
When using POCO we need to create our own ObjectContext. I
have created a simple one called AspenObjectContext. This
class will later support more entities, but at the moment it
will only have one, the Member. Here is the code for the
AspenObjectContext:
public class AspenObjectContext : ObjectContext { public AspenObjectContext(EntityConnection conn) : base(conn) { } public ObjectSet<Member> Members { get { return base.CreateObjectSet<Member>(); } } }
To create an ObjectContext we need to inherit the
ObjectContext class from the System.Data.Objects namespace
(System.Data.Entity.dll). The constructor will take an
EntityConnection and also have a getter for accessing the
ObjectSet of the Member entity. The AspenObjectContext is
located in its own Class Library, at the moment together
with the Repositories for the entities (which is not created
yet ;)). I will see if I will keep the AspenObjectContext in
the same assembly as the Repositories. The reason why I have
placed it with the Repositories now is because they are
going to use the AspenObjectContext. I will see if I will
try to follow the Dependency Inversion Principle here, so
the Repositories will not be dependent on the
AspenObjectContext, instead of an abstraction (interface).
In this case the app will only use Entity Framework 4 and
that will not be changed. You can of course send me comments
about what you would prefer etc.
When using POCO we can use a EDMX for the mapping, but I
decided to use the new code-only feature. When using the
code-only feature we can for example create an
EntityConfiguration class for the Entity, where a fluent-API
can be used to configure the mapping between the database
and the entity. Here is the mapping class for the Member
entity:
[Export(typeof(StructuralTypeConfiguration))] class MemberMappingConfiguration : EntityConfiguration<Member> { public MemberMappingConfiguration() { base.Property(m => m.ID).IsIdentity(); base.MapSingleType( m => new { ID = m.ID, FirstName = m.FirstName, LastName = m.LastName } ).ToTable("dbo.Members"); } }
By using the Property method of the EntityConfiguration class we can for example specify if a specific property should be the identifier. In this case the ID property of the Member entity. The MapSingleType is used to map the database columns (first property in the anonymous type) to the Member’s properties. The ToTable will tell which table the Member entity should map against, in this case the Members table.
Note: I often create the Domain Model first and
test it before I focus on the database. But in this case
I needed to test the code-only feature, so I created the
database and added a table with the name Members. I’m
thinking of renaming the table to Member instead. What
is your comments on the naming of tables?
The EntityConfiguration must be registered somehow so the
AspenObjectContext can use the code-only mapping. This is
done by using the ContextBuilder class and it’s
Configurations property. In the future there will be several
of EntiyConfiguration classes for each Entity, so I decided
to use MEF to import the configurations. That is the reason
why the [Export(typeof(StructuralTypeConfiguration))] is
added to the MemberMappingConfiguration class. The
configuration classes are added to their own class library.
I want to use MEF to separate the configuration from the
AspenObjectContext assembly. This is the part I would really
like to have some comments on, how should you have done it
etc. I decided to create a AspenObjectContextFactory class
to create the AspenObjectContext instance and also use the
ContextBuilder to configure the ObjectContext and the
mappings. Here is the code of the AspenObjectContextFactory
and where MEF is used to import the EntityConfiguration
classes:
public class AspenObjectContextFactory { [ImportMany(typeof(StructuralTypeConfiguration), AllowRecomposition=true)] private List<StructuralTypeConfiguration> _configurations = null; public AspenObjectContext Create(DbConnection con) { if (_configurations == null) this.Compose(); var builder = new ContextBuilder<AspenObjectContext>(); if (_configurations != null) _configurations.ForEach(c => builder.Configurations.Add(c)); return builder.Create(con); }
private void Compose() { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var catalogParts = new AggregateCatalog(); foreach (var assembly in assemblies) { if (assembly.FullName.Contains("EntityMapping")) catalogParts.Catalogs.Add( new AssemblyCatalog(assembly)); } var container = new CompositionContainer(catalogParts); container.ComposeParts(this); } } }
The ContextBuilder’s Create method will create the AspenObjectContext, and the method needs a connection so the DbConnection base class is used as the type of the Create method’s argument. I’m planning to use Untiy as the IoC Container to inject dependencies (or maybe stay with MEF only), but aren’t there yet. The _configurations List has the MEF’s ImportMany attribute to imports the EntityConfiguration classes. The reason I use the StructuralTypeConfiguration as the contract for the Import and Export, is because I don’t want to use a string, and the EntityConfiguration is a generic class. The EntityConfiguration inherits from the StructuralTypeConfiguration class and the ContextBuilder’s Configurations’s Add method takes a StructuralTypeConfiguration (The Add method is used to add the configuraitons to the ContextBuilder).
The Compose method will only be called when the _configuration is null for doing the composition. The Compose method will get all assemblies loaded to the AppDomain and will only add the Assemblies with the name “EntityMapping“. In this case I wanted to avoid configuration so I decided to use convention. I could just Load the assembly with the EntityConfigurations, but I decided to locate it without any specific configurations (In this case a referenced is needed. But I could have used configuration. Instead just specify the path to the mapping configuration assembly in a config file. By doing so I could easily replace the assembly and no reference is needed). I can now add other assemblies with the name EntityMapping, which can have some new mappings against new entities etc. I would really like to have some comments on this solution also´, and have in mind this is still under development and changes can be made to the code. I only put something together for trying the code-only feature.
Here is the code for using the AspenObjectContext (The code
will only insert new Members):
var aspenContext = new AspenObjectContextFactory().Create( ConfigurationManager.ConnectionStrings["AspenConnectionString"].ConnectionString)) aspenContext.Members.AddObject(new Member() { FirstName = "John", LastName = "Doe" }); aspenContext.SaveChanges(); aspenContext.Dispose();
If you have any comments on this first part and also
want to see some specific feature, please let me know. I
would be glad if you can come with some suggestions.
If you want to know when I publish more posts about this app and the progress, you can follow me on twitter: http://www.twitter.com/fredrikn