Factory methods in Effort (CreateTransient vs CreatePersistent)

The Effort library provides multiple factory classes that developers can choose from. All of them can serve well in different scenarios.

  • DbConnectionFactory
  • EntityConnectionFactory
  • ObjectContextFactory

They are capable of creating different type of fake data endpoints: DbConnection, EntityConnection and ObjectContext, respectively. Fake DbConnection objects are ideal for using them in the Code First programming approach. The purpose of fake EntityConnection objects is to utilize them in the Database First and Model First techniques. Instantiate ObjectContext objects by passing them as constructor argument. This can be also done automatically by creating fake ObjectContext instances directly.

All the factory components provide two kind of factory methods.

  • CreateTransient
  • CreatePersistent

What is the difference between them? The answer lies in the lifecycle of the underlying in-memory database bound to the endpoint. Let’s examine these factory methods with an extremely simple demonstration that uses the DbConnectionFactory component.

The demo includes a single Person entity with two members:

public class Person
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }
}

The DbContext class that provides the entity endpoint can be defined easily too. Note that it needs a constructor that can accept a DbConnection object.

public class PeopleDbContext : DbContext
{
    public PeopleDbContext(DbConnection connection)
        : base(connection, true)
    {
    }

    public IDbSet People { get; set; }
}

First, the CreateTransient method is demonstrated. The following code instantiates a DbContext object with a fake DbConnection object and adds some data to the fake database. Then creates another DbContext instance with a newly created fake DbConnection and tries to query the previously added data.

using (var ctx = new PeopleDbContext(Effort.DbConnectionFactory.CreateTransient()))
{
    ctx.People.Add(new Person() { Id = 1, Name = "John Doe" });
    ctx.SaveChanges();

    Console.WriteLine("Test 1 - First Count: {0}", ctx.People.Count());
}

using (var ctx = new PeopleDbContext(Effort.DbConnectionFactory.CreateTransient()))
{
    Console.WriteLine("Test 1 - Second Count: {0}", ctx.People.Count());
}

This code outputs the following:

Test 1 - First Count: 1
Test 1 - Second Count: 0

The second DbContext instance is not able to see the entity added by the first DbContext instance. Every time a fake connection object is created with the CreateTransient method, the new fake connection object will redirect the data operations to a completely new unique in-memory database instance. No other endpoint can use that database, it is completely isolated. In addition the database is cleaned up when the connection object is no longer in use.

Change the previous code a little bit: let the CreatePersistent method be used this time. Note that this method accepts a string argument. This string identifies the fake database instance that the connection object is bound to.

using (var ctx = new PeopleDbContext(Effort.DbConnectionFactory.CreatePersistent("1")))
{
    ctx.People.Add(new Person() { Id = 1, Name = "John Doe" });
    ctx.SaveChanges();

    Console.WriteLine("Test 2 - First Count: {0}", ctx.People.Count());
}

using (var ctx = new PeopleDbContext(Effort.DbConnectionFactory.CreatePersistent("1")))
{
    Console.WriteLine("Test 2 - Second Count: {0}", ctx.People.Count());
}

This code outputs the following:

Test 2 - First Count: 1
Test 2 - Second Count: 1

The second DbContext instance is able to see the newly added entity this time, because the connection objects are bound to the same database instance. This is possible because they were created with the same identifier.

In the last code sample the connection objects are created with the CreatePersistent method too, but with different identifiers.

using (var ctx = new PeopleDbContext(Effort.DbConnectionFactory.CreatePersistent("2")))
{
    ctx.People.Add(new Person() { Id = 1, Name = "John Doe" });
    ctx.SaveChanges();

    Console.WriteLine("Test 3 - First Count: {0}", ctx.People.Count());
}

using (var ctx = new PeopleDbContext(Effort.DbConnectionFactory.CreatePersistent("3")))
{
    Console.WriteLine("Test 3 - Second Count: {0}", ctx.People.Count());
}

This code outputs the following:

Test 3 - First Count: 1
Test 3 - Second Count: 0

The second DbContext object is not able to see the added entity, because the fake connection objects are bound to separate database instances. The reason of this, that the identifiers that were used to create them are not identical.

These two kind of factory method can be useful in different scenarios. For example, if someone wants to write automated tests for data oriented components, then CreateTransient is the way to go, because it ensures that the tests can run in completely insolated environments. They can run even in parallel. If someone wants to test the system in an interactive way without screwing up the persisted data in the real database, then CreatePersistent might worth a shot.

Use them wisely!