连接自动处理

时间:2016-02-24 11:33:10

标签: c# .net

这可能看似微不足道,但它真的让我感到困扰。 我今天开始在MVC项目中使用Dapper并创建了一个非常简单的POCO对象;当我运行这个项目时,我收到以下错误消息:

  

无法访问已处置的对象

     

对象名称:' OracleConnection'。

以下是代码:

public class MyController : Controller
{
    readonly IDbConnection sqlConn = new OracleConnection(ConfigurationManager.ConnectionStrings["LogDbContext"].ConnectionString);
    readonly string selectLog = "select * from LOG";
    readonly string insertLog = "insert into LOG (ID, Address) values (:ID, :Address)";

    // GET: Log
    public ActionResult Index()
    {
        using (sqlConn)
        {
            sqlConn.Open();

            //IEnumerable log = sqlConn.Query(selectLog);

            IEnumerable<Log> log = sqlConn.Query<Log>(selectLog);

            foreach (var item in log)
            {
                Console.WriteLine(item.ToString());
            }

        }

        return View();
    }

    public ActionResult Create()
    {
        using (sqlConn)
        {
            sqlConn.Open();

            var log = new Log()
            {
                ID = 1,
                Address = "test"
            };

            sqlConn.Execute(insertLog, log);

        }

        return View();
    }
}

似乎放置&#34; sqlConn&#34;进入using语句使其自动处理,所以当函数再次运行时,它无法处理连接。

我该怎样防止这种情况?我不希望每次需要时都手动打开和关闭连接。

更新

使用以下答案提供的所有帮助(一切正确)我最后使用类的构造函数在每次必须使用类时实例化一个新连接。

    //removed the wrong static attribute and the instantiation
    readonly IDbConnection sqlConn;
    readonly string selectLog = "select * from LOG";
    readonly string insertLog = "insert into LOG (ID, Address) values (:ID, :Address)";

    // Created a constructor to instantiate the connection everytime the controller gets called
    public LogController()
    {
         sqlConn = new OracleConnection(ConfigurationManager.ConnectionStrings["LogDbContext"].ConnectionString);
    }

4 个答案:

答案 0 :(得分:7)

  

似乎将“sqlConn”放入using语句会使其自动处理

是的,这就是using声明的用途。

  

我该怎样防止这种情况?我不希望每次需要时都手动打开和关闭连接。

我强烈建议您每次需要时执行打开和关闭连接 - 但是为它使用局部变量,而不是字段。这样每个操作都会获得一个单独的逻辑连接,因此您不必担心线程问题等。让连接池负责提高效率。我怀疑你担心在每次调用时打开一个“物理”连接(建立一个新的TCP / IP连接或其他) - 但连接池是为了确保不会发生超过它所需要的。

只需在方法中创建一个新连接:

using (var connection = new OracleConnection(...))
{
    ...
}

...和衡量表现以确定它是否令人满意。不要开始猜测你是否会遇到问题(并采用导致比他们解决的更多问题的不良解决方法)。

作为使用new的替代方法,控制器构造函数可以接受连接提供程序并要求新连接,但从根本上说它是关于每次创建新的一次性连接。

如果由于某种原因你真的,真的不想处理连接,只需删除using语句 - 但非常意识到你需要处理并发你自己。你几乎肯定不想这样做。

答案 1 :(得分:6)

您已将sqlConn声明为static readonly,这意味着整个应用程序只有一个实例。将其包含在using()中意味着在第一个请求完成后,sqlConn将被处理掉,后续请求将失败并显示ObjectDisposedException

要解决此问题,请按以下步骤重写代码:

var connectionString = 
    ConfigurationManager.ConnectionStrings["LogDbContext"].ConnectionString;
using(var sqlConn = new OracleConnection(connectionString))
{
    // ...
}

现在,至于你不想每次打开连接,这就是你必须要做的。连接很珍贵,必须小心管理:尽可能晚开,一旦你不再需要就关闭。您始终可以将连接初始化逻辑分解为单独的方法,或者采用Enterprise(r)(tm)方式并将其注入。

答案 2 :(得分:2)

这正是using的目的。一旦退出使用块,它就会处理IDisposable对象。

https://msdn.microsoft.com/en-us/library/yh598w02.aspx

这是SQL连接的好习惯 - 连接池确保打开新连接没有明显延迟,因为数据库层在池中保留了一个或多个就绪。

我会修改你的代码如下:

public class MyController : Controller
{
    static readonly string connStr = ConfigurationManager.ConnectionStrings["LogDbContext"].ConnectionString;
    readonly string selectLog = "select * from LOG";
    readonly string insertLog = "insert into LOG (ID, Address) values (:ID, :Address)";

    // GET: Log
    public ActionResult Index()
    {
        using (var sqlConn = new OracleConnection(connStr))
        {
            sqlConn.Open();

            //IEnumerable log = sqlConn.Query(selectLog);

            IEnumerable<Log> log = sqlConn.Query<Log>(selectLog);

            foreach (var item in log)
            {
                Console.WriteLine(item.ToString());
            }

        }

        return View();
    }

    public ActionResult Create()
    {
        using (var sqlConn = new OracleConnection(connStr))
        {
            sqlConn.Open();

            var log = new Log()
            {
                ID = 1,
                Address = "test"
            };

            sqlConn.Execute(insertLog, log);

        }

        return View();
    }
}

答案 3 :(得分:0)

使用本地连接 (在方法中),这是首选的实施方式

public class MyController : Controller {
  // No sqlConn field

  public ActionResult Index() {
    using (IDbConnection sqlConn = new OracleConnection(...)) {
      sqlConn.Open();
      ...
    }
  }

或者让对象的实例,而不是方法处理它,在这种情况下,你必须处理对象,从而检查你的代码:

  public class MyController : Controller, IDisposable { // note IDisposable
    // no static
    readonly IDbConnection sqlConn = new OracleConnection(...);

    public ActionResult Index() {
      // No using  
      sqlConn.Open();
      ...
    }

    protected void Dispose(Boolean disposing) {
      if (disposing) {
        sqlConn.Dispose();
      } 
    }

    public Dispose() {
      Dispose(true);

      GC.SuppressFinalize(this);
    }
  }

或者让 class 本身处理连接,最糟糕的,恕我直言,方式:

  public class MyController : Controller {
    static readonly IDbConnection sqlConn = new OracleConnection(...);

    public ActionResult Index() {
      // No using  
      sqlConn.Open();
      ...
    }

    private static void OnExit(Object sender, EventArgs e) {
      sqlConn.Dispose();
    }

    static MyController() {
      Application.ApplicationExit += OnExit;
    }
  }

无论你选择哪种方法,都不要混合这三个模型(你当前的代码是第一个和第三个实现的混合)。