views:

386

answers:

3

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?

A: 

If I understand your question correctly (and if you are using unity...I suppose you do because you have taggged it with unity) you could do something like this:

In your repository implementions,

[InjectionConstructor]
public SqlRepository(
    [Dependency] DataContext ctx)

but then you have to mark the service contructor in the same manner and use the container to resolve your services as well as the repository. The DataContext also has to be in the container to make it work.

An alternative approach is to do something like this with your repository:

[InjectionMethod]
public void Initialize(
    [Dependency] DataContext ctx

this will tell unity to call this method if you will, in your service constructor, use unity with the BuildUp method...something like this:

unitycontainer.BuildUp<IRepository>(repository);

I guess that´s not quite what your looking for but please tell me if I´m on the right track and I´ll see if I can help you further...

Cheers / J

Johan Leino
Thanks for a try, but actually, what I'm trying to accomplish, is to use the same DataContext instance during Service class initialization. This is required to support transactions for the Repository1, Repository2 and Repository3. ContainertLifeTime doesn't work in my case, because I want to have new DataContext for every new Service instance.
Sergejus
Actually, I think his approach is similar to my #2 above. He's injecting the Initialize method and creating the container there, then disposing it post call. Something like this:--> precall.initialize Unity Container, using ContainerControlledLifetimeManager so they are singletons.--> Service Initialize call:resolve datacontext, will always be the same datacontext as long as the Container created in the precall is in scope.--> Post Call:Dispose of the container...
Bryce Fischer
A: 

Have you tried using the RegisterInstance() method for the unity container? Something like this might work:

public static UnityContainer CreateContainer() { UnityContainer container = new UnityContainer();

        try
        {
            var section = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;

            if (section != null)
            {
                section.Containers[0].Configure(container);
            }
        }
        catch (Exception ex)
        {
            TraceLogger.LogMessage("Configurarion Error for Unity Container", ex.Message, TraceEventType.Critical);
            Environment.Exit(1);
        }


        container.RegisterInstance(new DataContext());
        return container;
    }

Now, every time this container tries to build an object which needs a DataContext, the same instance will be passed. You could even configure the DataContext before registering its instance.

UPDATE: One option (now, I don't know if its really a good practice, but this worked for me) is to create a different container for each object you're gonna create. Something like:

UnityContainer container1 = ContainerFactory.CreateContainer();
UnityContainer container2 = ContainerFactory.CreateContainer();
UnityContainer container3 = ContainerFactory.CreateContainer();
MyObject1 object1 = container1.Resolve<MyObject1>();
MyObject2 object2 = container2.Resolve<MyObject2>();
MyObject3 object3 = container3.Resolve<MyObject3>();

or a more summarized way:

MyObject1 object1 = ContainerFactory.CreateContainer().Resolve<MyObject1>();
MyObject1 object2 = ContainerFactory.CreateContainer().Resolve<MyObject2>();
MyObject1 object3 = ContainerFactory.CreateContainer().Resolve<MyObject3>();

Well, there's a lot of ways to do it, creating a list, using the factory pattern. Hope it helps

Samuel Carrijo
Thanks for the suggestion. Unfortunatelly I need same data context only while creating single object with the several DataContext arguments. For every new Service class I need new DataContext.
Sergejus
A: 

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...

Bryce Fischer