C# 多线程应用中基于上下文的依赖注入

C# 多线程应用中基于上下文的依赖注入,c#,dependency-injection,simple-injector,constructor-injection,C#,Dependency Injection,Simple Injector,Constructor Injection,我有一个在服务器上运行的服务,它监听消息。收到消息后,将启动一个新线程,并将消息传递给该线程进行处理 我已经定义了一个接口,该接口为当前用户提供用于消息处理的各种类别的访问权限: public interface IUserContext { User CurrentUser { get; } } 此用户可能会在不同的消息中更改 我的问题是如何在SimpleInjector中注册IUserContext的实现,以便CurrentUser属性正确返回传入消息中包含的正确用户 在我的Asp

我有一个在服务器上运行的服务,它监听消息。收到消息后,将启动一个新线程,并将消息传递给该线程进行处理

我已经定义了一个接口,该接口为当前用户提供用于消息处理的各种类别的访问权限:

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);
});
然后,注册将如下所示:

container.Register<IUserContext>(() => UserContextInitializer.UserContext);
container.Register(()=>UserContextInitializer.UserContext);

撇开线程安全不谈,这是在SimpleInjector中实现这一点的正确方法吗?还有其他更正确的模式吗?

让我们从特定于ASP.NET的
IUserContext
注册开始:

container.Register(()=>{
User=null;
试一试{
user=HttpContext.Current?.Session[user\u CONTEXT]作为IUser;
}
捕获{}
返回新的UserContext(用户);
});
这种注册是有问题的,因为
UserContext
组件取决于运行时数据的可用性,而如上所述,对象图的创建应该与运行时数据分开,并且运行时数据应该流经系统

换句话说,您应该将
UserContext
类重写为以下内容:

container.Register<IUserContext>(() => {
    User user = null;
    try {
         user = HttpContext.Current?.Session[USER_CONTEXT] as IUser;
    }
    catch { }

    return new UserContext(user);
});
公共类AspNetUserContext:IUserContext
{
用户CurrentUser=>(用户)HttpContext.Current.Session[User_CONTEXT];
}
这允许按如下方式注册此ASP.NET特定的
IUserContext
实现:

container.RegisterInstance(新的AspNetUserContext());

当然,前面没有解决Windows服务中的问题,但前面的问题确实为解决方案奠定了基础。 对于Windows服务,您还需要自定义实现(适配器):

公共类ServiceUserContext:IUserContext { 用户当前用户{get;set;} } 这个实现要简单得多,这里的
ServiceUserContext
CurrentUser
属性是一个可写属性。这很好地解决了您的问题,因为您现在可以执行以下操作:

container.Register<IUserContext>(() => {
    User user = null;
    try {
         user = HttpContext.Current?.Session[USER_CONTEXT] as IUser;
    }
    catch { }

    return new UserContext(user);
});
//Windows服务注册:
容器。寄存器(生活方式。范围);
容器。寄存器(生活方式。范围);
//新线程中的代码:
使用(container.BeginLifetimeScope())
{
.....
var userContext=container.GetInstance();
//设置作用域为ServiceUserContext的用户
userContext.CurrentUser=GetUserContext(消息);
var handler=container.GetInstance();
handler.Handle(消息);
.....
}

在这里,解决方案也是将对象图的创建和运行时数据的使用分离开来。在这种情况下,运行时数据在构建后提供给对象图(即使用
userContext.CurrentUser=GetUserContext(message)
)。

我假设“correct”是指“线程安全”。您是否可以避免写入
userContext
?@Renat:这是个好主意,但想必
userContext
包含有用的状态。也许是一个不变的实现?@RobertHarvey感谢您花时间阅读我的问题。我实际上在寻找注入用户状态的正确方法,通过SimpleInjector使用对象实例化,用户状态可以在消息之间变化。我对我的问题进行了编辑,以使其更加清晰。@Renat:UserContext将随着消息的变化而变化。好吧,假设你的意思是“线程安全”,我相信@Renat的想法是正确的。设置它,以便SimpleInjector为您提供一个不可变的实例。感谢您抽出时间回答我的问题。因此ServiceUserContext提供了一个setter,可以在那里设置正确的UserContext,在新线程中,通过使用BeginLifetimeScope,将IUserContext的范围限制到该线程。太棒了,我应该想到的。