none
Web程序的数据库连接应该保存在哪儿? RRS feed

  • 问题

  • 最近在搞asp.net的一个系统,一个头痛的问题就是一个页面中数据库连接存放在哪儿,想来想去总是想不好,多层构架的话肯定不能像以前asp一样开头一个conn.open……

    想了几种办法,总是觉得不完善:
    1。放在Application 域里,这个跟asp开头一个conn.open一个意思,显然破坏了构架
    2。放在page_load 或 page_init里,同上
    3。放在业务层里,看似比较合理,但也有许多业务,互不相干的,共享一个连接不好处理
    4。加一个业务总管,负责保存数据库连接等数据,其他业务要用的时候就丢它一个引用,看似不错,但处理起来很麻烦,结构也不紧凑
    5。数据层加一个总管,其他同上,缺点也同上……

    看了别人写的一些东西以及msdn里的相关说明,发现许多时候都是维护一个连接字符串(这个处理起来容易多了,大不了来一个static),每个单独的应用都各自打开一个连接,处理完了再关掉。由于有连接池的存在,性能上不会太糟糕,但这也意味着一个页面就可能要打开并关掉连接数十次,似乎难以接受……

    大家有什么好的方法没?或者说打开并关掉10次在性能上没有任何影响?(暂不考虑页面缓存技术)
    霸王
    2010年3月7日 17:01

答案

  • 你好!

    首先在 asp.net 中建议将数据库连接字符串放到 web.config 中,以便配置。

    如果你采用的是三层结构,连接的打开与关闭应该写到数据访问层中而不是每个页面都写。连接池的存在就是为了让你曾经创建的连接能够重复使用而不必每次都与数据库创建连接。严格来说连接的 Open 与 Close 是有性能开销的,如果你在一个循环中多次调用 Open 与 Close 这种开销就能明显体现出来(比如:50 次),一般我们在写数据访问层的时候都会提供方法传入 Connection 或 Transcation 以满足类似上面的需求。

    知识改变命运,奋斗成就人生!
    2010年3月8日 1:36
    版主
  • 另外建议你了解一下微软提供的 enterprise library 或 sqlhelper
    知识改变命运,奋斗成就人生!
    2010年3月8日 1:37
    版主
  • 应该放入web.config中..如果连接字符串多也可以以不同的命名进行调用....

    楼主多虑了

    2010年3月8日 2:00
  • 你好!
         连接字符串的问题上面的同行已经说的比较详细了,建议最好放到配置文件中,我来说说Open和Close方法:
    SqlConnection类的Close()方法在MSDN中的说明是这样的:
    关闭与数据库的连接。这是关闭任何打开连接的首选方法。 如果 SqlConnection 超出范围,则不会将其关闭。因此,必须通过调用 Close 或 Dispose 显式关闭该连接。Close 和 Dispose 在功能上等效。如果连接池值 Pooling 设置为 true 或 yes,则基础连接将返回到连接池。另一方面,如果 Pooling 设置为 false 或 no,则会关闭到服务器的基础连接。
    看说明好象是Close()方法和Dispose()方法是类似的,实际上只是在关闭连接这个功能上等效,让我们看看Close()方法的源代码:
            override public void Close() {
                IntPtr hscp;
                Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Close|API> %d#" , ObjectID);
                try {
                    SqlStatistics statistics = null;

                    RuntimeHelpers.PrepareConstrainedRegions();
                    try {
    #if DEBUG
                        object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot);

                        RuntimeHelpers.PrepareConstrainedRegions();
                        try {
                            Thread.SetData(TdsParser.ReliabilitySlot, true);
    #endif //DEBUG
                            statistics = SqlStatistics.StartTimer(Statistics);

                            // The lock here is to protect against the command.cancel / connection.close race condition
                            // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and
                            // the command will no longer be cancelable.  It might be desirable to be able to cancel the close opperation, but this is
                            // outside of the scope of Whidbey RTM.  See (SqlCommand::Cancel) for other lock.
                            lock (InnerConnection) {
                                InnerConnection.CloseConnection(this, ConnectionFactory);
                            }
                            // does not require GC.KeepAlive(this) because of OnStateChange

                            if (null != Statistics) {
                                ADP.TimerCurrent(out _statistics._closeTimestamp);
                            }
     #if DEBUG
                        }
                        finally {
                            Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue);
                        }
    #endif //DEBUG
                    }
                    catch (System.OutOfMemoryException e) {
                        Abort(e);
                        throw;
                    }
                    catch (System.StackOverflowException e) {
                        Abort(e);
                        throw;
                    }
                    catch (System.Threading.ThreadAbortException e) {
                        Abort(e);
                        throw;
                    }
                    finally {
                        SqlStatistics.StopTimer(statistics);
                    }
                }
                finally {
                    SqlDebugContext  sdc = _sdc;
                    _sdc = null;
                    Bid.ScopeLeave(ref hscp);
                    if (sdc != null) {
                       sdc.Dispose();
                    }
                }
            }
    Close()方法只是关闭了连接,然后这个连接被存储到连接池,所以在调用Close()方法以后,还是可以再通过Open()方法来打开连接的
    针对SqlConnection这个类来说,如果以后还需要使用这个连接可以使用Close()方法临时关闭连接,如果以后不需要使用这个连接了,可以优先选用Dispose()方法来释放资源,当然你可以使用using关键字来简化这个过程,OleDbConnection类和OdbcConnection类的源代码我没有找到,但是应该和SqlConnection类是类似的!
    周雪峰
    2010年3月8日 3:28
    版主

全部回复

  • 你好!

    首先在 asp.net 中建议将数据库连接字符串放到 web.config 中,以便配置。

    如果你采用的是三层结构,连接的打开与关闭应该写到数据访问层中而不是每个页面都写。连接池的存在就是为了让你曾经创建的连接能够重复使用而不必每次都与数据库创建连接。严格来说连接的 Open 与 Close 是有性能开销的,如果你在一个循环中多次调用 Open 与 Close 这种开销就能明显体现出来(比如:50 次),一般我们在写数据访问层的时候都会提供方法传入 Connection 或 Transcation 以满足类似上面的需求。

    知识改变命运,奋斗成就人生!
    2010年3月8日 1:36
    版主
  • 另外建议你了解一下微软提供的 enterprise library 或 sqlhelper
    知识改变命运,奋斗成就人生!
    2010年3月8日 1:37
    版主
  • 应该放入web.config中..如果连接字符串多也可以以不同的命名进行调用....

    楼主多虑了

    2010年3月8日 2:00
  • 你好!
         连接字符串的问题上面的同行已经说的比较详细了,建议最好放到配置文件中,我来说说Open和Close方法:
    SqlConnection类的Close()方法在MSDN中的说明是这样的:
    关闭与数据库的连接。这是关闭任何打开连接的首选方法。 如果 SqlConnection 超出范围,则不会将其关闭。因此,必须通过调用 Close 或 Dispose 显式关闭该连接。Close 和 Dispose 在功能上等效。如果连接池值 Pooling 设置为 true 或 yes,则基础连接将返回到连接池。另一方面,如果 Pooling 设置为 false 或 no,则会关闭到服务器的基础连接。
    看说明好象是Close()方法和Dispose()方法是类似的,实际上只是在关闭连接这个功能上等效,让我们看看Close()方法的源代码:
            override public void Close() {
                IntPtr hscp;
                Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Close|API> %d#" , ObjectID);
                try {
                    SqlStatistics statistics = null;

                    RuntimeHelpers.PrepareConstrainedRegions();
                    try {
    #if DEBUG
                        object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot);

                        RuntimeHelpers.PrepareConstrainedRegions();
                        try {
                            Thread.SetData(TdsParser.ReliabilitySlot, true);
    #endif //DEBUG
                            statistics = SqlStatistics.StartTimer(Statistics);

                            // The lock here is to protect against the command.cancel / connection.close race condition
                            // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and
                            // the command will no longer be cancelable.  It might be desirable to be able to cancel the close opperation, but this is
                            // outside of the scope of Whidbey RTM.  See (SqlCommand::Cancel) for other lock.
                            lock (InnerConnection) {
                                InnerConnection.CloseConnection(this, ConnectionFactory);
                            }
                            // does not require GC.KeepAlive(this) because of OnStateChange

                            if (null != Statistics) {
                                ADP.TimerCurrent(out _statistics._closeTimestamp);
                            }
     #if DEBUG
                        }
                        finally {
                            Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue);
                        }
    #endif //DEBUG
                    }
                    catch (System.OutOfMemoryException e) {
                        Abort(e);
                        throw;
                    }
                    catch (System.StackOverflowException e) {
                        Abort(e);
                        throw;
                    }
                    catch (System.Threading.ThreadAbortException e) {
                        Abort(e);
                        throw;
                    }
                    finally {
                        SqlStatistics.StopTimer(statistics);
                    }
                }
                finally {
                    SqlDebugContext  sdc = _sdc;
                    _sdc = null;
                    Bid.ScopeLeave(ref hscp);
                    if (sdc != null) {
                       sdc.Dispose();
                    }
                }
            }
    Close()方法只是关闭了连接,然后这个连接被存储到连接池,所以在调用Close()方法以后,还是可以再通过Open()方法来打开连接的
    针对SqlConnection这个类来说,如果以后还需要使用这个连接可以使用Close()方法临时关闭连接,如果以后不需要使用这个连接了,可以优先选用Dispose()方法来释放资源,当然你可以使用using关键字来简化这个过程,OleDbConnection类和OdbcConnection类的源代码我没有找到,但是应该和SqlConnection类是类似的!
    周雪峰
    2010年3月8日 3:28
    版主
  • 数据连接肯定放在web.config中,以便随时读取,连接数据库和操作数据库的基本代码放在数据访问层中,可以看一下sqlhelper
    咯咯
    2010年3月8日 4:59
  • 补充一点,我用的是“大三层”的方式,每个层都是一个单独的dll(最后一层是web或者app),根据配置的不同调用不同版本的dll,所以放在web.config 中的话,前面两层访问不到的。

    霸王
    2010年3月8日 9:41
  •  不过大问题应该解决了,就是数据库的打开在数据层中进行,前面两层不管。open和close也对性能没什么影响。

    而Close 和 Dispose 的区别更是了我多年心愿 = = 不过我从来都是只close,没有dispose过(认为这个应该是系统来干的事情 ),似乎也没发现过问题。
    霸王
    2010年3月8日 10:10
  • 恩,前边的同志介绍的已经比较好,比较详细了.其实,建议楼主可以看看PetShop,我感觉还是不错的.
    2010年3月8日 13:02