Designing loosely coupled Entity Framework based applications

The main purpose of designing loosely coupled applications is to reduce the dependency between the components of the system. This provides more flexibility, maintainability, modularity that are very important especially in long-term projects.

Dependency inversion

To achieve this it is highly recommended to follow the dependency inversion principle (one of
the SOLID principles):

  • High level modules should not depend upon low level modules. Both should depend on abstractions.
  • Abstractions should not depend upon details. Details should depend upon abstractions.

image

In practice this means that a class should not depend directly on another class (detail) but on an interface (abstraction). The TopComponent has a member (socket) that expects any component that implements the ILowComponent interface. In this way, the TopComponent does not depend on the LowComponent. The ILowComponent is defined by the top component, so actually the low component depends on the top component (inversed dependency). This programming technique is usually referred as component-based software engineering. In this blog post I will introduce a loosely coupled architecture for Entity Framework based applications that uses this method.

The demo application

The demo application manages the content of a database with a very simple scheme: a single table with a few data fields and an auto-generated primary key. I created a new Entity Framework model and imported the database schema.

image

Components

The main part of the demo application is a simple data component, that manages the content of the database. It can be described with the following interface:

public interface IDataService
{
    Person GetPerson(int id);

    void StorePerson(Person person);

    void DeletePerson(Person person);
}

How should be the GetPerson method implemented in a RAD project with the use of Entity Framework. This is a possible solution:

public Person GetPerson(int id)
{
    if (id < 0)
    {  
         return new Person();
    }

    using (ClubEntities context = new ClubEntities())
    {
        return context.People.Single(p => p.Id == id);
    }
}

There are multiple problems with this implementation:

  • Smaller problem: the component completely depends on Entity Framework.
  • Bigger problem: the initialization of the ClubEntities object context class is baked into the implementation of the component, so there will be no easy way to alter it later (imagine how many times the new ClubEntities() expression could be written is a large application).

The former can be solved by disabling the code generation of the EDM and implementing the entity classes in a plain old way and the object context class with component-based approach. This would need much work if there were many tables in the database, so instead I will apply this POCO T4 template to alter the code generation. I also modified it a little bit to make it generate a interface for the object context class. So I got this generated code:

public partial class Person
{
    public virtual int Id { get; set; }
    
    public virtual string Name { get; set; }
    
    public virtual string Title { get; set; }
}

public partial interface IClubEntities : IDisposable
{
    IObjectSet<Person> People { get; }
    
    ObjectStateManager ObjectStateManager { get; }
    
    int SaveChanges();
}

public partial class ClubEntities : ObjectContext, IClubEntities
{
   // Self-evident implementation
}

It surely could have been done in an even more ORM independent way, but this will be enough now.

The latter problem can be solved by decoupling the functionality: the data component should not instantiate the object context class, this role should belong to a separate component. Let this component be a factory component whose interface can be defined like this:

public interface IObjectContextFactory
{
    IClubEntities Create();
}

This is everything we need to be able to rewrite the implementation of the data service component.

public class DataService : IDataService
{
    private IObjectContextFactory contextFactory;

    public DataService(IObjectContextFactory contextFactory)
    {
        this.contextFactory = contextFactory;
    }

    public Person GetPerson(int id)
    {
        if (id < 0)
        {  
            return new Person();
        }

        using (ClubEntities context = this.contextFactory.Create())
        {
            return context.People.Single(p => p.Id == id);
        }
    }
}

The object context factory component is a member of the data service component and can be set through its constructor. The query method instantiates the object context with the use of the factory component. Notice that we were able to implement the data component without implementing the factory component. This is one of the main advantages of component-based software engineering.

The next task is to implement the factory component. The ObjectContext – that is the base of ClubEntities – class provides multiple constructors. One of them expects no arguments, it uses the default, baked-in connection string to initialize itself. The two others expect an EntityConnection object or a connection string. The former will be used this time. But how should the EntityConnection object be created? Let’s create another component that serves as provider for this kind of object. Its interface is defined like this:

public interface IConnectionProvider : IDisposable
{
    EntityConnection Connection { get; }
}

Note that this is a disposable component, because it also handles the lifetime of the provided connection object. If the provider component is disposed, than the connection object will be disposed too. The reason behind this will be explained later. For now, we can implement the IObjectContextFactory interface, it is really self-evident :

public class ObjectContextFactory : IObjectContextFactory
{
    private IConnectionProvider provider; 

    public ObjectContextFactory(IConnectionProvider provider)
    {
        this.provider = provider;
    }

    public IClubEntities Create()
    {
        return new ClubEntities(this.provider.Connection);
    }
}

So what is the motivation behind the connection provider component? If an ObjectContext class is instantiated with the empty or connection string constructor and it is disposed then the internal connection object will be disposed too. If the ObjectContext class is instantiated with an external connection object, it will not be disposed if the ObjectContext instance is disposed. The following sequence diagrams might help to understand the different mechanisms.

image

image

This means that the externally created EntityConnection instance should be manually disposed or leave it for the GC finalization (what is not recommended). You might ask why can’t we use the other constructor. My main reason that I want to ensure that the ObjectContext instances use the same EntityConnection instance. This has more reasons:

  • TransactionScope: if two connection objects are used in the same transaction scope, than the operations are might executed in a distributed transaction what needs DTC (despite one connection object would be enough).
  • Automated testing: this is explained in an other blog post

Since the purpose of the connection provider component is explained, a base implementation can be created: 

public abstract class ConnectionProviderBase : IConnectionProvider
{
    private EntityConnection connection;

    public EntityConnection Connection
    {
        get 
        {
            if (this.connection == null)
            {
                this.connection = this.CreateConnection();
            }

            return this.connection;
        }
    }

    protected abstract EntityConnection CreateConnection();

    public void Dispose()
    {
        if (this.connection != null)
        {
            this.connection.Dispose();
        }
    }
}

There is nothing complicated here. The component does lazy initialization: the connection object is only created when it is requested. If the component is disposed then the connection object will be disposed too (if it exists). It is really easy to create a concrete complement based on this implementation:

public class DefaultConnectionProvider : ConnectionProviderBase
{
    protected override EntityConnection CreateConnection()
    {
        return new EntityConnection("name=ClubEntities");
    }
}

This is quite self-evident, at this point there is no problem with baking the connection configuration in, it can be easily replaced by implementing another component derived from the same base class.

Assembling the components

All the required components are ready, there is only one thing to do: assemble them. The following component diagram shows the simplest configuration:

image

The façade component serves as a top-level component that is aware of the lifetime of the request currently served by the application. It holds a reference to the connection provider component that is used by all the object context factory components. In the appropriate time (typically in the end of the request), it disposes the connection provider component, that result in the disposal of the connection object. A simple implementation of the façade component could look like this:

using (IConnectionProvider provider = new DefaultConnectionProvider())
{
    IDataService dataService = new DataService(new ObjectContextFactory(provider));
    
    Person person = dataService.GetPerson(id);
    person.Title = "Software engineer";

    dataService.StorePerson(person);
}

It is easy to image how a complex application would look like. The following diagram is similar to previous, but it uses three types of data service to handle requests.

image

Note that all the object context factory and the façade service component uses the same connection provider component instance. This architecture ensures that the data service components will use the same connection object that also will be disposed in the appropriate time.

Conclusion

Designing loosely coupled applications is not self-evident. Finding the proper way of functionality decoupling requires deep understanding of the environment and planned features of the designed application.