首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MVC 3/EF/SQL处理连接、处理和超时

MVC 3/EF/SQL处理连接、处理和超时
EN

Stack Overflow用户
提问于 2012-02-15 14:29:36
回答 1查看 938关注 0票数 3

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

我想我已经在这里的另一个问题中解决了这个问题,但它又弹回来了,所以我想征求大家的意见,到底下面是在我的应用程序中尝试数据处理的最佳方法,还是有更好的方法来解决超时问题。

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

因此,总结我的问题:

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

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

Controller (使用自定义成员资格提供程序的LogOn):

代码语言:javascript
复制
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
    User newUser = new User();                    

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

成员资格提供程序ValidateUser:

代码语言:javascript
复制
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用户:

代码语言:javascript
复制
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);
        }           
    }

用户存在:

代码语言:javascript
复制
 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);
        } 
    }

存储库代码段(构造器、处置等):

代码语言:javascript
复制
    public class AccountRepository : IDisposable
    {
         private DbContext entities;

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

         ...

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

谢谢大家-我意识到这个问题是你超过9000与一堵巨大的文字墙!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-07-06 12:01:43

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

如果不使用依赖项注入/ IoC,则可以使用一个基本控制器,如下所示:

代码语言:javascript
复制
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;
        }
    }
}

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

在派生控制器中,您可以执行以下操作.

代码语言:javascript
复制
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将在一段时间内维护打开的连接,这将使后续的调用更高效,因为您没有打开连接的延迟。

干杯,

院长

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9295193

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档