2012-02-15 6 views
3

現在、MVC 3アプリケーション内でデータを処理しています。 MVC 3とEntity Frameworkの両方でかなり新しくなっていますが、これはアプリケーション内のデータを処理するための最善の方法であるとは確信していません。実際、以下のUserExistsを呼び出す呼び出しは、完全にランダムであるように見えるSQLConnectionTimeoutの問題を発生させることがあります。私はSQLプロファイラを介して問題をトレースしようとしたが、接続がEF - > SQLから行われた直後にタイムアウトが発生するように見えます。MVC 3/EF/SQLの処理接続、廃棄、タイムアウト

私はこれについて別の質問で解決したと思っていましたが、バックアップがポップアップしたので、アプリケーションでデータ処理を試みる最善の方法であるかどうか、タイムアウトの問題を解決する可能性があります。 MVC 3/EF/SQL Server strange connection timeout issue

をだから私の質問(複数可)を要約する:それは場合に役立ちます。ここ

は、他の記事へのリンクである

  • を許容下記のコードですか?
  • 正常に動作するはずですか?
  • 良い方法がありますか?
  • EFからSQLへの不要な接続は開いたままになりますか? (SQLプロファイラは、使用しているステートメントが終了してからしばらく開いているように見えます)
  • 私は他の記事に投稿したタイムアウトのアイデアはありますか?

注:リポジトリは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(); 
     } 
    } 

みんなありがとう - 私はこのことを実現あなたは巨大なウォールで9000人を超えるテキストのl!

+0

あなたが開始し、デシベルコンテキストをdispoeのですか? – MikeSW

+0

申し訳ありませんが、その情報を持つ投稿を編集しました。私はAccountRepositoryコンストラクタでエンティティコンテキストをインスタンス化し、Dispose()メソッドでそのコンテキストを破棄します。 – S9Designs

答えて

0

一般に、IActionFilterを使用してコンテキストのインスタンス化と廃棄を制御し、それを依存クラスに挿入するメカニズムを提供します(Ninjectを使用)。

あなたは少し、次のようなベースコントローラで逃げることができ依存性の注入/ IoCのを使用していない場合:

using System; 
using System.Diagnostics; 
using System.Linq; 
using System.Transactions; 
using System.Web.Mvc; 

public class ControllerBase : Controller 
{ 
    private ContextState contextState; 

    protected EntityContext Context 
    { 
     get { return this.contextState.Context; } 
    } 

    protected TransactionScope TransactionScope 
    { 
     get { return this.contextState.TransactionScope; } 
    } 

    protected override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     base.OnActionExecuting(filterContext); 

     IsolationLevel isolationLevel = filterContext.ActionDescriptor 
      .GetCustomAttributes(typeof(UnitOfWorkAttribute), false) 
      .Cast<UnitOfWorkAttribute>() 
      .Select(a => a.IsolationLevel) 
      .DefaultIfEmpty(IsolationLevel.ReadCommitted) 
      .First(); 

     Trace.TraceInformation("Creating database context & transaction scope with isolation {0}.", isolationLevel); 

     this.contextState = new ContextState 
      { 
       TransactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = isolationLevel }), 
       Context = new EntityContext() 
      }; 
    } 

    protected override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     base.OnActionExecuted(filterContext); 

     try 
     { 
      if (filterContext.Exception == null) 
      { 
       Trace.TraceInformation("Commiting transaction scope."); 
       this.contextState.TransactionScope.Complete(); 
      } 
      else 
      { 
       Trace.TraceInformation("Rolling back transaction scope."); 
      } 
     } 
     finally 
     { 
      try 
      { 
       Trace.TraceInformation("Disposing database context."); 
       this.contextState.Context.Dispose(); 
      } 
      catch (Exception e) 
      { 
       Trace.TraceError("Failed to dispose database context. {0}", e); 
      } 

      try 
      { 
       Trace.TraceInformation("Disposing transaction scope."); 
       this.contextState.TransactionScope.Dispose(); 
      } 
      catch (Exception e) 
      { 
       Trace.TraceError("Failed to dispose transaction scope. {0}", e); 
      } 

      this.contextState = null; 
     } 
    } 

    private class ContextState 
    { 
     public EntityContext Context { get; set; } 
     public TransactionScope TransactionScope { get; set; } 
    } 
} 

/// <summary> 
/// Marks an MVC action as requiring a particular <see cref="IsolationLevel" /> when a transaction is 
/// created for it. 
/// </summary> 
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 
public class UnitOfWorkAttribute : Attribute 
{ 
    private readonly IsolationLevel isolationLevel; 

    public UnitOfWorkAttribute(IsolationLevel isolationLevel) 
    { 
     this.isolationLevel = isolationLevel; 
    } 

    /// <summary> 
    /// Gets an <see cref="IsolationLevel" /> value indicating the isolation level 
    /// a transaction should use. 
    /// </summary> 
    public IsolationLevel IsolationLevel 
    { 
     get 
     { 
      return this.isolationLevel; 
     } 
    } 
} 

ここで私達はちょうどAN前に、あなたのコンテキストおよびトランザクションスコープのインスタンスを作成しますアクションが実行されたら、クリーンアップを実行します。あなたはその後、次の操作を行うことができます、あなたの派生コントローラで

...

public class HomeController : ControllerBase 
{ 
    public ActionResult Index() 
    { 
     using (AccountRepository accountRepository = new AccountRepository(this.Context)) 
     { 
      // do stuff 
     } 

     return View(); 
    } 
} 

リポジトリにコンテキストを渡すと少し厄介で、むしろあなたよりも依存関係を注入するNinjectのようなものを使用して片付けすることができますそれを提供する。興味があればhttp://stevescodingblog.co.uk/dependency-injection-beginners-guide/はかなり合理的な出発点です。

また、コンテキストによって使用されるトランザクションの作成を制御するために、UnitOfWorkAttributeでアクションをマークアップすることもできます。データベース作業(http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions)を行う際に暗黙のトランザクションを使用しないようにして、アクションを実行するときに常にトランザクションスコープを作成することをお勧めします。コネクションが開かれていなければ、トランザクションスコープはあまり効果がないため、オーバーヘッドはほとんどありません。

編集:ちょうどあなたの質問の他に答えるために...

はSQLへの不必要な接続はEFから開いたままでしょうか? (SQLプロファイラは、使用しているステートメントが終了してもしばらく開いているように見えます)

ここには接続プールがあります。 ADO.NETは一定期間オープン接続を維持します。これにより、接続を開く待ち時間がないため、後続のコールの効率が向上します。

乾杯、

ディーン

+0

ディーン、ありがとうございました、ごめんなさい、この質問のためにとても長いです! :) – S9Designs

関連する問題