1

MVC 3 と EF にはかなり慣れていないので、会社のアプリケーションを開発するための最適なアーキテクチャ アプローチを理解しようとしています。申請は、同時に数百人のユーザーを処理する可能性がある大規模な申請になるため、適切な手順を理解し、従うことを確認したいと思います。これまでのところ、単純なリポジトリ パターン (コントローラー -> リポジトリ -> EF など) のアプローチが最適で最も簡単に実装できると判断しましたが、それが最善の方法であるかどうかはわかりません。アプリケーションは基本的に、devexpress グリッドでユーザーに表示されるデータを返し、ユーザーはこのデータを変更したり、追加したりできます。

私はこの記事を見つけましたが、現時点ではかなり混乱しているので、切断された EF を使用しようとする理由があるかどうか、また、なぜそうしたいのか疑問に思っています: http://www.codeproject .com/Articles/81543/Finally-Entity-Framework-working-in-fully-disconne?msg=3717432#xx3717432xx

私の質問を要約すると:

  • 以下のコードは受け入れられますか?
  • 大規模な MVC アプリケーションで問題なく動作するはずですか?
  • より良い方法はありますか?
  • SQL への不要な接続は EF から開いたままになりますか? (SQL プロファイラーは、using ステートメントが終了した後も、しばらく開いたままのように見せます)
  • 切断されたフレームワークのアイデアはより良いものですか?なぜそれをしたいのですか? 層を超えてデータを追跡する必要があるとは思いません...

注:リポジトリは IDisposable を実装し、以下に示す dispose メソッドを備えています。リポジトリ コンストラクターでエンティティ コンテキストの新しいインスタンスを作成します。

使用例:

コントローラー(カスタム メンバーシップ プロバイダーを使用してログオン):

if (MembershipService.ValidateUser(model.UserName, model.Password))
{
    User newUser = new User();                    

    using (AccountRepository repo = new AccountRepository())
    {
         newUser = repo.GetUser(model.UserName);
         ...
    }
}

メンバーシップ プロバイダー ValidateUser:

public override bool ValidateUser(string username, string password)
{
    using (AccountRepository repo = new AccountRepository())
    {
        try
        {
            if (string.IsNullOrEmpty(password.Trim()) || string.IsNullOrEmpty(username.Trim()))
                return false;

            string hash = FormsAuthentication.HashPasswordForStoringInConfigFile(password.Trim(), "md5");

            bool exists = false;

            exists = repo.UserExists(username, hash);

            return exists;
        }catch{
            return false;
        }
    }
}

GetUser および UserExists のアカウント リポジトリ メソッド:

ユーザーを取得:

public User GetUser(string userName)
    {
        try
        {
            return entities.Users.SingleOrDefault(user => user.UserName == userName);
        }
        catch (Exception Ex)
        {
            throw new Exception("An error occurred: " + Ex.Message);
        }           
    }

ユーザーが存在します:

 public bool UserExists(string userName, string userPassword)
 {
        if (userName == "" || userPassword == "")
            throw new ArgumentException(InvalidUsernamePassword);

        try
        {
            bool exists = (entities.Users.SingleOrDefault(u => u.UserName == userName && u.Password == userPassword) != null);
            return exists;
        }
        catch (Exception Ex)
        {
            throw new Exception("An error occurred: " + Ex.Message);
        } 
    }

リポジトリ スニペット (コンストラクター、破棄など):

    public class AccountRepository : IDisposable
    {
         private DbContext entities;

         public AccountRepository()
         {
            entities = new DbContext();
         }

         ...

         public void Dispose()
         {
            entities.Dispose();
         }
    }
4

2 に答える 2

5

何が受け入れられるかはかなり主観的なものですが、適切なデータ アクセスを行いたい場合は、リポジトリ パターンを使用しないことをお勧めします。

最大の理由は、データベースへのアクセスを最小限に抑えることです。たとえば、リポジトリを見て、GetUser()メソッドに注目してください。ここで、コードから一歩離れて、アプリケーションがどのように使用されるかを考えてください。ここで、追加データなしで user テーブルからデータを要求する頻度について考えてみましょう。基本的なデータ入力アプリケーションを作成している場合を除き、ほとんどの場合、答えは「めったにありません」です。

あなたのアプリケーションは多くのグリッドを表示すると言います。そのグリッドにはどのようなデータがありますか? 私は、(アプリケーション ドメインを知らなくても) グリッドがユーザー データをそのユーザーに関連する他の情報と組み合わせると想定しています。その場合、リポジトリをどのように処理しますか?

1 つの方法は、次のように各リポジトリのメソッドを個別に呼び出すことです。

var user = userRepository.GetUser("KallDrexx");
var companies = companyRepository.GetCompaniesForUser(user.Id);

これは、実際には 1 つだけであるべきデータベース呼び出しが 2 つあることを意味します。画面がますます複雑になるにつれて、データベースへのヒット数がますます増加し、アプリケーションが大量のトラフィックを受け取ると、パフォーマンスの問題が発生します。リポジトリ パターンでこれを行う唯一の現実的な方法は、次のような特定のクエリを実行するための特別なメソッドをリポジトリに追加することです。

public class UserRepository
{
    public User GetUser(string userName)
    {
        // GetUser code          
    }

    public User GetUserWithCompanies(string userName)
    {
        // query code here
    }
}

では、ユーザーが必要で、その連絡先データを 1 つのクエリで指定するとどうなるでしょうか。ここで、別のメソッドをユーザー リポジトリに追加する必要があります。ここで、各企業のクライアント数も返す別のクエリを実行する必要があるとします。そのため、さらに別のメソッドを追加する (またはオプションのパラメーターを追加する) 必要があります。ここで、すべての会社とそこに含まれるユーザーを返すクエリを追加するとします。ここで、新しいクエリ メソッドが必要になりますが、それをリポジトリに配置するUserか、Companyリポジトリに配置するかという問題が生じます。どちらに入っているかを追跡し、後で必要になったときGetUserWithCompanyに簡単に選択できるようにするにはどうすればよいでしょうか?GetCompanyWithUsers

その時点からすべてが非常に複雑になり、リポジトリ パターンを削除するようになったのはそのような状況です。データ アクセスのために今行っていることは、個々のクエリおよびコマンド クラスを作成することです。各クラスは、データベースに対する 1 つの (そして 1 つだけの) クエリまたはデータ更新コマンドを表します。各クエリ クラスは、1 つの特定のユーザー使用シナリオに必要なデータのみを含むビュー モデルを返します。他のデータ アクセス パターンも機能します (仕様パターン、一部の優れた開発者は、EFデータ アクセス レイヤーであるため、コントローラーでデータ アクセスを実行するだけでよいとさえ言っています)。

データ アクセスを成功させるための鍵は、適切な計画です。画面がどのように見えるか知っていますか?ユーザーがシステムをどのように使用するか知っていますか? 各画面に実際に表示されるすべてのデータを知っていますか? これらのいずれかに対する答えが「いいえ」の場合は、一歩下がってデータ層のことを忘れる必要があります。データ層は、アプリケーションが実際にどのようになるかに基づいて決定される (または優れたアプリケーションの場合はそうあるべきである) からです。 UI と画面は、データ層の設計方法に依存してはなりません。データ アクセスを開発するときに、UI のニーズとユーザーの使用シナリオを考慮に入れないと、アプリケーションは適切にスケーリングされず、パフォーマンスが低下します。サイトを大きくする予定がなければ、問題にならないこともあります。

于 2012-02-16T13:52:54.747 に答える
1

何をするにしても、次のようにインスタンス化を移動し、コンテキストをコントローラーに破棄することを検討できます。

public class MyController : Controller
{
    private Entities context = new Entities();

    ...

    public override void Dispose()
    {
        context.Dispose();
    }
}

その後、作成のオーバーヘッドを複製することなく、そのコンテキストを必要とする任意のメソッドに渡すことができます。

同じ理由で、リポジトリ パターンが必ずしも悪いという意見には同意しません。複数のクラスを作成してコードを分割し、管理しやすくし、同じコンテキストを再利用します。それは次のようになります。

repository.Users.GetUser(userName);

この場合、「Users」は、リポジトリからコンテキストを再利用するユーザー リポジトリ クラスの遅延ロードされたインスタンスです。したがって、リポジトリ内のその Users プロパティのコードは次のようになります。

private UserRepository users;

public UserRepository Users
{
    get
    {
        If (users == null)
        {
            users = new UserRepository(this);
        }
        return users;
    }
}

その後、プロパティを介してこれらの他の遅延ロードされたクラスにコンテキストを公開できます。

これが必ずしも KallDrexx のパターンと矛盾するとは思いません。彼の方法は単にこれをひっくり返すだけです。

repository.Users.GetUser(userName);

あなたは次のようなものを持っているでしょう

UserQuery query = new UserQuery(repository.Users);

これは、構文の問題になります。これが欲しいですか:

repository.Area.Query(value1, value2, ...);

またはこれ:

AreaQuery query = new AreaQuery { Property1 = value1, ... };

後者は実際にはモデル バインディングでうまく機能しますが、実際にコーディングする必要がある場合は明らかに冗長になります。

KallDrexx が与えた最善のアドバイスは、コードをアクションに入れ、それを理解することです。単純な CRUD を実行している場合は、MVC にモデルをインスタンス化してデータを入力させます。後は、アタッチして保存するだけです。コードを再利用できる場合は、再利用できる場所に移動します。アプリケーションが複雑になり始めた場合は、適切なものが見つかるまで、これらの推奨事項のいくつかを試してください。

于 2012-02-18T05:08:42.380 に答える