2017-11-03 31 views
0

ASP.NETコア2から始めています。メッセージハンドラにメッセージを書き込む要求に関係する各要素の方法が必要です。AsyncLocalを使用してリクエスト情報を保存しますか?

いくつかの制限事項:

  • 我々はHttpContext.ItemsHttpContextは、我々はコントローラの内部で使用しているクラスでは使用できません、と私たちはそこに全体のコンテキストを転送するために好きではない)を使用しません。
  • 私たちは複数の異なるサービスを持っているので、コンストラクタにあまりにも多くのパラメータを持つので、依存性のない注入を使用しようとしました。
  • また、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); 
} 

このコードは動作しますが、それが落とし穴です我々は知りません?または、より良い解決策がありますか?

答えて

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); 
    } 
} 

そして、それがすべてです。 staticsを導入する必要はありませんが、完全な依存性注入を使用すると、コードを完全にテスト可能にし、すべての依存関係をクリアすることができます。

私たちは複数の異なるサービスを持っている場合、コンストラクタに多くのパラメータを持たなければならないので、依存性注入なしでそれを使用しようとしました。

single responsibility principleの違反については、コンストラクタパラメータが多すぎると明確な記号です。あなたのサービスが多くの依存関係を取っていると分かったら、それを分割することを検討するべきです。 refactoring to facade servicesも考えてみてください。

関連する問題