Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IDisposable on an injected repository

I have the following ADO .Net Repository

public class Repository : IRepository, IDisposable
{
   private readonly IUnitOfWork UnitOfWork;
   private SqlConnection Connection;

   public Repository(IUnitOfWork unitOfWork, connectionString)
   {
      UnitOfWork = unitOfWork;
      Connection = new SqlConnection(connectionString);
      Connection.Open();
   }

   public MyObject FindBy(string userName)
   {
      //...Ado .Net command.ExecuteReader, etc.
   }
}

This repository is injected with an IoC container to a Domain Service and is used like so:

public class UserDomainService : IUserDomainService
{
   private readonly IRepository Repository;

   public UserDomainService(IRepository repository)
   {
      Repository = repository;
   }

   public User CreateNewUser(User user)
   {
      using(Repository)
      {
         var user = Repository.FindBy(user.UserName);
         if(user != null)
            throw new Exception("User name already exists!");

         Repository.Add(user);
         Repository.Commit();
      }
   }
}

The idea is that I always put the Repository object in a using statement so when it finishes, the connection is closed and disposed of but I see it as a problem since the Domain Service class is still alive and if there is a second call to it, it will fail since the repository has already been destroyed.

Now I have full control of all the code and I want to design only coarse grain service calls, but there's something about the whole thing that doesn't feel right.

I am doing it like this so I can avoid that the Domain Service knows about OpenConnection and CloseConnection methods in the repository.

Is this design inherently bad or is there a better way of doing this?

After thought: All the dependency tree is being generated at the WCF level when a request arrives and, of course you can see that the connection is opened at that moment since it happens in the repository's constructor so I believe that it is not that bad since it is open only for the duration of this particular call. Am I right on this assumption or am I doing something terribly bad by opening the DB connection so early in the process?

like image 802
Sergio Romero Avatar asked Apr 03 '12 14:04

Sergio Romero


2 Answers

Inject a factory that creates the instances you need, not an instance itself.

Take an IRepositoryFactory so that you can create an IRepository and dispose of it each time you use it. This way, neither the domain service or the factory would need to be disposable. Also, and importantly, you keep the code abstract by still injecting the implementation as opposed to hard-coding it.

public class UserDomainService : IUserDomainService
{
   private readonly IRepositoryFactory RepositoryFactory;

   public UserDomainService(IRepositoryFactory factory)
   {
      RepositoryFactory = factory;
   }

   public User CreateNewUser(User user)
   {
      using (IRepository repository = RepositoryFactory.Create())
      {
         var user = repository.FindBy(user.UserName);
         if(user != null)
            throw new Exception("User name already exists!");

         repository.Add(user);
         repository.Commit();
      }
   }
}

You don't always have to inject the type you need. On reading up on Castle Windsor (whose mindset is register-resolve-release), you find that if you want to Resolve stuff at an indeterminate time in the app's life, it is suggested to use Type Factories.

You know you will need a Repository, but don't know when. Instead of asking for a repository, ask for something that creates them. The level of abstraction is thus maintained and you have not leaked any implementation.

like image 71
Adam Houldsworth Avatar answered Oct 22 '22 08:10

Adam Houldsworth


The problem you have is one of ownership. The UserDomainService class does not create the IRepository, yet it still takes the ownership of that instance, since it disposes it.

The general rule is that the one who creates an object should distroy it. In other words, he who creates the object is the owner, and the owner shall destroy that object.

There are two solutions to your problem.

  1. Create a IRepositoryFactory, as Adam clearly explains. A CreateNewRepository() method on such a factory will clearly communicate that the caller gets the ownership and should dispose that created repository.

  2. Let the one who creates (and injects) that repository deal with the disposal of that repository. Either you do this manually in your WCF service, or you use an IoC/DI framework. In case you use a DI framework, you should probably look at a Per Web Request lifetime, or something similar.

Last note, your IRepository implements IDisposable. When choosing solution 2, you can remove the IDisposable interface from IRepository, which hides the fact that resources are involved from the application. Hiding IDisposable from the application is a good thing, since that interface is a leaky abstraction. You already encountered this, since calling Dispose from within the application, breaks the entire application.

like image 44
Steven Avatar answered Oct 22 '22 08:10

Steven