目前,这就是我在MVC 3应用程序中处理数据的方式。作为MVC 3和实体框架的新手,我不太确定这是处理应用程序中数据的最佳方法。实际上,下面检查UserExists的调用有时会产生一个似乎完全随机的SQLConnectionTimeout问题。我已经尝试通过SQL来跟踪这个问题,似乎超时就发生在从EF -> SQL建立连接之后。
我想我已经在这里的另一个问题中解决了这个问题,但它又弹回来了,所以我想征求大家的意见,到底下面是在我的应用程序中尝试数据处理的最佳方法,还是有更好的方法来解决超时问题。
下面是另一篇文章的链接,如果有帮助的话:MVC 3/EF/SQL Server strange connection timeout issue
因此,总结我的问题:
注意:存储库实现了IDisposable,并在下面列出了dispose方法。它在存储库构造函数中创建实体上下文的新实例。
Controller (使用自定义成员资格提供程序的LogOn):
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:的帐户存储库方法
Get用户:
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与一堵巨大的文字墙!
发布于 2012-07-06 12:01:43
我们通常遵循这样的模式:使用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)时不要使用隐式事务,因此在执行操作时,我们总是创建事务范围。这样做的开销很小,因为除非打开连接,否则事务作用域不会执行太多任务。
编辑:为了回答你的另一个问题.
从EF到SQL的不必要连接会保持打开吗?(SQL使它看起来像是打开了一段时间,即使在using语句退出之后)
这里最可能的原因是连接池。ADO.NET将在一段时间内维护打开的连接,这将使后续的调用更高效,因为您没有打开连接的延迟。
干杯,
院长
https://stackoverflow.com/questions/9295193
复制相似问题