Как порождать процессы дочерних потоков при использовании общих сущностей, внедряемых через Unity? - PullRequest
2 голосов
/ 14 марта 2012

У меня есть решение ASP.Net MVC3, запускающее процесс пакетного импорта, который извлекает данные из веб-службы.Для каждой строки / цикла процесс должен отправить до четырех электронных писем.Я хотел бы отключить эти письма в фоновых темах, чтобы основной поток не должен был ждать отправки писем.Дочерний поток электронной почты должен обновить таблицу аудита базы данных при завершении или сбое отправки электронной почты.

Проблема, с которой я сталкиваюсь, заключается в том, что я использую Unity для внедрения класса IEmailer в мой основной поток процесса, который также назначает текст данных «основной поток процесса» в класс emailer.Таким образом, я получаю сообщения об ошибках, когда текстовый текстовый текст уже закрыт, когда электронная почта пытается обновить таблицу аудита, если основной цикл уже завершен (вероятный сценарий).

Как сказать Unity назначить новый текстовый текст для моих новых потоков электронной почты, или как я могу указать моему классу электронной почты использовать другой контейнер для единства (я думаю, настроенный с помощью Transient datacontext)?)?

Вот мой урезанный код.(Я понимаю, что мог бы просто создать экземпляр 'new MyDataContext ()' внутри программы электронной почты, но определенно есть лучший способ).

Любая помощь, предложения, идеи или комментарии будут высоко оценены - спасибо!

Контейнер IOC

this.unityContainer = new UnityContainer()
    .RegisterType<IDataProvider, DataProvider>()
    .RegisterType(typeof(IEmailer), typeof(Emailer))
    .RegisterType<DbContext, MyDataContext>(new HierarchicalLifetimeManager());

Класс импорта (основной поток)

public class DataSyncer : IDataSyncer
{
    public DataSyncer(IDataProvider dataProvider, IEmailer emailer)            {
            this.dataProvider = dataProvider;
            this.emailer = emailer;
        }

    public void Import(Guid key)        {
        // some import code
        emailer.EmailAddress = "someone@somewhere.com";
        emailer.Subject = "subject line";
        new Thread(emailer.SendMail).Start(); // send email in new thread
    }
}

Класс Emailer (для дочерних потоков)

public class Emailer : IEmailer
{
    [Dependency]
    public IDataProvider DataProvider { get; set; }
    // etc 
} 

DataProvider (содержит datacontext через инъекцию ctor)

public DataProvider(MyDataContext context, // etc) { // etc } 

1 Ответ

1 голос
/ 14 марта 2012

Я пытаюсь перефразировать ваше объяснение, чтобы понять, правильно ли я понял.

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

Электронная почта внедряется в ваш импортер, и оба имеют зависимость от класса, полученного из DbContext? Это тот же самый экземпляр DbContext? Если так: почему вы делитесь этим экземпляром? Разве не каждая задача отправки электронной почты независима от всех других задач? Если это так, удалите HierarchicalLifetimeManager.

Вы используете инъекцию свойства для IDataProvider. Я понял, что это обязательная зависимость. Если это так, вы должны использовать конструктор инъекций, как вы уже использовали для других классов. Кстати: Не используйте DependencyAttribute. Вы также можете настроить внедрение свойства, используя InjectionProperty в своем звонке на RegisterType.


Обновление

Насколько я знаю, Unity никогда не убирает за собой. Это значит, что я не ожидал, что он все равно позвонит на Dispose на ваш DbContext. У вас есть ссылка, где говорится, что HierarchicalLifetimeManager правильно располагает объекты? Мне было бы очень интересно прочитать это!

HierarchicalLifetimeManager работает так же, как и ContainerControlledLifetimeManager, если вы не имеете дело с дочерними контейнерами. Это в основном означает, что у вас есть один экземпляр вашего контекста во всех потоках. Если вы просто удалите этот менеджер времени жизни, вы получите новый экземпляр всякий раз, когда он понадобится в качестве зависимости. Это должно решить вашу проблему.

Если вам нужно позаботиться об удалении ваших экземпляров контекста, я бы вставил фабрику для контекста вместо экземпляра. Просто объявите параметр ctor равным Type Func<MyDataContext> Unity автоматически сгенерирует для вас делегата (эта функция называется автоматические фабрики). Тогда вы можете использовать using(var ctx = dbContextFactory()) { ... }.

...