我们从ASP.NET Core 2开始。我们需要一种方法,用于将消息写入消息处理程序的请求中涉及的每个元素。
一些限制:
HttpContext.Items
(HttpContext
在我们在Controller中使用的课程中不可用,我们不想在那里转发整个上下文) async
/ await
。我们尝试了使用AsyncLocal<T>
的方法。
为此我们创建了一个类:
public class NotificationExecutionContext
{
private static readonly AsyncLocal<NotificationHandler> NotificationHandler =
new AsyncLocal<NotificationHandler>();
public static NotificationHandler Instance =>
NotificationHandler.Value ?? (NotificationHandler.Value = new NotificationHandler());
}
将创建一个NotificationHandler
,每个请求都应该存在。 NotificationHandler
是一个简单的类,您可以在其中添加/从集合中获取消息:
public class NotificationHandler : INotificationHandler
{
public List<NotificationBase> Notifications { get; } = new List<NotificationBase>();
public void AddNotification(NotificationBase notification)
{
Notifications.Add(notification);
}
public void AddNotificationRange(List<NotificationBase> notifications)
{
Notifications.AddRange(notifications);
}
}
使用此解决方案,我可以轻松获取此上下文的NotificationHandler
并添加通知。
NotificationExecutionContext.Instance.AddNotification(new NotificationBase(){..})
在中间件中,我们正在等待Response.OnStarting()
事件,然后我们从NotificationHandler
获取所有消息并添加响应头:
public async Task Invoke(HttpContext context)
{
var e = NotificationExecutionContext.Instance; // Required so that notification handler will be created in this context
context.Response.OnStarting((state) =>
{
List<NotificationBase> notifications = NotificationExecutionContext.Instance.Notifications;
if (notifications.Count > 0)
{
string messageString = JsonConvert.SerializeObject(notifications, Formatting.None);
context.Response.Headers.Add("NotificationHeader", messageString);
}
return Task.FromResult(0);
}, null);
await Next(context);
}
此代码有效,但是我们不知道有哪些陷阱?或者有更好的解决方案吗?
答案 0 :(得分:2)
你应该不使用这样的静态单例。在代码中具有类似静态依赖性会破坏依赖注入的整个目的。你应该在这里接受依赖注入,这会使这个变得非常简单:
/* in Startup.ConfigureServices */
// register the notification handler as a scoped dependency, this automatically makes the
// instance shared per request but not outside of it
services.AddScoped<INotificationHandler, NotificationHandler>();
/* in Startup.Configure */
// register your custom middleware
app.Use<NotificationHandlerMiddleware>();
public class NotificationHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly NotificationHandler _notificationHandler;
public NotificationHandlerMiddleware(RequestDelegate next, INotificationHandler notificationHandler)
{
_next = next;
_notificationHandler = notificationHandler;
}
public void Invoke(HttpContext context)
{
// do whatever with _notificationHandler
await _next(context);
}
}
就是这样。无需引入静态,但使用完全依赖注入使您的代码完全可测试并且所有依赖项都清晰。
我们试图在没有依赖注入的情况下使用它,因为如果我们有多个不同的服务,我们将在构造函数中拥有许多参数。
构造函数参数太多是违反single responsibility principle的明显标志。如果您发现您的服务需要很多依赖项,那么您应该考虑将其拆分。您可能还想考虑refactoring to facade services。