一个C#重构问题

时间:2010-06-11 12:17:32

标签: c# .net refactoring

我今天遇到了以下代码,我不喜欢它。这是相当明显的,但我还是会在这里添加一些解释:

基本上,它从数据库中读取应用程序的所有设置,并遍历所有这些设备,查找数据库版本和APP版本,然后将一些变量设置为数据库中的值(稍后将使用)。

我看着它并认为它有点难看 - 我不喜欢switch语句而且我讨厌在完成后继续遍历列表的事情。所以我决定重构它。

我向大家提出的问题是你如何重构它?或者你认为它甚至需要重构?

以下是代码:

        using (var sqlConnection = new SqlConnection(Lfepa.Itrs.Framework.Configuration.ConnectionString))
        {
            sqlConnection.Open();

            var dataTable = new DataTable("Settings");

            var selectCommand = new SqlCommand(Lfepa.Itrs.Data.Database.Commands.dbo.SettingsSelAll, sqlConnection);
            var reader = selectCommand.ExecuteReader();
            while (reader.Read())
            {
                switch (reader[SettingKeyColumnName].ToString().ToUpper())
                {
                    case DatabaseVersionKey:
                        DatabaseVersion = new Version(reader[SettingValueColumneName].ToString());
                        break;
                    case ApplicationVersionKey: 
                        ApplicationVersion = new Version(reader[SettingValueColumneName].ToString());
                        break;
                    default:
                        break;
                }
            }

            if (DatabaseVersion == null)
                throw new ApplicationException("Colud not load Database Version Setting from the database.");
            if (ApplicationVersion == null)
                throw new ApplicationException("Colud not load Application Version Setting from the database.");
        }

8 个答案:

答案 0 :(得分:16)

我的两分钱......

  1. 正如Bobby评论的那样,在每个一次性物品上使用
  2. 我会避免打开一个表并遍历所有记录,如果可能的话使用过滤器来获取值
  3. 如果根本不可能,请避免在字符串上使用开关,因为只有两个选项,如果使用字符串,则可以执行。与不区分大小写的选项比较,因此您不必每次都使用上限
  4. 在读取之前检查空值以避免未处理的异常
  5. 如果您必须在代码中多次打开这种连接,可以使用工厂方法为您提供连接。
  6. 当您已经知道要创建的对象类型时,我会避免使用“var”关键字。您通常会重构以增强代码的易读性。

答案 1 :(得分:2)

代码中存在轻微的低效率(大量字符串分配和不必要的查找)。

以下是代码中的一些变化:

  • 没有ToUpper()调用。 (ToUpper和 ToLower可以是evil
  • 缓存DataReader值
  • 没有ToString调用
  • 删除了DataTable实例创建(未使用)

结果代码如下所示:

using (var sqlConnection = new SqlConnection(Lfepa.Itrs.Framework.Configuration.ConnectionString))
{
    sqlConnection.Open();

    using(var selectCommand = new SqlCommand(Lfepa.Itrs.Data.Database.Commands.dbo.SettingsSelAll, sqlConnection))
    {
        using (var reader = selectCommand.ExecuteReader())
        {
            while (reader.Read())
            {
                string val = reader.GetString(reader.GetOrdinal(SettingKeyColumnName));
                if ( string.IsNullOrEmpty(val) )
                    continue;
                if ( val.Equals(DatabaseVersionKey, StringComparison.OrdinalIgnoreCase))
                    DatabaseVersion = new Version(val);
                else if (val.Equals(ApplicationVersionKey, StringComparison.OrdinalIgnoreCase))
                    ApplicationVersion = new Version(val);
            }
        }
    }
}

if (DatabaseVersion == null)
    throw new ApplicationException("Could not load Database Version Setting from the database.");
if (ApplicationVersion == null)
    throw new ApplicationException("Could not load Application Version Setting from the database.");

答案 2 :(得分:1)

这段代码实际上做了两件事:

1) Get the database version

2) Get the application version

唯一的共性是数据存储方法

我建议重构有三种方法:

// Get the entire dataset from the database using the SettingsSelAll command.
private DataTable GetVersionData() {...}
// Parse a version from the dataset.
private Version GetVersion(string versionName, DataTable dataSet) { ... }

public Version GetVersion(string versionName)
{
    DataTable table = GetVersionData();
    return GetVersion(versionName, table);
}

这在获取数据和实际使用数据之间强制分离,这总是需要的目标。建议使用一种缓存形式以避免两次执行查询,我建议可以使用方法同时执行数据库和应用程序版本调用。

答案 3 :(得分:1)

据推测,设置表中还有其他有用的值。我建议将所有值读入应用程序持有的字典中。然后根据需要查找值。与稍后再建立另一个连接并重新执行相同查询相比,获取所有记录而不仅仅是这两个记录所增加的费用是微不足道的。

答案 4 :(得分:0)

使用if而不是开关少于5个案例,它会更快。

答案 5 :(得分:0)

我会重写查询,以便返回包含两列的单个记录。这将摆脱using()语句中的条件。正如加布所说,我要加上

if (DatabaseVersion != null && ApplicationVersion != null) break;

在使用区内。

答案 6 :(得分:0)

我要补充的一个建议是在执行更多命令之前执行检查以确保已建立与数据库的连接。

sqlConn.Open();

if (sqlConn.State == ConnectionState.Open)
{

   // perform read settings logic here

}

答案 7 :(得分:0)

如果您希望通过更多设置来实现可持续性和扩展,请设置一个策略模式,用于存储处理字典中特定设置的每个策略以及相关的Key值,以便将正确的策略拉出来替换switch语句。