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ì AsyncCommandHandlerDecorator
phả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.
Liên quan: http://stackoverflow.com/questions/11041601/simple-injector-multi-threading-in-mvc3-asp-net – Steven
Related: http://stackoverflow.com/questions/10304023/simpleinjector-is -this-the-right-way-to-registermanyforopengeneric-when-i-have – Steven