【问题标题】:Possible performance issues with C# code in a legacy project旧项目中的 C# 代码可能存在性能问题
【发布时间】:2016-04-06 05:33:31
【问题描述】:

我最近开始在一个拥有数千行遗留代码的新项目中工作。我们面临着几个性能问题。我决定看一下代码并看到以下内容。有一堂课:

public class BaseDataAccess
{
    private Database dB;

    public Database DB
    {
        get
        {
            if (dB == null)
            {
                dB = DatabaseFactory.CreateDatabase();
            }
            return dB;
        }
    }
}

还有许多继承自前一个基类的后代类。在内部,那些其他类使用 DB 属性,如下所示:

DataSet ds = DB.ExecuteDataSet(spGetCustomersSortedByAge);

最后,有一个庞大的类(5000 行代码),其中包含数十种方法,如下所示:

public void ProcessPayments()
    {
        try
        {
            List<Employee> employees = new EmployeesDA().SelectAll(null);
            foreach (Employee employee in employees)
            {

                employee.Account = new MovementsDA().SelectAll(employee.Id, DateTime.Now);

                ...

                City city = new CitiesDA().Select(zone.cityId);

                ...

                Management m = new ManagmentDA().Select(city.id);

            }
        }
        catch (Exception ex)
        {
                ...
        }

    }

注意在前面的方法中,EmployeesDA、MovementsDA、CitiesDA 和 ManagmentDA 都是 BaseDataAccess 的继承者,并且在内部使用它们各自的 DB 属性。另请注意,它们在 foreach 循环中不断被实例化(很多时候在 2 级嵌套中)。

我认为实例化本身是可疑的,但我更关心这里的数据库连接发生了什么?实例化的每个 DA 是否都打开了一个新的底层连接?这段代码有多糟糕?

关于我正在考虑的解决方案的附带说明,以防此代码应该被修复:我正在考虑将每个构造函数设为私有,因此编译器开始抱怨实例化并通过调用 GetInstance 方法重构实例化(单例模式)避免重新创建对象和底层连接。但是,我不确定这是否在某种程度上也可能是危险的,例如,如果连接可能会关闭。当前的代码没有这个问题,因为实例化一直在发生。

【问题讨论】:

  • 这种方法有很多问题,但如果DatabaseFactory.CreateDatabase(); 方法为每次调用都返回一个新的、已连接的数据库连接,那么您就遇到了很大的连接问题。跨度>
  • 您遇到的另一个问题是事务支持。 ProcessPayments 不应该确保所有事情都发生在一个事务中吗?或者每个员工至少有一笔交易,或者类似的事情?
  • 处理此问题的正确方法是开始让您的程序中的连接状态可见,并且必须对其进行管理。
  • @LasseV.Karlsen,DatabaseFactory 是一个 .NET 类,但我不确定每次执行 CreateDatabase 时发生了什么。所以我希望有人能在这里有所启发。
  • 如果这里的数据库对象只在查询时打开连接,之后又关闭,那么打开很多连接应该没有问题,但还是要考虑事务的问题。

标签: c# .net performance .net-3.5 processing-efficiency


【解决方案1】:

一种常见的误解是,对象构造成本很高。它比基本算术或其他机器级别的东西要昂贵得多,但不太可能是性能问题的直接根源。

例如,对循环使用装箱整数是一种浪费,但是在每个循环中构造一个 Employee 对象与重用一个可变的 Employee 对象并不会带来有意义的性能优势。

许多垃圾收集器能够在这样的循环中重用对象内存帧。实际上,每次循环都会分配和覆盖单个对象帧。

在这种特定情况下,如果 DA 的初始化成本很高,则可能会产生成本。如果是这种情况,我将重构代码以在循环外创建那些代码。我不会使用实际的静态单例。如果需要,我会使用依赖注入来管理单例对象。静态单例实际上是全局变量,是状态耦合和模块化分解的邀请。

【讨论】:

  • 谢谢,是的,我知道 Singleton 不好。但是对于这种情况,我希望进行一些快速重构以测试性能等。但是,我仍然想知道这种情况有多糟糕,每个 DA 都使用 .NET 框架中的 DatabaseFactory 类以获得始终保持连接(周围有数百个实例化)。
  • @StephenH.Anderson 分析它并查看构造函数消耗了多少时间。您不需要更改系统来衡量它。您应该能够获得最小/最大/平均调用时间和在特定调用堆栈中花费的总时间。我怀疑连接池(如果它还没有完成,这是我要做的第一件事)将大大增加召回这些构造函数的摊销成本。
猜你喜欢
  • 1970-01-01
  • 2011-09-07
  • 2013-10-16
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-30
相关资源
最近更新 更多