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.

image


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

2 Comments

  • Nice post Frederik. I like the way you used MEF as plumbing for your infrastructure.

    Glenn


  • @Damien Guard:
    Yes, I'm aware of it, but I wanted to make it clear how the mapping with code-only works.. This is a reference project and by removing the MapToSingelType etc will not show any kind of mapping at all, so people wouldn't know about some basic mapping things. The Member class will be changed in the future.

Comments have been disabled for this content.