14

Tôi đang sử dụng SimpleInjector làm thư viện IoC của mình. Tôi đăng ký DbContext theo yêu cầu web và nó hoạt động tốt. Nhưng có một nhiệm vụ mà tôi chạy nó trong một chủ đề nền. Vì vậy, tôi có một vấn đề để tạo ra DbContext trường hợp. ví dụ.Lối sống hỗn hợp cho mỗi chủ đề và mỗi yêu cầu web với đầu phun đơn giản

  1. Service1 có một thể hiện của DbContext
  2. Service2 có một thể hiện của DbContext
  3. Service1Service2 chạy từ sợi nền.
  4. Service1 lấy về một thực thể và vượt qua nó để Service2
  5. Service2 sử dụng mà tổ chức nào, nhưng thực thể được tách ra từ DbContext

Trên thực tế vấn đề là ở đây: Service1.DbContext là chênh lệch từ Service2.DbContext.

Dường như khi tôi chạy tác vụ trong một chuỗi riêng biệt trong ASP.NET MVC, SimpleInjector tạo một phiên bản mới DbContext cho mỗi cuộc gọi. Trong khi một số thư viện IoC (ví dụ: StructureMap) có lối sống hỗn hợp cho mỗi yêu cầu trên mỗi web, có vẻ như SimpleInjector chưa có. Tôi có đúng không?

Bạn có ý tưởng nào để giải quyết vấn đề này trong SimpleInjector không? Cảm ơn trước.

EDIT:

dịch vụ của tôi đang ở đây:

class Service1 : IService1 { 
    public Service1(MyDbContext context) { } 
} 

class Service2 : IService2 { 
    public Service2(MyDbContext context, IService1 service1) { } 
} 

class SyncServiceUsage { 
    public SyncServiceUsage(Service2 service2) { 
     // use Service2 (and Service1 and DbContext) from HttpContext.Current 
    } 
} 

class AsyncServiceUsage { 
    public AsyncServiceUsage(Service2 service2) { 
     // use Service2 (and Service1 and DbContext) from background thread 
    } 
} 

public class AsyncCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> where TCommand : ICommand { 

    private readonly Func<ICommandHandler<TCommand>> _factory; 

    public AsyncCommandHandlerDecorator(Func<ICommandHandler<TCommand>> factory) { 
     _factory = factory; 
    } 

    public void Handle(TCommand command) { 
     ThreadPool.QueueUserWorkItem(_ => { 
      // Create new handler in this thread. 
      var handler = _factory(); 
      handler.Handle(command); 
     }); 
    } 
} 

void InitializeSimpleInjector() { 
    register AsyncCommandHandlerDecorator for services (commands actually) that starts with "Async" 
} 

tôi sử dụng Service2 đôi và AsyncService2 thời điểm khác.

+0

Liên quan: http://stackoverflow.com/questions/11041601/simple-injector-multi-threading-in-mvc3-asp-net – Steven

+0

Related: http://stackoverflow.com/questions/10304023/simpleinjector-is -this-the-right-way-to-registermanyforopengeneric-when-i-have – Steven

Trả lời

17

Dường như khi tôi chạy tác vụ trong một chuỗi riêng biệt trong ASP.NET MVC, SimpleInjector tạo phiên bản DbContext mới cho mỗi cuộc gọi.

Hành vi của các RegisterPerWebRequest lối sống của Simple Injector v1.5 và dưới đây là để trở về một trường hợp thoáng qua khi trường hợp được yêu cầu bên ngoài bối cảnh của một yêu cầu web (nơi HttpContext.Current là null). Trả về một thể hiện thoáng qua là một lỗ hổng thiết kế trong Simple Injector, vì điều này làm cho nó dễ dàng che giấu việc sử dụng không đúng cách. Version 1.6 of the Simple Injector sẽ ném một ngoại lệ thay vì trả lại không chính xác một phiên bản tạm thời, để giao tiếp rõ ràng rằng bạn đã định cấu hình sai vùng chứa.

Trong khi một số thư viện IoC (ví dụ StructureMap) có một lối sống hỗn hợp cho mỗi thread-per-WebRequest, có vẻ như đơn giản Injector có không phải là một

Đúng là Injector Simple không có hỗ trợ tích hợp cho lối sống hỗn hợp vì một vài lý do. Trước hết nó là một tính năng khá kỳ lạ mà không nhiều người cần. Thứ hai, bạn có thể trộn lẫn hai hoặc ba lối sống với nhau, vì vậy đó sẽ là một sự kết hợp vô tận của các giống lai.Và cuối cùng, nó là (khá) dễ dàng đăng ký này cho mình.

Mặc dù bạn có thể trộn Per Web Request với Per Thread lối sống, nó có lẽ sẽ tốt hơn khi bạn kết hợp mỗi Request Web với Per Lifetime Scope, vì với Phạm vi trọn đời bạn bắt đầu một cách rõ ràng và kết thúc phạm vi (và có thể xử lý các DbContext khi phạm vi kết thúc) .

Từ Simple Injector 2 và bật, bạn có thể dễ dàng kết hợp bất kỳ số lượng lối sống nào với nhau bằng phương pháp Lifestyle.CreateHybrid. Dưới đây là một ví dụ:

var hybridLifestyle = Lifestyle.CreateHybrid(
    () => HttpContext.Current != null, 
    new WebRequestLifestyle(), 
    new LifetimeScopeLifestyle()); 

// Register as hybrid PerWebRequest/PerLifetimeScope. 
container.Register<DbContext, MyDbContext>(hybridLifestyle); 

Có một câu hỏi Stackoverflow mà đi vào chủ đề này một chút sâu hơn, bạn có thể muốn có một cái nhìn: Simple Injector: multi-threading in MVC3 ASP.NET

CẬP NHẬT

Về cập nhật của bạn. Bạn gần như ở đó. Các lệnh chạy trên chuỗi nền cần phải chạy trong phạm vi Phạm vi lâu dài, vì vậy, bạn sẽ phải bắt đầu lệnh một cách rõ ràng. Bí quyết ở đây là gọi BeginLifetimeScope trên chuỗi mới, nhưng trước khi trình xử lý lệnh thực tế (và các phụ thuộc của nó) được tạo. Nói cách khác, cách tốt nhất để làm điều này là bên trong một trang trí.

Giải pháp đơn giản nhất là để cập nhật AsyncCommandHandlerDecorator của bạn để thêm phạm vi:

public class AsyncCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> where TCommand : ICommand 
{ 
    private readonly Container _container; 
    private readonly Func<ICommandHandler<TCommand>> _factory; 

    public AsyncCommandHandlerDecorator(Container container, 
     Func<ICommandHandler<TCommand>> factory) 
    { 
     _container = container; 
     _factory = factory; 
    } 

    public void Handle(TCommand command) 
    { 
     ThreadPool.QueueUserWorkItem(_ => 
     { 
      using (_container.BeginLifetimeScope()) 
      { 
       // Create new handler in this thread 
       // and inside the lifetime scope. 
       var handler = _factory(); 
       handler.Handle(command); 
      } 
     }); 
    } 
} 

Purists rằng chủ trương SOLID nguyên tắc này sẽ hét lên rằng lớp này đang vi phạm các Single Responsibility Principle, kể từ khi trang trí này cả hai chạy lệnh trên một chủ đề mới và bắt đầu một phạm vi đời mới. Tôi sẽ không lo lắng nhiều về điều này, vì tôi nghĩ rằng có một mối quan hệ chặt chẽ giữa bắt đầu một chủ đề nền và bắt đầu một phạm vi đời (bạn sẽ không sử dụng một mà không có khác anyway). Nhưng vẫn còn, bạn có thể dễ dàng rời khỏi AsyncCommandHandlerDecorator ảnh hưởng và tạo ra một mới LifetimeScopedCommandHandlerDecorator như sau:

public class LifetimeScopedCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> where TCommand : ICommand 
{ 
    private readonly Container _container; 
    private readonly Func<ICommandHandler<TCommand>> _factory; 

    public LifetimeScopedCommandHandlerDecorator(Container container, 
     Func<ICommandHandler<TCommand>> factory) 
    { 
     _container = container; 
     _factory = factory; 
    } 

    public void Handle(TCommand command) 
    { 
     using (_container.BeginLifetimeScope()) 
     { 
      // The handler must be created inside the lifetime scope. 
      var handler = _factory(); 
      handler.Handle(command); 
     } 
    } 
} 

Thứ tự mà các trang trí đã được đăng ký là tất nhiên rất cần thiết, vì AsyncCommandHandlerDecoratorphải quấn LifetimeScopedCommandHandlerDecorator. Điều này có nghĩa rằng việc đăng ký LifetimeScopedCommandHandlerDecorator phải đến đầu tiên:

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(LifetimeScopedCommandHandlerDecorator<>), 
    backgroundCommandCondition); 

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(AsyncCommandHandlerDecorator<>), 
    backgroundCommandCondition); 

This old Stackoverflow question cuộc đàm phán về vấn đề này một cách chi tiết hơn. Bạn chắc chắn nên xem xét.

+1

Cảm ơn + Steven Tôi đã đọc liên kết khác và nó hữu ích và hữu ích. Ngoài ra, tôi đã bỏ phiếu cho câu trả lời và đặt câu hỏi tất cả, để cải thiện danh tiếng 'SimpleInjector'. Chúc may mắn –

+0

WTF Tôi đã nhầm lẫn! Tôi chỉnh sửa câu hỏi của mình. Bạn có thể cho tôi biết về cách sử dụng không? Các đối tượng 'Service1' và' Service2', được sử dụng trong cả chuỗi chính (yêu cầu web) và chuỗi nền. Tôi muốn có thể làm điều này. Và tôi tiêm 'DbContext' vào chúng bằng cách tiêm ctor. Tôi có nên thay đổi triển khai của mình không? –

+1

@Javad_Amiry: Tôi xin lỗi vì trả lời trễ của tôi, nhưng ngay cả các nhà phát triển cũng cần đôi khi nghỉ ngơi ;-). Tôi đã cập nhật câu trả lời của mình. Tôi hi vọng cái này giúp được. – Steven