Get()请求中SQL语句/连接的最佳实践

时间:2016-01-26 16:36:16

标签: c# http asp.net-web-api

对于简单查找,我需要在DB2机器上执行一些SQL语句。我目前无法使用ORM。我有一个通过这个代码的工作示例,但我想知道它是否可以进行更多优化,因为这实际上会在每个请求上创建一个连接。这看起来好像编程错误。

有没有办法可以优化此Get()请求以保持连接打开?嵌套using语句似乎也很脏。我应该如何处理这样一个事实:Get()真的想要返回User的对象,无论如何;即使是错误的?我可以将这个连接放在程序的开头,以便我可以一遍又一遍地使用它吗?对此有哪些最佳实践?

public class UsersController : ApiController
{
    String constr = WebConfigurationManager.ConnectionStrings["DB2Connection"].ConnectionString;

    public User Get([FromUri] User cst)
    {
        if (cst == null)
        {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
        else
        {
            using (OdbcConnection DB2Conn = new OdbcConnection(constr))
            {
                DB2Conn.Open();
                using (OdbcCommand com = new OdbcCommand(
                    // Generic SQL Statement
                    "SELECT * FROM [TABLE] WHERE customerNumber = ?", DB2Conn))
                {
                    com.Parameters.AddWithValue("@var", cst.customerNumber);

                    using (OdbcDataReader reader = com.ExecuteReader())
                    {
                        try
                        {
                            while (reader.Read())
                            {
                                cst.name = (string)reader["name"];

                                return cst;
                            }
                        }
                        catch
                        {
                            throw;
                        }
                    }
                }
            }
            return cst;
        }
    }
}

我发现great question并没有真正详细的答案,我觉得这两个问题都存在类似的解决方案......

2 个答案:

答案 0 :(得分:2)

  

这看起来好像编程错误。

你为什么这么想?

底层系统应该为您维护连接池中的连接。创建连接应该非常优化。

从逻辑的角度来看,您现在正在做的事情正是您想要做的事情。创建连接,使用它,并立即处理它。这允许其他线程/进程/等。现在您已经完成了它,从连接池中使用它。

这也避免了无数因手动维护使用它们的代码之外的打开连接而产生的问题。

  

有没有办法可以优化此Get()请求以保持连接打开?

测量实际的性能问题了吗?如果没有,那就无法优化。

在您的Web应用程序中,在静态上下文中打开连接时,非常好的机会将会产生严重的性能影响。

简而言之......你已经正确地做到了这一点。 (好吧,除了那个不必要的尝试/捕获。你可以删除它。)

修改:如果您只是希望提高代码的可读性(这本身就是个人喜好),这对我来说似乎是可读的:

public User Get([FromUri] User cst)
{
    if (cst == null)
        throw new HttpResponseException(HttpStatusCode.NotFound);

    using (var DB2Conn = new OdbcConnection(constr))
    using (var com = new OdbcCommand("SELECT * FROM [TABLE] WHERE customerNumber = ?", DB2Conn))
    {
        com.Parameters.AddWithValue("@var", cst.customerNumber);
        DB2Conn.Open();
        using (OdbcDataReader reader = com.ExecuteReader())
            while (reader.Read())
            {
                cst.name = (string)reader["name"]
                return cst;
            }
    }
    return cst;
}

请注意,您可以通过重新寻址该SQL查询的逻辑来进一步改进它。由于您从一条记录中获取一个值,因此您不需要遍历数据阅读器。只需获取一个文字并将其返回。请注意,这是免费的,未经测试,但它可能看起来像这样:

public User Get([FromUri] User cst)
{
    if (cst == null)
        throw new HttpResponseException(HttpStatusCode.NotFound);

    using (var DB2Conn = new OdbcConnection(constr))
    using (var com = new OdbcCommand("SELECT name FROM [TABLE] WHERE customerNumber = ? FETCH FIRST 1 ROWS ONLY", DB2Conn))
    {
        com.Parameters.AddWithValue("@var", cst.customerNumber);
        DB2Conn.Open();
        cst.name = (string)com.ExecuteScalar();
    }
    return cst;
}

答案 1 :(得分:1)

@David的答案完美地解决了您的实际问题,但这里有一些其他观察结果可能会让您的代码更适合您:

  • 删除try / catch块 - 你正在做的就是重新抛出异常,如果你根本不使用try / catch会发生什么。除非你能做某事,否则不要抓住异常。 (我现在看到@David的答案解决了这个问题 - 无论是在我阅读之后添加还是我错过了它 - 我为重叠道歉但值得加强)
  • 将您的查询更改为仅提取name并使用ExecuteScalar代替ExecuteReader。您从第一个记录中取出name值并退出while循环。 ExecuteScalar会返回第一条记录中第一列的值,因此您可以删除while循环和using。{/ li>