Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject same DataContext instance across several types with Unity

Suppose I have IRepository interface and its implementation SqlRepository that takes as an argument LINQ to SQL DataContext. Suppose as well that I have IService interface and its implementation Services that takes three IRepository, IRepository and IRepository. Demo code is below:

public interface IRepository<T> { }

public class SqlRepository<T> : IRepository<T>
{
    public SqlRepository(DataContext dc) { ... }
}

public interface IService<T> { }

public class Service<T,T1,T2,T3> : IService<T>
{
    public Service(IRepository<T1> r1, IRepository<T2>, IRepository<T3>) { ... }
}

Is it any way while creating Service class to inject all three repositories with the same DataContext?

like image 593
Sergejus Avatar asked May 22 '09 10:05

Sergejus


2 Answers

All you need to do is make sure when you register the Datacontext with your Unity container use the PerResolveLifetimeManager either in config:

<type type="<namespace>.DataContext, <assembly>">
    <lifetime type="Microsoft.Practices.Unity.PerResolveLifetimeManager, Microsoft.Practices.Unity" />
</type>

or in code:

container.RegisterType<DataContext>(new PerResolveLifetimeManager());

then whenever the container resolves the Service any dependencies which also require a DataContext will be provided with exactly the same one. But the next request to resolve Service will create a new DataContext.

like image 118
bstoney Avatar answered Sep 19 '22 02:09

bstoney


I think I know what you want to do. I'm in the same boat and am trying to come up with a solution.

My Service layer performs operations on in coming requests, and what it does depends on the contents. It passes it to a series of chain of responsibility classes. I want the same context passed to all classes within the lifetime of the service method called

You can Specify PerResolveLifetimeManager. So far, it seems to be working with my test cases:

Service Class:

public interface IServiceClass
{
    void DoService();
}

class ServiceClass : IServiceClass
{
    private IHandler Handler { get; set; }

    public ServiceClass(IHandler handler)
    {
        Handler = handler;
    }

    public void DoService()
    {
        Handler.HandleRequest();
    }
}

IHandler is implemented by two classes, and performs Chain of Responsibility pattern:

    public interface IHandler
{
    void HandleRequest();
}

class Handler : IHandler
{
    private IDataContext DataContext { get; set; }
    public Handler(IDataContext dataContext)
    {
        DataContext = dataContext;
    }

    public void HandleRequest()
    {
        DataContext.Save("From Handler 1");
    }
}

class Handler2 : IHandler
{
    private IDataContext DataContext { get; set; }
    private IHandler NextHandler { get; set; }

    public Handler2(IDataContext dataContext, IHandler handler)
    {
        DataContext = dataContext;
        NextHandler = handler;
    }

    public void HandleRequest()
    {
        if (NextHandler != null)
            NextHandler.HandleRequest();

        DataContext.Save("From Handler 2");
    }
}

As you can see, both handlers accept an instance of IDataContext, which I want to be the same in both of them. Handler2 also accepts an instance of IHandler to pass control to (it does both here to demonstrate, but actually, only one would handle the request...)

IDataContext. In the constructor I initialize a Guid, and during its operation, output it so I can see if both times its called is using the same instance:

public interface IDataContext
{
    void Save(string fromHandler);
}

class DataContext : IDataContext
{
    private readonly Guid _guid;

    public DataContext()
    {
        _guid = Guid.NewGuid();
    }

    public void Save(string fromHandler)
    {
        Console.Out.WriteLine("GUI: [{0}] {1}", _guid, fromHandler);
    }
}

Finally, registration and calling of service:

    private IUnityContainer container;
    private void InitializeUnity()
    {
        container = new UnityContainer();
        container.RegisterType<IHandler, Handler2>("Handler2",
            new InjectionConstructor(new ResolvedParameter<IDataContext>(), new ResolvedParameter<IHandler>("Handler1")));
        container.RegisterType<IHandler, Handler>("Handler1");
        container.RegisterType<IDataContext, DataContext>(new PerResolveLifetimeManager());
        container.RegisterType<IServiceClass, ServiceClass>("MyClass", new InjectionConstructor(new ResolvedParameter<IHandler>("Handler2")));
    }

    private void CallService()
    {
        var service = container.Resolve<ServiceClass>("MyClass");
        service.DoService();

        // Resolving and calling again to simulate multiple resolves:
        service = container.Resolve<ServiceClass>("MyClass");
        service.DoService();
    }

This is the output I get:

GUI: [f2250055-8a5f-4f80-a1b6-bcc5574138cf] From Handler 1
GUI: [f2250055-8a5f-4f80-a1b6-bcc5574138cf] From Handler 2
GUI: [22a5c0a3-3c5c-4683-807d-bf2b43f3cd0a] From Handler 1
GUI: [22a5c0a3-3c5c-4683-807d-bf2b43f3cd0a] From Handler 2

Hope this wall of text answered your question... If not sorry, it did inspire a solution I needed to implement...

like image 23
Bryce Fischer Avatar answered Sep 18 '22 02:09

Bryce Fischer