1

This might be a strange case but I want to sometimes reuse the same instance when getting exports with MEF and sometimes create a new.

Basicly I have a WCF service class the is instance per call. Each instance imports a RepositoryFactory which will also be new instance per service class. I return a Repository in the Factory and a repository gets a IDbContext injected.

I want each instance of the Factory to inject the same instance of IDbContext but have seperate instances between Factory instances.

So:

1) Factory1 is created
2) Factory1 creates Repository1-1 that gets IDbContext1 injected
3) Factory1 creates Repository1-2 that gets IDbContext1 injected
4) Factory2 is created
5) Factory2 creates Repository2-1 that gets IDbContext2 injected
6) Factory2 creates Repository2-2 that gets IDbContext2 injected

This should ensures that Repositories created from the same factory share a Unit of Work.

But being new to MEF I'm not sure how I would go about doing that.


EDIT

This is what I got:

public class RepositoryFactory
{
    private readonly CompositionContainer _container;

    [Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
    private readonly IDbContext _context;

    public IRepository<T> CreateRepository<T>() where T : class, IEntity
    {
        //Somehow add the _context instance into the Repository import

        return _container.GetExportedValue<EntityRepository<T>>();
    }
}

and then

public class EntityRepository<T> : IRepository<T> where T : class, IEntity
{
    // Perhaps a contract name might help!!
    [Import(RequiredCreationPolicy=CreationPolicy.Shared)]
    protected readonly IDbContext _context;
Ingó Vals
  • 4,788
  • 14
  • 65
  • 113
  • One idea I had was to Import the Context into the Factory and somehow make it the instance that would be exported to the repositories created. – Ingó Vals Oct 11 '11 at 13:08
  • Here is the same question except for Unity the IoC container http://stackoverflow.com/questions/787001/can-i-pass-constructor-parameters-to-unitys-resolve-method – Ingó Vals Oct 17 '11 at 14:57

2 Answers2

1

You cannot accomplish this with MEF; no matter what you do the MEF container will not act correctly as a Unit of Work manager for you, it just isn't made for this.

You should attempt to explicitly code a Unit of Work infrastructure for your DAL to consume. Your repositories should explicitly ask the a Unit of Work Manager to provide a current Unit of Work and with it the appropriate context.

Take a look at the code in NCommon https://github.com/riteshrao/ncommon; you can refactor the Unit of Work features to serve your needs.

Xacron
  • 341
  • 4
  • 11
  • Well this type of action isn't something that would only be useful in a UnitOfWork. I'm pretty sure IoC containers do supply something like this and MEF has some mention of PartCreator (which I can't find). Its really about more control over your exports and imports. – Ingó Vals Oct 12 '11 at 14:35
  • You're correct that other IoC containers offer this, but I don't believe MEF currently does; you're most likely going to have to wait until .NET 4.5 at the earliest. Like you mentioned in your comment above it would most likely involve configuring MEF to delegate object instnatiation to your Factory. I'd be very interested if you found a solution like this for .NET 4.0 – Xacron Oct 12 '11 at 15:40
  • ok I came up with a solution that doesn't really use MEF but makes it the responsibility of the factory to ensure repositories share instances when they are made from some factory instance. http://stackoverflow.com/questions/7725190/mef-and-dynamic-decision-on-instance-creation/7795988#7795988 – Ingó Vals Oct 17 '11 at 15:30
  • So each instance of your per-call service creates it's own RepositoryFactory instance which is used to serve up all Repository instnaces, hence they share the same context. I assume with this solution, you have very simple calls to your services, with no long-running prcoesses or service methods calls that need to be correlated across service instance? If that's the case this should work just fine, but you will quickly find that this solution will not be flexible enough to handle more complicated service patterns. – Xacron Oct 17 '11 at 20:35
  • Yes you are right. I must admit I'm not fully aware of what a proper Unit of Work implementation tries to accomplish beyond this. One thing I know from experience is that I would rather have to do microtransactions then keep objects attached for a long time. I simply get an object, make a DTO and move it over the service. – Ingó Vals Oct 18 '11 at 09:19
0

OK here is a solution I came up with but haven't tried. It's somewhat simple and actually works around MEF but doesn't really break it, at least not in my case.

Add to IRepository class the following method:

void SetContext(IDbContext context);

or better yet

IDbContext context { set; }

and in the factory:

public class RepositoryFactory
{
    private readonly CompositionContainer _container;

    [Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
    private readonly IDbContext _context;

    public IRepository<T> CreateRepository<T>() where T : class, IEntity
    {
        IRepository<T> repo = _container.GetExportedValue<EntityRepository<T>>();
        repo.context = _context;

        return repo;
    }
}

And the rest should be self explanatory:

public  class   EntityRepository<T> :   IRepository<T>  where   T   :   class,  IEntity
{
    protected   IDbContext  _context;

    IDbContext  context
    {
        set {   _context    =   value;  }
    }

    public  virtual IQueryable<T>   GetQuery()
    {
        return  _context.Set<T>();
    }

    public  virtual T   GetById(Guid    id)
    {
        return  _context.Set<T>().Find(id);
    }

    public  virtual void    SaveOrUpdate(T  entity)
    {
        if  (_context.Set<T>().Find(entity.Id)  ==  null)
        {
            _context.Set<T>().Add(entity);
        }

        _context.SaveChanges();
    }

    public  virtual void    Delete(T    entity)
    {
        _context.Set<T>().Remove(entity);

        _context.SaveChanges();
    }
}

If you are using it in the same way as I am I can't really see a problem with this implementation. The factory is responsible for creating the class so It can be responsible for setting the context. The CreationPolicy should ensure that each Factory gets it's own instance of DbContext that it then relegates to it's Repositories so they share a context.

Ingó Vals
  • 4,788
  • 14
  • 65
  • 113