With dependency injection, how do you dispose of resources?

dependency-injection dispose entity-framework structuremap

Question

To resolve references to my repository class, I'm using StructureMap. IDisposable is implemented in my repository interface, for example.

public interface IMyRepository : IDisposable
{
  SomeClass GetById(int id);
}

Using Entity Framework, the interface was implemented:

public MyRepository : IMyRepository
{
    private MyDbContext _dbContext;

    public MyDbContext()
    {
        _dbContext = new MyDbContext();
    }

    public SomeClass GetById(int id)
    {
        var query = from x in _dbContext
                    where x.Id = id
                    select x;
        return x.FirstOrDefault();
    }

    public void Dispose()
    {
        _dbContext.Dispose();
    }
}

In any case, as I already indicated, I resolve IMyRepository using StructureMap. Should I thus name my discard method how, when, and where?

1
27
11/26/2015 3:19:11 AM

Popular Answer

Please be aware that my opinions have changed, and the advice that follows should be seen as being out of date. Please take a moment to read the update.


Even while some DI frameworks can handle object lifetime management and even dispose of objects for you after you're done with them, this makes object disposal much too implicit. TheIDisposable interface was developed as a result of the need for predictable resource cleanup. I personally prefer to be extremely open about this clean-up in the context of DI. When you are explicit, you essentially have two choices: 1. Set up the DI to return temporary items, then get rid of them on your own. 2. Set up a factory, then tell it to produce new instances.

Because your code isn't as tidy as it might be, particularly when using Dependency Injection, I prefer the second method over the first. Take a look at this code, for example:

public sealed class Client : IDisposable
{
    private readonly IDependency dependency;

    public Client(IDependency dependency)
    {
        this. dependency = dependency;
    }

    public void Do()
    {
        this.dependency.DoSomething();
    }

    public Dispose()
    {
        this.dependency.Dispose();
    }
}

Although this code clearly disposes the dependence, readers may be confused since resources should typically only be disposed of by the resource owner. It seems that theClient moment it was injected, the owner of the resource changed.

I prefer using a factory because of this. Consider this illustration as an example:

public sealed class Client
{
    private readonly IDependencyFactory factory;

    public Client(IDependencyFactory factory)
    {
        this.factory = factory;
    }

    public void Do()
    {
        using (var dependency = this.factory.CreateNew())
        {
            dependency.DoSomething();
        }
    }
}

The behavior in this case is identical to that in the preceding example, however see how theClient class is not required to implementIDisposable because it produces and discards the resource within theDo method.

The most direct route (the one that causes the least surprise) to do this is to inject a factory. That is why I like this look. The drawback to this is that you often have to declare extra classes (for your factories), but I don't mind that.


RPM1984 requested a more specific illustration.

I would oppose having the repository useIDisposable but having a Unit of Work that puts it into practiceIDisposable , manages/contains repositories, and has a factory that is capable of producing new units of labor. In light of this, the code above would seem as follows:

public sealed class Client
{
    private readonly INorthwindUnitOfWorkFactory factory;

    public Client(INorthwindUnitOfWorkFactory factory)
    {
        this.factory = factory;
    }

    public void Do()
    {
        using (NorthwindUnitOfWork db = 
            this.factory.CreateNew())
        {
            // 'Customers' is a repository.
            var customer = db.Customers.GetById(1);

            customer.Name = ".NET Junkie";

            db.SubmitChanges();
        }
    }
}

I employ a tangible form in the design I've described in here.NorthwindUnitOfWork class that encompasses anIDataMapper It serves as the entry point to the underlying LINQ provider (such as LINQ to SQL or Entity Framework). The design is, in brief, as follows:

  1. An INorthwindUnitOfWorkFactory is administered to a customer.
  2. The specific way that manufacturing is used produces a real result.NorthwindUnitOfWork injects an O/RM-specific class andIDataMapper adds class to it.
  3. The NorthwindUnitOfWork is really a wrapper around the type-safeIDataMapper both theNorthwindUnitOfWork asks for theIDataMapper for repositories and sends requests to the mapper to submit modifications and dispose.
  4. The IDataMapper returns Repository<T> implements classes and a repositoryIQueryable<T> to enable LINQ over the repository on the client side.
  5. The precise use of theIDataMapper refers to the precise unit of labor for O/RM, such as EF'sObjectContext ). Because of this, theIDataMapper must put into practiceIDisposable .

As a consequence, the following design is produced:

public interface INorthwindUnitOfWorkFactory
{
    NorthwindUnitOfWork CreateNew();
}

public interface IDataMapper : IDisposable
{
    Repository<T> GetRepository<T>() where T : class;

    void Save();
}

public abstract class Repository<T> : IQueryable<T>
    where T : class
{
    private readonly IQueryable<T> query;

    protected Repository(IQueryable<T> query)
    {
        this.query = query;
    }

    public abstract void InsertOnSubmit(T entity);

    public abstract void DeleteOnSubmit(T entity);

    // IQueryable<T> members omitted.
}

The NorthwindUnitOfWork is a concrete class that has features related to certain repositories, suchCustomers , Orders , etc:

public sealed class NorthwindUnitOfWork : IDisposable 
{
    private readonly IDataMapper mapper;

    public NorthwindUnitOfWork(IDataMapper mapper)
    {
        this.mapper = mapper;
    }

    // Repository properties here:    
    public Repository<Customer> Customers
    {
        get { return this.mapper.GetRepository<Customer>(); }
    }

    public void Dispose()
    {
        this.mapper.Dispose();
    }
}

What's left is the actualization of theINorthwindUnitOfWorkFactory and a real application of theIDataMapper . One for Entity Framework is as follows:

public class EntityFrameworkNorthwindUnitOfWorkFactory
    : INorthwindUnitOfWorkFactory
{
    public NorthwindUnitOfWork CreateNew()
    {
        var db = new ObjectContext("name=NorthwindEntities");
        db.DefaultContainerName = "NorthwindEntities";
        var mapper = new EntityFrameworkDataMapper(db);
        return new NorthwindUnitOfWork(mapper);
    }
}

both theEntityFrameworkDataMapper :

public sealed class EntityFrameworkDataMapper : IDataMapper
{
    private readonly ObjectContext context;

    public EntityFrameworkDataMapper(ObjectContext context)
    {
        this.context = context;
    }

    public void Save()
    {
        this.context.SaveChanges();
    }

    public void Dispose()
    {
        this.context.Dispose();
    }

    public Repository<T> GetRepository<T>() where T : class
    {
        string setName = this.GetEntitySetName<T>();

        var query = this.context.CreateQuery<T>(setName);
        return new EntityRepository<T>(query, setName);
    }

    private string GetEntitySetName<T>()
    {
        EntityContainer container =
            this.context.MetadataWorkspace.GetEntityContainer(
            this.context.DefaultContainerName, DataSpace.CSpace);

        return (
            from item in container.BaseEntitySets
            where item.ElementType.Name == typeof(T).Name
            select item.Name).First();
    }

    private sealed class EntityRepository<T>
        : Repository<T> where T : class
    {
        private readonly ObjectQuery<T> query;
        private readonly string entitySetName;

        public EntityRepository(ObjectQuery<T> query,
            string entitySetName) : base(query)
        {
            this.query = query;
            this.entitySetName = entitySetName;
        }

        public override void InsertOnSubmit(T entity)
        {
            this.query.Context.AddObject(entitySetName, entity);
        }

        public override void DeleteOnSubmit(T entity)
        {
            this.query.Context.DeleteObject(entity);
        }
    }
}

More details are available about the here model.

December 2012 UPDATE

This is an update to my initial response from two years ago. The way I attempt to design the systems I'm working on has evolved significantly over the previous two years. I no longer like to employ the factory method when dealing with the Unit of Work pattern, even if it has served me well in the past. As an alternative, I just directly inject a Unit of Work instance into consumers. However, a lot relies on how your system is set up as to whether this concept is feasible for you. Please check out my most recent Stackoverflow answer for additional information on this: Why just one DbContext per web request?.

22
12/4/2016 4:54:17 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow