Внедрить один и тот же экземпляр DataContext в несколько типов с помощью Unity - PullRequest
6 голосов
/ 22 мая 2009

Предположим, у меня есть интерфейс IRepository и его реализация SqlRepository, который принимает в качестве аргумента LINQ to SQL DataContext. Предположим также, что у меня есть интерфейс IService и службы его реализации, которые используют три IRepository, IRepository и IRepository. Демо-код ниже:

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>) { ... }
}

Можно ли при создании класса Service внедрить все три репозитория с одинаковым DataContext?

Ответы [ 4 ]

7 голосов
/ 04 апреля 2011

Все, что вам нужно сделать, это убедиться, что при регистрации Datacontext в вашем контейнере Unity используйте PerResolveLifetimeManager в конфигурации:

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

или в коде:

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

тогда всякий раз, когда контейнер разрешает Service, любые зависимости, которые также требуют DataContext, будут предоставляться точно такими же. Но следующий запрос на разрешение Service создаст новый DataContext.

3 голосов
/ 29 мая 2010

Я думаю, я знаю, что ты хочешь сделать. Я в одной лодке и пытаюсь найти решение.

Уровень My Service выполняет операции с входящими запросами, и то, что он делает, зависит от содержимого. Он передает его в серию классов цепочки ответственности. Я хочу, чтобы один и тот же контекст передавался всем классам в течение срока службы метода с именем

Вы можете указать PerResolveLifetimeManager. Пока что похоже на работу с моими тестами:

Класс обслуживания:

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 реализуется двумя классами и выполняет шаблон цепочки ответственности:

    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");
    }
}

Как видите, оба обработчика принимают экземпляр IDataContext, который я хочу быть одинаковым в обоих из них. Handler2 также принимает экземпляр IHandler для передачи управления (здесь он демонстрирует и то, и другое, но на самом деле только один обработал бы запрос ...)

IDataContext. В конструкторе я инициализирую Guid и выводю его во время его работы, чтобы я мог видеть, использует ли оба раза его вызванный один и тот же экземпляр:

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);
    }
}

Наконец, регистрация и вызов услуги:

    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();
    }

Это вывод, который я получаю:

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

Надеюсь, эта стена текста ответила на ваш вопрос ... Если не извините, это вдохновило на решение, которое мне нужно было реализовать ...

0 голосов
/ 29 мая 2009

Вы пытались использовать метод RegisterInstance () для контейнера Unity? Примерно так может работать:

публичная статическая UnityContainer CreateContainer () { Контейнер UnityContainer = новый 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;
    }

Теперь, каждый раз, когда этот контейнер пытается построить объект, которому нужен DataContext, будет передаваться один и тот же экземпляр. Вы даже можете настроить DataContext перед регистрацией его экземпляра.

UPDATE: Один из вариантов (сейчас я не знаю, действительно ли это хорошая практика, но это сработало для меня) - создать отдельный контейнер для каждого объекта, который вы собираетесь создать. Что-то вроде:

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>();

или более кратко:

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

Ну, есть много способов сделать это, создать список, используя фабричный шаблон. Надеюсь, это поможет

0 голосов
/ 23 мая 2009

Если я правильно понимаю ваш вопрос (и если вы используете единство ... я полагаю, вы делаете это, потому что вы пометили его единством), вы могли бы сделать что-то вроде этого:

В ваших реализациях репозитория,

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

но затем вы должны пометить конструктор служб таким же образом и использовать контейнер для разрешения ваших служб, а также хранилища. DataContext также должен находиться в контейнере, чтобы он работал.

Альтернативный подход - сделать что-то подобное с вашим хранилищем:

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

это скажет Unity для вызова этого метода, если в конструкторе сервисов вы будете использовать Unity с методом BuildUp ... что-то вроде этого:

unitycontainer.BuildUp<IRepository>(repository);

Полагаю, это не совсем то, что вы ищете, но, пожалуйста, скажите мне, нахожусь ли я на правильном пути, и я посмотрю, смогу ли я вам помочь ...

Приветствия / J

...