2012-02-15 26 views
3

目前,这是我如何处理我的MVC 3应用程序中的数据。对于MVC 3和实体框架都很新颖我不太确定这是在应用程序中处理数据的最佳方法。实际上,检查下面UserExists的调用有时会给出一个SQLConnectionTimeout问题,它似乎完全是随机的。我试图通过SQL分析器来追踪问题,并且看起来超时是在EF - > SQL进行连接之后发生的。MVC 3/EF/SQL处理连接,处理和超时

我以为我在另一个问题上解决了这个问题,但它又弹出来了,所以我希望得到每个人的意见,看下面是否是在我的应用程序中尝试数据处理的最佳方式,还是有更好的方法可以解决超时问题的方式。

下面是其他文章的链接,如果有帮助:MVC 3/EF/SQL Server strange connection timeout issue

所以总结一下我的问题(S):

  • 代码是否低于可接受的?
  • 它应该可以正常工作吗?
  • 有没有更好的方法?
  • 将不必要的连接到SQL保持从EF打开吗? (即使在使用语句退出后,SQL Profiler使它看起来仍然保持打开状态)
  • 关于我在其他文章中发布的超时问题的任何想法?

注意:存储库实现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与巨型沃尔玛你文字!

+0

你在哪里开始和dispoe分贝范围内? – MikeSW 2012-02-15 14:36:17

+0

抱歉,刚刚使用该信息编辑了帖子 - 我在AccountRepository构造函数中实例化实体上下文,并将其置于Dispose()方法中。 – S9Designs 2012-02-15 14:38:40

回答

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

在这里,我们的前刚刚创建上下文的实例和交易范围动作执行,然后我们在动作完成后进行清理。

在派生控制器,那么你可以做以下...

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 Profiler使它看起来仍保持打开状态)

这里最有可能的原因是连接池。 ADO.NET将在一段时间内保持打开的连接,从而使后续调用更有效,因为您没有打开连接的延迟。

干杯,

院长

+0

谢谢迪恩,对不起,这个问题已经这么久了! :) – S9Designs 2013-01-31 20:01:58