gpt4 book ai didi

sql - MVC 3/EF/SQL 处理连接、处置和超时

转载 作者:行者123 更新时间:2023-12-01 02:31:32 26 4
gpt4 key购买 nike

目前,这就是我在 MVC 3 应用程序中处理数据的方式。作为 MVC 3 和 Entity Framework 的新手,我不太确定这是在应用程序中处理数据的最佳方法。事实上,下面检查 UserExists 的调用有时会给出一个似乎完全随机的 SQLConnectionTimeout 问题。我尝试通过 SQL 事件探查器跟踪问题,似乎超时发生在从 EF -> SQL 建立连接之后。

我以为我已经在 SO 上的另一个问题中解决了这个问题,但它又弹出了,所以我想听听大家的意见,看看下面是否是尝试在我的应用程序中处理数据的最佳方法,或者是否有更好的方法这可能会解决超时问题。

如果有帮助,这里是另一篇文章的链接:MVC 3/EF/SQL Server strange connection timeout issue

总结一下我的问题:

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

注意:存储库实现了 IDisposable 并具有下面列出的 dispose 方法。它在存储库构造函数中创建实体上下文的新实例。

Controller (使用自定义成员提供程序登录):

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

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

成员(member)提供者验证用户:

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!

最佳答案

我们通常遵循使用 IActionFilter 控制上下文的实例化和处理的模式,并提供一种机制将其注入(inject)依赖类(使用 Ninject)。

如果您不使用依赖注入(inject)/IoC,您可以像下面这样使用基本 Controller :

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

在这里,我们会在操作执行之前创建上下文实例和事务范围,然后在操作完成后进行清理。

然后在您的派生 Controller 中,您可以执行以下操作...

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

return View();
}
}

将上下文传递到您的存储库中有点困惑,可以使用 Ninject 之类的工具注入(inject)依赖项而不是您提供它来整理。 http://stevescodingblog.co.uk/dependency-injection-beginners-guide/如果您有兴趣,它提供了一个非常合理的起点。

您还可以使用 UnitOfWorkAttribute 标记操作,以控制上下文使用的事务的创建。建议在进行数据库工作时不要使用隐式事务(http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions),因此我们总是在执行操作时创建一个事务范围。这几乎没有开销,因为除非打开连接,否则事务范围不会做太多事情。

编辑:只是为了回答你的另一个问题......

与 SQL 的不必要连接是否会从 EF 保持打开状态? (SQL Profiler 让它看起来像是在 using 语句退出后仍保持打开状态)

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

干杯,

院长

关于sql - MVC 3/EF/SQL 处理连接、处置和超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9295193/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com