27

私は、静的クラスがグローバル情報の格納に使用されることに関して、SOで多くの悪い評判を得ていることに気づいています。(そして一般的に軽蔑されているグローバル変数)私は以下の私の例の良い代替案が何であるかを知りたいだけです...

WPFアプリを開発していますが、データベースから取得したデータの多くのビューは、現在ログインしているユーザーのIDに基づいてフィルター処理されます。同様に、私のアプリの特定のポイントは、「管理者」と見なされるユーザーのみがアクセスできるようにする必要があります。

現在、loggedInUserIdisAdminboolを静的クラスに格納しています。

私のアプリのさまざまな部分がこの情報を必要としていますが、この場合、なぜそれが理想的ではないのか、そして代替案は何であるのか疑問に思っています。立ち上がって実行するのは非常に便利なようです。

代替案として私が考えることができる唯一のことは、IoCコンテナーを使用して、このグローバル情報を必要とするクラスにシングルトンインスタンスを注入することです。その後、クラスはそのインターフェイスを介してこれと通信できます。しかし、これはやり過ぎ/分析の麻痺に私を導きますか?

洞察を事前に感謝します。


アップデート

したがって、必要に応じて「グローバル」情報をモックで提供するサービスに交換できるため、IoCを介した依存性注入に傾倒しています。残っているのは、注入されたオブジェクトがシングルトンであるか静的であるかどうかだと思います。:-)

これ以上の議論があるかどうかを確認するのを待っていますが、おそらくマークの答えを選ぶでしょう。そのような正しい方法はないと思います。建設的な代替案がないいくつかの同様の質問について、「これは悪い」「それは悪い」という発言がたくさんあるように思われるので、私を啓発するいくつかの議論を見ることに興味があります。


アップデート#2 それで、ロバートの答えを選んだのは、それが素晴らしい代替案であるためです(代替案は奇妙な言葉だと思います。おそらく、フレームワークに組み込まれているので、One True Wayが見ています)。静的クラス/シングルトンを作成する必要はありません(スレッド静的ですが)。

それでも私が興味を持っているのは、保存しなければならない「グローバル」データがユーザー認証とは何の関係もない場合に、これにどのように取り組むかということだけです。

4

3 に答える 3

12

シングルトンと静的データを忘れてください。そのアクセスのパターンは、いつかあなたを失敗させるでしょう。

独自のカスタムIPrincipalを作成し、ログインが適切な時点でThread.CurrentPrincipalをそれに置き換えます。通常、現在のIIdentityへの参照を保持します。

ユーザーがログオンするルーチンで、たとえば、ユーザーの資格情報を確認した場合は、カスタムプリンシパルをスレッドにアタッチします。

IIdentity currentIdentity = System.Threading.Thread.CurrentPrincipal.Identity;
System.Threading.Thread.CurrentPrincipal 
   = new MyAppUser(1234,false,currentIdentity);

ASP.NetHttpContext.Current.Userでは、同時に設定します

public class MyAppUser : IPrincipal
{
   private IIdentity _identity;

   private UserId { get; private set; }
   private IsAdmin { get; private set; } // perhaps use IsInRole

   MyAppUser(userId, isAdmin, iIdentity)
   {
      if( iIdentity == null ) 
         throw new ArgumentNullException("iIdentity");
      UserId = userId;
      IsAdmin = isAdmin;
      _identity = iIdentity;          
   }

   #region IPrincipal Members
   public System.Security.Principal.IIdentity Identity
   {
      get { return _identity; }
   }

   // typically this stores a list of roles, 
   // but this conforms with the OP question
   public bool IsInRole(string role)
   {  
      if( "Admin".Equals(role) )
         return IsAdmin;     

      throw new ArgumentException("Role " + role + " is not supported");
   }
   #endregion
}

これはそれを行うための好ましい方法であり、それは理由のためにフレームワークにあります。このようにして、標準的な方法でユーザーにアクセスできます。

また、ユーザーが匿名(不明)の場合にプロパティを追加して、匿名/ログイン認証が混在するシナリオをサポートすることもできます。

さらに:

  • 資格情報を取得/チェックするメンバーシップサービスを注入することにより、DI(依存関係注入)を引き続き使用できます。
  • リポジトリパターンを使用して、現在のMyAppUserにアクセスすることもできます(おそらく、MyAppUserへのキャストを作成しているだけですが、これには利点があります)
于 2009-08-11T22:03:20.090 に答える
2

静力学(シングルトンを含む)があなたにとって悪い理由を説明するSOに関する他の多くの答えがここにあるので、詳細には立ち入りません(私は心からそれらの感情を2番目にしていますが)。

原則として、DIが進むべき道です。次に、環境について知っておく必要のあることをすべて伝えることができるサービスを注入できます。

ただし、ユーザー情報を扱っているため、Thread.CurrentPrincipalが実行可能な代替手段になる可能性があります(ただし、スレッド静的です)。

便宜上、強く型付けされたUserクラスをラップすることができます。

于 2009-08-11T21:14:35.400 に答える
2

別のアプローチを試してみます。静的データクラスは問題を引き起こします-これは経験によるものです。Userオブジェクト(これを行うための優れた方法については@Robert Paulsonの回答を参照)を作成し、それを作成するときにすべてのオブジェクトに渡すことができます。これでうまくいくかもしれませんが、どこでも繰り返される多くのテンプレートコードを取得できます。 。

すべてのオブジェクトを必要な権限を持つデータベース/暗号化ファイルに保存し、ユーザーの権限に基づいてすべてのオブジェクトを動的に読み込むことができます。データベース上の単純な管理フォームを使用すると、保守が非常に簡単になります(ファイルは少し難しくなります)。

すべての機密オブジェクトに適用するRequiresAdminPermissionAttributeオブジェクトを作成し、実行時にUserオブジェクトと照合して、条件付きでオブジェクトにロードすることができます。

あなたが今通っているルートにはメリットがありますが、試してみるより良いオプションがいくつかあると思います。

于 2009-08-11T21:56:16.500 に答える