我在侦听消息队列的服务器上运行服务。收到消息后,将启动一个新线程,并将消息传递到该线程进行处理。
我定义了一个接口,该接口可提供当前用户的访问权限,以供消息处理所用的各种类使用:
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);
});
我想这可以使用Lifetime Scoping来完成,但是我无法在静态类中定义它并在每个线程中设置User,因为它可能破坏另一个进程。这是我对实现的最佳猜测?
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中实现此方法的正确方法吗?还有另一种更正确的模式吗?
答案 0 :(得分:3)
让我们从您的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
类重写为以下内容:
public class AspNetUserContext : IUserContext
{
User CurrentUser => (User)HttpContext.Current.Session[USER_CONTEXT];
}
这允许此特定于ASP.NET的IUserContext
实现注册如下:
container.RegisterInstance<IUserContext>(new AspNetUserContext());
当然,前一个方法不能解决Windows Service中的问题,但前一个方法确实为解决方案奠定了基础。
对于Windows服务,您还需要一个自定义实现(适配器):
public class ServiceUserContext : IUserContext
{
User CurrentUser { get; set; }
}
此实现要简单得多,这里的ServiceUserContext
的{{1}}属性是可写的属性。这可以轻松解决您的问题,因为您现在可以执行以下操作:
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);
.....
}
)将运行时数据提供给对象图。