3

我有一個關於使用SimpleInjector或任何其他IoC框架的UoW模式的問題。應當指出的我已經閱讀了關於它的計算器不同的線程,更具體這兩個線程:Mixed lifestyle for Per Thread and Per Web Request with Simple InjectorHow to configure Simple Injector to run background threads in ASP.NET MVC單元在WCF中使用Quartz(線程)

我正在開發一個WCF服務。該服務依賴於IUnitOfWork。我可以在SimpleInjector中註冊帶有RegsiterPerWcfOperation擴展的IUnitOfWork。然而,我的WCF服務還從Quartz調度器框架中衍生出一些新的作業(線程)。其中一個作業也包含對我的IUnitOfWork的依賴,因此我無法使用RegsiterPerWcfOperation。我可以做像計算器的一個線程說,和我的註冊IUnitOfWork這樣的:

ScopedLifestyle hybridLifestyle = Lifestyle.CreateHybrid(
    () => OperationContext.Current != null || HttpContext.Current != null, 
    new WcfOperationLifestyle(), 
    new LifetimeScopeLifestyle()); 

container.Register<IUnitOfWork, UnitOfWork<TEntities>>(hybridLifestyle); 

首先我不知道在OperationContext.Current如果無效檢查是正確的,因爲我的WCF服務不運行在AspNetCompatibilityMode,因爲NET.TCP綁定 - 所以HttpContext.Current的空檢查不會幫助我,除非我運行我的WCF服務AspNetCompatibilityMode = true

好了,所以我的石英依賴已在容器中,我已經創建一個工廠,所以這個框架可以這樣創建我IJob實例的實例:

public class SimpleInjectorJobFactory : IJobFactory 
{ 
    private static readonly ILog _log = 
     LogManager.GetLog(typeof(SimpleInjectorJobFactory)); 
    private readonly Container _container; 

    public SimpleInjectorJobFactory(Container container) { 
     _container = container; 
    } 

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { 
     IJobDetail jobDetail = bundle.JobDetail; 
     Type jobType = jobDetail.JobType; 

     try 
     { 
      if (_log.IsDebugEnabled) { 
       _log.Debug(string.Format("Producing Instance of job: {0}, class: {1}", 
        jobDetail.Key, jobType.FullName)); 
      } 

      // Return job registrated in container 
      return (IJob)_container.GetInstance(jobType); 
     } 
     catch (Exception ex) 
     { 
      _log.Error("Problem instantiating class", ex); 
      throw new SchedulerException("Problem instantiating class", ex); 
     } 
    } 

    public void ReturnJob(IJob job) { } 
} 

的IJob外觀的實現像這樣:

public class MyJob : IJob 
{ 
    private static readonly ILog _log = LogManager.GetLog(typeof(MyJob)); 
    private readonly IUnitOfWork _unitOfWork; 

    public MyJob(IUnitOfWork unitOfWork) { 
     _unitOfWork = unitOfWork; 
    } 

    public void Execute(IJobExecutionContext context) { 
     try 
     { 
      _unitOfWork.GetSomeDate().... //PSEUDO 
     } 
     catch (Exception ex) 
     { 
      _log.Error("Could not execute " + context.JobDetail.JobType, ex); 
     } 
    } 
} 

如果我應遵循從指定線程上面的建議,我會需要創建一個新的生命週期範圍,如:

try 
{ 
    using (container.BeginLifetimeScope()) 
    { 
     var uow = container.GetInstance<IUnitOfWork>(); 
     uow.GetSomeDate().... //PSEUDO 
    } 
} 

但是,使用這種解決方案,我不會與IoC框架緊密耦合嗎?我似乎無法找到解決此問題的適當方法。我讀了一些關於裝飾器的內容,但是我似乎無法弄清楚如何使用它們。我希望我能夠得到一個很好的例子,我該如何解決我的問題。

回答

3

你快到了。您需要創建了混合的生活方式:

var hybridLifestyle = Lifestyle.CreateHybrid(
    container.GetCurrentWcfOperationScope() != null, 
    new WcfOperationLifestyle(), 
    new LifetimeScopeLifestyle()); 

你需要調用container.GetCurrentWcfOperationScope()container.GetCurrentLifetimeScope(),以確定選擇哪個生活方式。

此外,您應該絕對不要在您的MyJob(或任何工作實現)中引用Container。那是an anti-pattern。相反,你應該建立一個IJob裝飾,增加了生命週期作用域行爲工作:

// This class is part of your Composition Root 
public class LifetimeScopeJobDecorator : IJob { 
    private readonly IJob _decoratee; 
    private readonly Container _container; 

    public LifetimeScopeJobDecorator(IJob decoratee, Container container) { 
     _decoratee = decoratee; 
     _container = container; 
    } 

    public void Execute(IJobExecutionContext context) { 
     using (_container.BeginLifetimeScope()) { 
      _decoratee.Execute(context); 
     } 
    } 
} 

現在也有適用於裝飾作業的SimpleInjectorJobFactory返回兩種方式。最簡單的方法是手動添加SimpleInjectorJobFactory.NewJob方法內部裝飾:

var job = (IJob)_container.GetInstance(jobType); 
return new LifetimeScopeJobDecorator(job, _container); 

更漂亮那就讓簡單的注射器做裝修的你,但是這將是你的情況下,更多的代碼。下面就來做到這一點:

Type[] jobTypes = /* fetch IJob implementations here */; 

// Instead of calling jobTypes.ToList().ForEach(container.Register), we register the 
// types as a collection of IJob types. 
container.RegisterAll<IJob>(types); 

// Here we register the decorator 
container.RegisterDecorator(typeof(IJob), typeof(LifetimeScopeJobDecorator)); 

// We create a jobFactory delegate. 
// we use Lazy<T> because at this stage in the application the container might not 
// be fully initialized yet. 
var jobs = new Lazy<IEnumerable<IJob>>(() => container.GetAllInstance<IJob>()); 
var jobIndexes = types.ToDictionary(t => t, t => types.IndexOf(t)); 
Func<Type, IJob> jobFactory = jobType => jobs.Value.ElementAt(jobIndexes[jobType]); 

// And pass that job factory on to the SimpleInjectorJobFactory. 
var factory = new SimpleInjectorJobFactory(jobFactory); 

// Inside the SimpleInjectorJobFactory: 
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) 
    // ... 
    // replace "return (IJob)_container.GetInstance(jobType);" with:   
    return _jobFactory(jobType); 
    // ... 
} 

這最後的解決方案比較複雜,但是當你有多個裝飾,特別是如果他們需要立足於實現類型有條件應用這種方法變得非常有趣。這樣,工廠不需要知道任何有關應用裝飾器的信息。

+0

「container.GetCurrentWcfOperationScope()」和修飾符部分正是我所缺少的。很好的例子 - 感謝您的幫助! –