多线程应用程序中基于上下文的依赖注入
Context Based Dependency Injection in Multi-Threaded Application
我在侦听消息队列的服务器上有一个服务 运行。当接收到消息时,启动一个新线程并将消息传递给该线程进行处理。
我已经定义了一个接口,它提供当前用户访问用于消息处理的各种 classes 的消费:
public interface IUserContext {
User CurrentUser { get; }
}
该用户可能会在不同的消息之间切换。
我的问题是如何在 SimpleInjector 中注册 IUserContext 的实现,以便 CurrentUser 正确返回传入消息中包含的正确用户 属性?
在我的 Asp.Net 应用程序中,这是通过以下方式完成的:
container.Register<IUserContext>(() => {
User user = null;
try {
user = HttpContext.Current?.Session[USER_CONTEXT] as IUser;
}
catch { }
return new UserContext(user);
});
我想这可以使用生命周期范围来完成,但我不能在静态 class 中定义它并在每个线程中设置用户,因为它可能会破坏另一个进程。这是我对实现的最佳猜测?
public static Func<User> UserContext { get; set; }
然后在新线程中我的代码中:
using (container.BeginLifetimeScope()) {
.....
var user = GetUserContext(message);
UserContextInitializer.UserContext = () => new UserContext(user);
.....
}
然后注册看起来像这样:
container.Register<IUserContext>(() => UserContextInitializer.UserContext);
撇开线程安全不谈,这是在 SimpleInjector 中实现它的正确方法吗?还有其他更正确的模式吗?
让我们从您的 ASP.NET-特定 IUserContext
注册开始:
container.Register<IUserContext>(() => {
User user = null;
try {
user = HttpContext.Current?.Session[USER_CONTEXT] as IUser;
}
catch { }
return new UserContext(user);
});
这个注册是有问题的,因为UserContext
组件依赖于运行时数据的可用性,而如here所述,对象图的创建应该与运行时数据和运行时数据分开应该流经系统。
换句话说,您应该将 UserContext
class 重写为以下内容:
public class AspNetUserContext : IUserContext
{
User CurrentUser => (User)HttpContext.Current.Session[USER_CONTEXT];
}
这允许这个 ASP.NET 特定的 IUserContext
实现按如下方式注册:
container.RegisterInstance<IUserContext>(new AspNetUserContext());
当然前面的并没有解决你Windows服务中的问题,但是前面的确实为解决这个问题打下了基础
对于 Windows 服务,您还需要自定义实现(适配器):
public class ServiceUserContext : IUserContext
{
User CurrentUser { get; set; }
}
这个实现要简单得多,这里 ServiceUserContext
的 CurrentUser
属性 是可写的 属性。这优雅地解决了您的问题,因为您现在可以执行以下操作:
// Windows Service Registration:
container.Register<IUserContext, ServiceUserContext>(Lifestyle.Scoped);
container.Register<ServiceUserContext>(Lifestyle.Scoped);
// Code in the new Thread:
using (container.BeginLifetimeScope())
{
.....
var userContext = container.GetInstance<ServiceUserContext>();
// Set the user of the scoped ServiceUserContext
userContext.CurrentUser = GetUserContext(message);
var handler = container.GetInstance<IHandleMessages<SomeMessage>>();
handler.Handle(message);
.....
}
在这里,解决方案也是将对象图的创建与运行时数据的使用分开。在这种情况下,运行时数据在构建后提供给对象图(即使用userContext.CurrentUser = GetUserContext(message)
)。
我在侦听消息队列的服务器上有一个服务 运行。当接收到消息时,启动一个新线程并将消息传递给该线程进行处理。
我已经定义了一个接口,它提供当前用户访问用于消息处理的各种 classes 的消费:
public interface IUserContext {
User CurrentUser { get; }
}
该用户可能会在不同的消息之间切换。
我的问题是如何在 SimpleInjector 中注册 IUserContext 的实现,以便 CurrentUser 正确返回传入消息中包含的正确用户 属性?
在我的 Asp.Net 应用程序中,这是通过以下方式完成的:
container.Register<IUserContext>(() => {
User user = null;
try {
user = HttpContext.Current?.Session[USER_CONTEXT] as IUser;
}
catch { }
return new UserContext(user);
});
我想这可以使用生命周期范围来完成,但我不能在静态 class 中定义它并在每个线程中设置用户,因为它可能会破坏另一个进程。这是我对实现的最佳猜测?
public static Func<User> UserContext { get; set; }
然后在新线程中我的代码中:
using (container.BeginLifetimeScope()) {
.....
var user = GetUserContext(message);
UserContextInitializer.UserContext = () => new UserContext(user);
.....
}
然后注册看起来像这样:
container.Register<IUserContext>(() => UserContextInitializer.UserContext);
撇开线程安全不谈,这是在 SimpleInjector 中实现它的正确方法吗?还有其他更正确的模式吗?
让我们从您的 ASP.NET-特定 IUserContext
注册开始:
container.Register<IUserContext>(() => { User user = null; try { user = HttpContext.Current?.Session[USER_CONTEXT] as IUser; } catch { } return new UserContext(user); });
这个注册是有问题的,因为UserContext
组件依赖于运行时数据的可用性,而如here所述,对象图的创建应该与运行时数据和运行时数据分开应该流经系统。
换句话说,您应该将 UserContext
class 重写为以下内容:
public class AspNetUserContext : IUserContext
{
User CurrentUser => (User)HttpContext.Current.Session[USER_CONTEXT];
}
这允许这个 ASP.NET 特定的 IUserContext
实现按如下方式注册:
container.RegisterInstance<IUserContext>(new AspNetUserContext());
当然前面的并没有解决你Windows服务中的问题,但是前面的确实为解决这个问题打下了基础
对于 Windows 服务,您还需要自定义实现(适配器):
public class ServiceUserContext : IUserContext
{
User CurrentUser { get; set; }
}
这个实现要简单得多,这里 ServiceUserContext
的 CurrentUser
属性 是可写的 属性。这优雅地解决了您的问题,因为您现在可以执行以下操作:
// Windows Service Registration:
container.Register<IUserContext, ServiceUserContext>(Lifestyle.Scoped);
container.Register<ServiceUserContext>(Lifestyle.Scoped);
// Code in the new Thread:
using (container.BeginLifetimeScope())
{
.....
var userContext = container.GetInstance<ServiceUserContext>();
// Set the user of the scoped ServiceUserContext
userContext.CurrentUser = GetUserContext(message);
var handler = container.GetInstance<IHandleMessages<SomeMessage>>();
handler.Handle(message);
.....
}
在这里,解决方案也是将对象图的创建与运行时数据的使用分开。在这种情况下,运行时数据在构建后提供给对象图(即使用userContext.CurrentUser = GetUserContext(message)
)。