Linq to Sql – How-to Separate the entities and the DataContext.

Updated See comments for details.

Yesterday we started to use Linq to Sql in a project at work. We didn’t select NHibernate but kind of wanted a clean model anyway, where the entities are separated and not bundled in the same class as the context. We also wanted clean entities: No partial methods; No (by us) unused INotify-interfaces. We also wanted the DataContext to live in an own assembly, so it’s not included in the assembly that holds the entities. Ok, so whith al these demands you probably think that NHibernate would be a better choice, well it wasn’t that hard to accomplish a solution that fullfilled our needs, and that’s what this post is about.

Demands:

  • Clean Entities (No partial methods, INotify-interfaces etc)
  • DataContext and Entities should live in different assemblies
  • No repositories, but one EntityStore, that uses a generic DataContext.

For this article I put together an example app, which is composed in VS2010. You can download it here. The example app has the following projects:

Some short notes about the projects:

  • Pls.Core: Should be seen as Microsoft-system lib. The code here has no coupling to a certain Domain.
  • Pls.Blog: Holds commonly shared resources within the application being built. E.g: Resources, Interface-definitions, Exceptions etc. In our case, nothing more than the IoC-container.
  • Pls.Blog.Domain: Holds code that are specific to the domain.
  • Pls.Blog.IoC.Configurations: Holds configuration used to bootstrap my StructureMap-based IoC-container.
  • L2SqlGenerator: Only used for generating the initial entities.

If I would have had specific EntityStore-implementations in the project, these would live in a own assembly: Pls.Blog.Storage. Since we are satisfied with the generic L2SqlEntityStore found in Pls.Core, this project is not needed.

T4-templates to the rescue

Damien Guard has put together some T4-templates (read more) that could be used to take control of how the DataContext and the entities are generated. All I did was to create a project that only has the responsibility of holding the DBML, and to make use of the T4-templates to generate the entities (it will also generate the DataContext, but I don’t care about that). These entities are then moved to the Domain-project and with a little work with ReSharper, the entities looks kind of slick. So the generated code is only used as a boilerplate. Of course you can tweak the T4-template to generate “perfect” code directly.

The solution

Step 1 – Download the templates
The templates are located at CodePlex: http://l2st4.codeplex.com/

You need two files from the ZIP:

  • L2ST4.ttinclude
  • [The language you are using, c# or VB.Net].tt

Step 2 – Setup a Generation project
I want to be able to work with the DBML-file and to customize the TT-file without affecting the application. Due to this I created an “dummy” project in my solution, named: “L2SqlGenerator”.

Add an DBML-file to this generation project. Then ensure that “Build Action = None” and that “Custom Tool = “.

Include the T4-files. Open the TT-file and check the options that Damien have included. I switched “FilePerEntity” to true and implemented a couple of my own options: [GenerateINotify, GenerateExtensibilityMethods, UseRegions].

Now add the tables and adjust the namespace for the entities in the DBML (property “Entity Namespace”, e.g Pls.Domain.Entities). Modify the model as you want: Naming of entities; Hiding of FK-properties etc. Whatever you want.

The DataContext and the entities should have been generated for you and lies beneath the TT-file. If they haven’t been generated/regenerated, right-click on the TT-file and select “Run Custom tool”.

Step 3 – Put the entities where they belong
Extract the entities and place them where you want them. In the example I have put them in “Pls.Domain.Entities” which lies in the “Pls.Domain-assembly”.

Pls.Blog.Domain.Entities.Entry

[Table(Name = @"dbo.BlogEntry")]
public class Entry
{
    private EntityRef<BlogContext> _blog;

    public Entry()
    {
        _blog = default(EntityRef<BlogContext>);
    }

    [Column(Name = @"Id", AutoSync = AutoSync.OnInsert, DbType = @"Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true, UpdateCheck = UpdateCheck.Never)]
    public int Id { get; set; }

    [Column(Name = @"BlogId", DbType = @"Int NOT NULL", CanBeNull = false)]
    private int BlogId { get; set; }

    [Column(Name = @"Title", DbType = @"NVarChar(100) NOT NULL", CanBeNull = false)]
    public string Title { get; set; }

    [Column(Name = @"Preamble", DbType = @"NVarChar(500)")]
    public string Preamble { get; set; }

    [Column(Name = @"Body", DbType = @"NVarChar(MAX) NOT NULL", CanBeNull = false)]
    public string Body { get; set; }

    [Association(Name = @"Blog_BlogEntry", Storage = @"_blog", ThisKey = @"BlogId", OtherKey = @"Id", IsForeignKey = true)]
    public BlogContext BlogContext
    {
        get
        {
            return _blog.Entity;
        }
        set
        {
            BlogContext previousValue = _blog.Entity;
            if ((previousValue != value) || (!_blog.HasLoadedOrAssignedValue))
            {
                if (previousValue != null)
                {
                    _blog.Entity = null;
                    previousValue.BlogPosts.Remove(this);
                }
                _blog.Entity = value;
                if (value != null)
                {
                    value.BlogPosts.Add(this);
                    BlogId = value.Id;
                }
                else
                {
                    BlogId = default(int);
                }
            }
        }
    }
}

Step 4 – Create a generic EntityStore
I like to have the actual “provider” that uses the entities to move data back and forth between the database in a sepparate assembly, e.g. “Pls.Storage”. In that assembly I add a namespace for the current provider-type, e.g. “L2Sql”. In this example app, I didn’t need it, since I’m using StructureMap, I could easily let the L2SqlEntityStore recide in Pls.Core.

Pls.Core.Storage.IEntityStore and Pls.Core.Storage.L2Sql.IUnitOfWork

public interface IEntityStore : IDisposable
{
    void EnsureCleanDatabase();

    IUnitOfWork<T> CreateUnitOfWork();

    IQueryable<T> Query<T>(Expression<Func<T, bool>> expression)
        where T : class;
}

public interface IUnitOfWork : IDisposable
{
    void Insert<T>(T entity) where T : class;
    void Update<T>(T entity) where T : class;
    void Delete<T>(T entity) where T : class;

    IQueryable<T> Query<T>(Expression<Func<T, bool>> expression) where T : class;

    void SaveChanges();
}

Thats the definition of the EntityStore-concept. Kind of simple. The implementation is not that hard either.

Pls.Core.Storage.L2Sql.L2SqlEntityStore

public class L2SqlEntityStore
    : IEntityStore
{
    protected static readonly object ContextLock = new object();

    private readonly MappingSource _mappings;

    public virtual string ConnectionString { get; private set; }
    protected virtual DataContext Context { get; private set; }

    public L2SqlEntityStore(string connectionString, MappingSource mappings)
    {
        _mappings = mappings;
        ConnectionString = connectionString;
        Context = CreateNewContext(_mappings);
    }

    public virtual void EnsureCleanDatabase()
    {
        lock (ContextLock)
        {
            if (Context.DatabaseExists())
                Context.DeleteDatabase();

            Context.CreateDatabase();
        }
    }

    public virtual IUnitOfWork CreateUnitOfWork()
    {
        var context = CreateNewContext(_mappings);

        return new L2SqlUnitOfWork(context);
    }

    public virtual IQueryable<T> Query<T>(Expression<Func<T, bool>> expression = null) where T : class
    {
        var table = GetTable<T>();

        return expression != null ? table.Where(expression) : table;
    }

    protected virtual Table<T> GetTable<T>() where T : class
    {
        return Context.GetTable<T>();
    }

    protected virtual DataContext CreateNewContext(MappingSource mappings)
    {
        return new DataContext(ConnectionString, mappings);
    }

    #region Object lifetime, Disposing
    ...
    #endregion
}

Pls.Core.Storage.L2Sql.L2SqlUnitOfWork

public class L2SqlUnitOfWork
    : IUnitOfWork
{
    protected readonly object ContextLock = new object();
    protected DataContext Context { get; private set; }

    public L2SqlUnitOfWork(DataContext context)
    {
        Context = context;
    }

    public virtual void Insert<T>(T entity)
        where T : class
    {
        var table = GetTable<T>();
        table.InsertOnSubmit(entity);
    }

    public virtual void Update<T>(T entity)
        where T : class
    {
        var table = GetTable<T>();
        table.Attach(entity, true);
    }

    public virtual void Delete<T>(T entity)
        where T : class
    {
        var table = GetTable<T>();
        table.DeleteOnSubmit(entity);
    }

    public virtual IQueryable<T> Query<T>(Expression<Func<T, bool>> expression)
        where T : class
    {
        return GetTable<T>();
    }

    public virtual void SaveChanges()
    {
        Context.SubmitChanges();
    }

    protected virtual Table<T> GetTable<T>()
        where T : class
    {
        return Context.GetTable<T>();
    }

    #region Object lifetime, Disposing
    ...
    #endregion
}

The actual Linq to Sql implementation of an EntityStore needs information about the mappings. The only support I have included, is support for Attribute-based mapping. You could of course use Xml-to. The mappings are resolved by looking for types that has the custom attribute “TableAttribute”. Instead of going throw all assemblies I pass in a prefix, so that only assemblies that has a name matching this prefix, will be scanned for types. This is controlled in the IoC-container.

Pls.Core.Storage.L2Sql.MappingSourceResolver

public MappingSource GetMappings(string assemblyPrefix)
{
    var mappings = new AttributeMappingSource();
    var model = mappings.GetModel(typeof(DataContext));
    var assemblies = AppDomain.CurrentDomain.GetAssemblies()
        .Where(a => a != null && a.FullName != null && a.FullName.StartsWith(assemblyPrefix));
    var mappingsFound = false;

    foreach (var assembly in assemblies)
    {
        var types = assembly.GetTypes()
            .Where(t => t.GetCustomAttributes(typeof(TableAttribute), true).Length > 0);

        foreach (var type in types)
        {
            model.GetMetaType(type);
            mappingsFound = true;
        }
    }

    if(!mappingsFound)
        throw  new DataException(string.Format("No attributemappings where found in any assembly with a name matching the prefix \"{0}\".", assemblyPrefix));

    return mappings;
}

Step 5 – Wire things up in the IoC-container
I have an IoC-container that is explicit for the application being built. The only thing it does is specifying an assembly that holds the bootstrap info.

Pls.Blog.IoC

public class BlogIoCContainer
    : StructureMapIoCContainer
{
    private readonly static string ConfigurationNamespace = typeof(BlogIoCContainer).Namespace + ".Configurations";

    public static IIoCContainer Instance
    {
        get { return Singleton<BlogIoCContainer>.Instance; }
    }

    protected override void BootstrapContainer()
    {
        Container.Configure(x => x.Scan(scanner =>
        {
            scanner.Assembly(ConfigurationNamespace);
            scanner.LookForRegistries();
        }));
    }
}

The configurations are really simple. I create a MappingSource by looking at assemblies having a name starting with “Pls.Blog”, hence the classes under “Pls.Blog.Domain.Entities” will be scanned and registered. The MappingSource is registrered as a singleton resource and the IEntityStore is mapped to the Linq to Sql implementation.

Pls.Blog.IoC.Configurations

[Serializable]
public class EntityStoreRegistry
    : Registry
{
    public EntityStoreRegistry()
    {
        Bootstrap();
    }

    private void Bootstrap()
    {
        var mappingsResolver = new MappingSourceResolver();
        var mappings = mappingsResolver.GetMappings("Pls.Blog");

        For<MappingSource>()
            .Singleton()
            .Use(mappings);

        For<IEntityStore>()
            .LifecycleIs(Lifecycles.GetLifecycle(InstanceScope.PerRequest))
            .Use<L2SqlEntityStore>()
            .Ctor<string>("connectionString").Is(ConfigurationManager.ConnectionStrings["Pls.Blog"].ConnectionString);
    }
}

Step 5 – Use the instrastructure
I have created a simple domain-object that has the responsibility to write entries to the blog. It uses the injected EntityStore to get a UnitOfWork and to add the entry. For brewity, I have left out validation.

Pls.Blog.Domain.BlogWriter

public class BlogWriter
    : IBlogWriter
{
    public IEntityStore EntityStore { protected get; set; }

    public BlogWriter(IEntityStore entityStore)
    {
        EntityStore = entityStore;
    }

    public virtual void WritePost(Entry entry)
    {
        using (var uow = EntityStore.CreateUnitOfWork())
        {
            uow.Insert(entry);
            uow.SaveChanges();
        }
    }
}

The BlogWriter is created via a DomainFactory. So the consumer code doesn’t go directly to the IoC-container, but via this factory. The DomainFactory uses the IoC-container, which is configured in the Pls.Blog.IoC.Configurations.DomainRegistry class.

Pls.Blog.Domain.DomainFactory

public class DomainFactory
    : FactoryBase
{
    public virtual IBlogInstaller CreateBlogInstaller()
    {
        return IoCContainer.GetInstance<IBlogInstaller>();
    }

    public virtual IBlogQuery CreateBlogQuery()
    {
        return IoCContainer.GetInstance<IBlogQuery>();
    }

    public virtual IBlogWriter CreateBlogWriter()
    {
        return IoCContainer.GetInstance<IBlogWriter>();
    }
}

The client

class Program
{
    static void Main(string[] args)
    {
        var domainFactory = new DomainFactory();

        var installer = domainFactory.CreateBlogInstaller();
        installer.SetupEntityStore();

        var blog = new BlogContext
                   {
                       Name = "The Blog", 
                       Owner = new Owner
                               {
                                   Username = "theuser", 
                                   Password = "p@ssword", 
                                   Firstname = "Daniel", 
                                   Lastname = "Wertheim", 
                                   Email = "none@none.xom"
                               }
                   };

        var writer = domainFactory.CreateBlogWriter();
        writer.WritePost(
            new Entry
            {
                BlogContext = blog,
                Title = "Normal",
                Preamble = "Check this out!",
                Body = "This is normal!"
            });

        var query = domainFactory.CreateBlogQuery();
        var post = query.Where(bp => bp.Title == "Normal").SingleOrDefault();

        Console.Out.WriteLine("Blog = {0}", post.BlogContext.Name);
        Console.Out.WriteLine("Owner = {0}", post.BlogContext.Owner.Name);
        Console.Out.WriteLine("Title = {0}", post.Title);

        Console.ReadKey();
    }
}

That’s it. May have looked a bit tricky, but download the code and see for yourself, it really isn’t that hard.

//Daniel

About these ads

10 thoughts on “Linq to Sql – How-to Separate the entities and the DataContext.

  1. Will update this story and code when I get home. IUnitOfWork should define the generic type on each method instead.

    //Daniel

    • Just updated the article and the code-sample. The UnitOfWork now has the generic-type on each method instead.

      //Daniel

  2. Pingback: DotNetShoutout

  3. I will update it again, but for now I announce the changes like this:
    – Delete should also attach
    – Attach as modified needs Row Version.

    [Column(
        Name = @"Version",
        AutoSync = AutoSync.Always,
        DbType = @"rowversion NOT NULL",
        CanBeNull = false,
        IsDbGenerated = true,
        IsVersion = true,
        UpdateCheck = UpdateCheck.Never)]
    public Binary Version { get; set; }
    

    //Daniel

  4. Why you choose factory pattern for IoC here as compared to
    ServiceEngine.Instance.IoC.Resolve(IEntityStore))

    what are the benafit with this new changes?

    • Hi,
      Even though I have a IoC-container, I like to have a factory that gets named and somewhat “states” what could be returned, as opposed to the IoC-container, which is completely dynamic.

      //Daniel

  5. Thanks for the nice details of how to use T4 templates. I was trying out the sample and kept hitting the detached entities while performing updates on the object graph. Any idea how this can be over come.
    Thanks,

    • Hi,
      There’s a comment on this for deletions. Updates are the same. You need to attach the item to the context first. You could of course refetch it and update the refetched item.

      //Daniel

  6. Hi,

    I’ve moved the solution over to VS2008 .Net 3.5 however I get an error on the following:

    public virtual IQueryable Query(Expression<Func> expression = null) where T : class

    Could you recommend how to change this the be .NET 3.5 compatible?

    Thanks
    Gary

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s