【问题标题】:Which is the more efficient way to connect to a database?哪种连接数据库更有效?
【发布时间】:2012-01-09 18:52:58
【问题描述】:

与同事发生分歧,而我此时不在乎谁是对的,我更好奇哪个是更好的解决方案,以便我可以继续使用它。

我们有不同的方式来访问一个系统。

选项 #1: 使用以下代码创建数据库。

using Microsoft.Practices.EnterpriseLibrary.Data;

namespace Ivans.Healthcare.CommercialAccess.Data
{
    public abstract class DataAccess : DataHelperBase
    {
        public const int commandTimeout = 7200;
        private static Database m_db = null;
        public StringBuilder Status {get; set;}

        public DataAccess()
        {
            this.Status = new StringBuilder();

            if (m_db == null)
            {
                bool bIfRunsOnWebService = false;
                try
                {
                    if (DynamicConfigurationManager.AppSettings["WebService"] != null)
                    {
                        bIfRunsOnWebService = true;
                    }
                }
                catch {}

                if (!bIfRunsOnWebService)
                {
                    m_db = DatabaseFactory.CreateDatabase(DataAccessResource.IDS_DB_ALIAS);
                }
                else
                {
                    m_db = CreateDatabase(DataAccessResource.IDS_WS_DB_ALIAS);
                }
            }
        }

然后每次需要调用存储过程时,该方法将包含如下内容:

public IEnumerable<InquiryServiceType> GetActive(bool is5010)
{

    Database db = getDB();
    DbCommand dbCmd = db.GetStoredProcCommand(DataAccessResource.IDS_SP_SEL_InquiryServiceTypeData_ListServiceTypes);
    db.AddInParameter(dbCmd, DataAccessResource.IDS_SP_SEL_InquiryServiceTypeData_ListServiceTypes_Is5010Request, DbType.Boolean, is5010);

    DataSet ds = new DataSet();
    db.LoadDataSet(dbCmd, ds, new string[] { DataAccessResource.IDS_TBL_InquiryServiceTypeData });

    return DataSetTranslator.TranslateInquiryServiceTypeDataSet(ds);
}

选项 2

这个选项更加模块化,试图创建一个通用的数据库方法。

    private Database currentDB;
private const int commandTimeout = 7200;

public DataAccess(Common.Enums.ConnectionString currentConnection)
{
    currentDB = DatabaseFactory.CreateDatabase(currentConnection.ToDescription());
}

public IEnumerable<T> SelectMany<T>(string spName, params Param[] parameters) where T : IDataPopulate<T>, new()
{
    var storedProcedure = CreateStoredProcedureCommand(spName);
    AddParameters(storedProcedure, parameters);

    IDataReader myReader = null;
    IList<T> listOfItems = new List<T>();

    try
    {
        myReader = currentDB.ExecuteReader(storedProcedure);
        if (myReader == null)
        {
            return listOfItems;
        }

        while (myReader.Read())
        {
            listOfItems.Add(new T().FillObject(myReader));
        }

        return listOfItems;
    }
    catch (Exception ex)
    {
        string message = string.Format("Error Message: {0}\r\nStored Procedure: {1}\r\n", ex.ToString(), spName);
        throw new Exception(message);
    }
    finally
    {
        DataAccessDisposal.DataReader(myReader);
        DataAccessDisposal.StoredProcedure(storedProcedure);
    }
}

然后调用数据库将如下所示:

public IEnumerable<InquiryServiceTypes> GetAll(int payerID)
{
    Param payerIdParam = new Param("@payerID", DbType.Int32, payerID);
    return dataAccess.SelectMany<InquiryServiceTypes>("dbo.proc_PayersInquiryServiceTypesSel", payerIdParam);
}

结论

在每个部分中肯定存在编码错误的内容。我很确定有一个中间立场是最有效的代码。

上面的代码有两点效率低下。首先是它首先如何连接到数据库。二是一旦数据返回,如何处理。我很想讨论这两个问题,但我觉得第一个对这一点更重要。

谢谢, C

【问题讨论】:

  • 性能测试这两种方法并自己获得答案。
  • “确实效率低下,因为它首先创建了一个数据库”是主观的,顺便说一句
  • 请在此处的代码上制作 cmets,而不是直接编辑它。 (附注是由其他用户添加的),我同意这是主观的。为什么真的效率低下?这就是我正在努力学习的。而且他们都碰巧使用了 CreateDatabase()
  • Oded - 我以前从未这样做过。除了在每次通话之前和之后添加开始时间/停止时间之外,我还可以使用首选资源吗?

标签: c# asp.net sql-server coding-style data-access-layer


【解决方案1】:

我会简单地说:已经有一些抽象可以做到这一点(并且做得很好)。如果您可以处理创建连接,例如,使用dapper-dot-net

return connection.Query<InquiryServiceTypes>(
        "dbo.proc_PayersInquiryServiceTypesSel",
        new { payerId }, commandType: CommandType.StoredProcedure);

它将在大量缓存的 IL 中为您编写所有参数化和实现。无需编写复杂的Fill 方法或填充方法,而且速度非常快(与手动编写所有 ADO.NET 阅读器代码的性能相同,但没有无聊的代码和拼写错误的机会)。

手动编写所有这些Fill 方法不是效率高(对于开发人员而言)。

以上注意事项;匿名类型正在定义参数,即它说“有一个名为 payerId 的 int 参数,其值与传入的值相同”。您还可以:

new { id = payerId, name = "abc", allData = true }

这会将@id (int) 与payerId 的值相加,@name (nvarchar) 与'abc' 的值相加,@allData (bit) 与1 的值相加。


重新编辑 cmets 中的点:

  • 连接池是正交的,因为只要您及时释放连接,默认情况下(使用 SQL 服务器)它会自动完成
  • entlib 毫无理由地增加了 IMO 开销。与 entlib 交谈的代码实际上与与原始 ado.net 交谈的代码相同,只是有更多的膨胀和间接性。我会避免使用 entlib,除非你真的使用它来让你的生活更轻松
  • 加载数据集总是开销; DataTable 等很复杂——比加载 POCO 模型复杂得多。加载数据集只是这样您就可以使用数据集加载对象模型效率低下,并且会在需要收集的堆上创建不必要的垃圾,并且无论如何都有很多步骤(适配器等) ,所有这些都需要时间
  • DataTable 方法还要求完全读取表,而不是非缓冲假脱机(可能使用原始读取器和迭代器块);如果您有非常大的结果,这可能很重要
  • 在介绍的两种方法中,第二种方法更适合 IMO,但我喜欢该界面;那是糟糕的“关注点分离”——POCO 的工作是代表一个领域对象,而不是了解数据库
  • 如上所述,有与第二个选项类似的选项,更少实现/维护工作,并且不会引入 SoC 问题;我会很感兴趣地看这个
  • 此类选项还可以为您解决更复杂的场景;多个网格;水平连接(进入子对象);等等

【讨论】:

  • 为了这个讨论,我意识到我们可以使用数百个选项,但我对上述代码的正面和负面感到好奇。我知道有些东西,比如连接池和堆管理,我不是很了解。所以我正在努力学习,我不只是想接受其他开发人员的理由是事实。因为他可能错了。
  • 我同意这是一个更简洁的解决方案,并且会在我们下一次架构会议时提出。
  • 感谢您在编辑中的 cmets 正是我正在寻找的。要列出的数据表肯定是浪费的。所以为了确保我对关注点分离很清楚,问题是当我创建对象时,我有一个填充方法,它需要类确切地知道数据是什么样的,以便它可以转换类的数据阅读器。我明白你在说什么,我是如此坚持开放-封闭原则,以至于我错过了这一点。
【解决方案2】:

我个人认为,Dot net 代码,即应用程序代码不能以任何方式与数据库耦合。原因是:

  1. 如果上面创建的方法是为 SQL Server 设计的,那么您会受到 SQL Server 的限制。您必须能够轻松切换存储。

  2. 如果您在创建数据库时需要更多设置和限制怎么办?你必须发布另一个版本,因为你必须修改代码。

通用数据库方法在大多数情况下都会失败,并且非常难以调试。

【讨论】:

  • 大多数业务解决方案从不切换数据库后端。当您拥有数万亿条记录时,只有白痴才会这样做。
  • 啊,但是 HLGEM,这不是真的。即使您坚持使用一种数据库产品,它们也会每隔几年重新版本。
  • 如果您是一家为多个客户制定定制解决方案的公司,该怎么办。每个客户都使用不同的数据库,您需要将其整合到您的解决方案中吗?那你会用这种方法吗?想想大 HLGEM。开阔你的视野!!
【解决方案3】:

如果您只希望只读访问来迭代返回的数据,那么使用数据读取器在性能和内存使用方面都会更有效。

有关数据访问技术的性能比较,请参阅 http://msdn.microsoft.com/en-us/library/ms978388.aspx

建议您考虑使用 Entity Framework、Massive 或 PetaPoco,因为使用其中一个框架可能会节省您的时间并使代码更具可读性/可维护性。

【讨论】:

  • 我们正在研究实体框架,但没有时间完全重写系统。感谢您提供链接,现在查看。
猜你喜欢
  • 1970-01-01
  • 2021-04-03
  • 1970-01-01
  • 1970-01-01
  • 2017-08-10
  • 1970-01-01
  • 2020-03-16
  • 2013-04-25
  • 2015-02-21
相关资源
最近更新 更多