2

I'm out of ideas how to configure right Windsor container for use with repositories in Windows app. I have generic repository implementation Repository, where T is entity type, it has a dependency IDatacontextProvider, which provides datacontext for it:

public class Repository<T> : IRepository<T> where T : class
{
    protected DataContext DataContext;
    public Repository(IDataContextProvider dataContextProvider) {
         DataContext = dataContextProvider.DataContext;
    }
    ....
}

And for simple things everything works ok with following configuration:

 container.Register(                                
            Component.For<IDataContextProvider>()
                  .ImplementedBy<DataContextProvider>()
                  .Lifestyle.Transient,
            Component.For(typeof(IRepository<>))
                  .ImplementedBy(typeof(Repository<>))
                  .Lifestyle.Transient, .... 

Problems occur, when i try to join different entities from several repositories, as long as each repository instance has different data context instance.
For example i have simple service:

public class SimpleService : ISimpleService {
     public SimpleService(IRepository<Order>, IRepository<OrderLine>) {
         ....
     }
}

I could make IDataContextProvider as Singleton, but i think that would bring even bigger problems.
I could pass IDataContextProvider to SimpleService, and try to resolve repository instances there, but that would require additional code to make service easy testable and would require additional dependencies. May be somebody has a better idea how to solve this?

update: following advice, I've created repository factory (it's little bit different from proposed in answer, it does not have direct dependency to datacontext, but idea is very same):

 public interface IRepositoryFactory
{
    IRepository<T> GetRepository<T>() where T:class;
}

public class RepositoryFactory : IRepositoryFactory
{
    private readonly IDataContextProvider dataContextProvider;

    public RepositoryFactory(IDataContextProvider dataContextProvider)
    {
        this.dataContextProvider = dataContextProvider;
    }

    public IRepository<T> GetRepository<T>() where T : class
    {
        return new Repository<T>(dataContextProvider);
    }
}
Mauricio Scheffer
  • 98,863
  • 23
  • 192
  • 275
Giedrius
  • 8,430
  • 6
  • 50
  • 91
  • why is singleton causing "bigger" problems? for a windows app with only one thread, that should be the best option.. – Can Gencer Apr 20 '11 at 07:14
  • well, because DataContext isn't meant to live long (it does not have cache clearing, changes separation for several submits, etc - it assumes that you'll release it and create new instance as soon as possible), there's many discussions about that (for example this: http://stackoverflow.com/questions/226127/multiple-single-instance-of-linq-to-sql-datacontext). Plus there's multiple users and threading is used in quite lot of places. – Giedrius Apr 20 '11 at 07:21
  • OK I understand. Not very familar with Entity Framework, use NHibernate myself. Why not have a RepositoryFactory, which takes a IDataContextProvider. Your user classes can have IRepositoryFactory as a dependency and get the repositories they need from there and all the repositories created from the same instance of RepositoryFactory would share the same DataContext. – Can Gencer Apr 20 '11 at 07:24
  • Very interesting idea, i'll try to see how it could look in code. – Giedrius Apr 20 '11 at 07:30
  • I added an answer with a possible solution. – Can Gencer Apr 20 '11 at 07:30

3 Answers3

2

What about having another layer in between, such as a RepositoryFactory? That one could have a transient lifestyle. All repositories created from the factory would share the same DataContext instance. You would also need to change your repository classes so they take a DataContext instance instead of a DataContextProvider.

public class RepositoryFactory : IRepositoryFactory
{
   protected DataContext dataContext;
   public RepositoryFactory(IDataContextProvider provider)
   {
      dataContext = dataContextProvider.DataContext;
   }

   public IRepository<T> GetRepository<T>()
   {
      return new Repository<T>(dataContext);
   }
}

public class SimpleService : ISimpleService {
     public SimpleService(IRepositoryFactory factory) {
         ....
     }
}
Can Gencer
  • 8,822
  • 5
  • 33
  • 52
  • Well, I've mentioned in my question, that this is Windows application, so PerWebRequest is not for this case. I've used it in my web app with same repository implementation and it worked great though. – Giedrius Apr 20 '11 at 07:10
1

IDatacontextProvider sounds like a factory interface and these are usually defined as singletons in the dependency injection. I see several potential paths to a solution:

  1. I don't know about particulars of your application, but maybe you can write your own lifestyle manager for IDatacontextProvider (since you say neither singleton nor transient suits you).
  2. If you want to ensure the same IDatacontextProvider is passed among repositories, maybe you should think about providing it explicitly as a method parameter, instead of an injected dependency.
  3. @Can's answer is also a possible solution, I've used that one myself once.
Igor Brejc
  • 18,714
  • 13
  • 76
  • 95
  • regarding own lifestyle, there's http://blog.schuager.com/2010/11/contextual-lifestyle-reloaded.html it looks also very interesting and quite clean. – Giedrius Apr 20 '11 at 07:39
  • @Giedrius, this link is interesting too but I don't like that you need to have a reference to the kernel to be able to create a ContainerContext. That means your client classes that will be creating the context need to be aware of the castle kernel.. – Can Gencer Apr 20 '11 at 07:55
  • @Can, well, I don't like that either, but from other side it may help to solve more complex composition problems, like serviceA uses serviceB and both use repositoryC and repositoryD, that need to come with same datacontext. – Giedrius Apr 20 '11 at 13:36
0

Your problem is in the configuration of the lifestyle. I had the same issues. You have to configure your repositories with an PerWebRequest lifestyle. This gave me an nice performance boost and reducing my errors from dozens to zero.

On my blog you can find an simple example http://marcofranssen.nl of dependency injection in combination with mvc3 and EF Code first.

Marco
  • 4,817
  • 5
  • 34
  • 75
  • It is mentioned, that I'm talking about windows app, not web app. – Giedrius Jul 16 '11 at 15:43
  • I know but you can translate this example to your windows app. The example is in a webapp, but you can do the same for a windows app. – Marco Jul 22 '11 at 18:53
  • thing is, that in windows forms usually everything more complicated - it is hard to split workflow into "requests". For example, if opens child window, should i think of it as new request? If yes, then by web definitions, parent window request should be disposed? If no, what is then new request? I'm not saying that idea is not interesting, just at the moment have no idea how that could look like. – Giedrius Jul 25 '11 at 05:54