1

我目前有一个数据访问层,它也使用 Web 服务公开一些 API。

API 调用

[WebMethod]
public List<GlobalStat> GetStats()
{
    List<GlobalStat> Stats = new List<GlobalStat>();

    string sql = @"
       A huge multi-line SQL query
    ";

    try
    {
        string ConString = Constants.connString;
        con = new SqlConnection(ConString);

        cmd = new SqlCommand(sql, con);
        con.Open();
        dr = cmd.ExecuteReader();

        while (dr.Read())
        {
            GlobalStat stat = new GlobalStat();
            stat.Key = dr[0].ToString();
            stat.Value = int.Parse(dr[1].ToString());

            Stats.Add(stat);
        }

    }
    catch (Exception x)
    {

        Response.Write(x);
    }

    return Stats;
}

我有点担心 SQL 的编写方式。

有很多东西硬编码在其中:数据库名称,表名称等。

为了解决这个问题,我是只在一个地方创建一个包含所有 SQL 命令的单独全局文件,还是有更好的范例?我没有在应用程序中创建 SQL 表,但这些表驻留在不同的预构建数据库中。

我应该如何构建使用内联 SQL 从数据库生成数据的应用程序?

4

3 回答 3

5

恕我直言,正确的方法是调用存储过程。在您的 C# 代码中,您只需引用一个存储过程并传递正确类型的参数。在您的 C# 代码中构建的即席 SQL 为许多事情打开了大门——其中最重要的是 SQL 注入和计划缓存的低效使用。此外,它使您很难在不重新编译和重新部署应用程序的情况下重构查询。这对于某些更改是必要的(例如,当存储过程的接口更改时),但对于许多其他典型的查询更改则不需要。

于 2012-06-13T01:11:22.057 回答
4

您提出了几个问题,而您向我们展示的代码又提出了一些问题。您可能需要考虑的事情:

  • 尝试将以数据库为中心的活动限制在它们自己的类中。GlobalStats除了处理从数据库中提取数据并将其变为实际对象的实际类之外,没有其他人需要知道如何为对象返回行列表。没有人。如果其他人确实知道,那么您的班级没有使用信息隐藏(因为我们使用的是面向对象的语言,所以您应该使用)。

  • 如果对象 implements IDisposable,那么你应该用一个try {} finally {}块来包装它,或者更好的是,用一个using语句来包装它(见我下面的第二个例子)。

  • 您的连接字符串应该只能被实际需要它的类访问(分离您的关注点的一部分)。也许有一个包含该信息的基本 DataAccess 类?

    public abstract class DataAccess 
    {
        protected const string ConnectionString = "YourConnectionStringHere";
    }
    

然后您的存储库可以从此类继承,并且您没有导致代码不必要地耦合的全局静态常量。

以下是我将如何编写您正在编写的内容(请注意,此代码并不是真正要使用的,仅用于说明目的):

[WebMethod]
public List<GlobalStat> GetStats()
{
    GlobalStatsRepository repository = new GlobalStatsRepository();
    List<GlobalStat> stats = repository.GetStats();
    return stats;
}

数据访问层

public class GlobalStatsRepository
{
    public List<GlobalStat> GetStats()
    {

         string sql = @"SELECT * from GlobalStats"; //no, not a good practice

        var stats = new List<GlobalStat>();

        try
        {
            string ConString = Constants.connString;
            conn = new SqlConnection(ConString);

            cmd = new SqlCommand(sql, conn);
            conn.Open();
            dr = cmd.ExecuteReader();

            while (dr.Read())
            {
                GlobalStat stat = new GlobalStat();
                stat.Key = dr[0].ToString();
                stat.Value = int.Parse(dr[1].ToString());

                stats.Add(stat);
            }

        }
        catch (SQLDataReaderException ex)
        {
            logger.Log(ex);
            throw;
        }
    return stats;
    }
}

参数化查询示例

public List<GlobalStat> GetStatsById(int id)
{
    var stats = new List<GlobalStat>();

    string sql = @"SELECT * from GlobalStats WHERE Id = @Id";
    using (SqlConnection conn = new SqlConnection(ConString))
    {
         conn.Open();
         using (SQLCommand command = new SqlCommand(sql, conn))
         {         
             command.Parameters.Add(new SqlParameter("Id", id));
             SqlDataReader reader = command.ExecuteReader();
             while (reader.Read())
             {
                  GlobalStat stat = new GlobalStat();
                  stat.Key = dr[0].ToString();
                  stat.Value = int.Parse(dr[1].ToString());

                  stats.Add(stat);
             }
         }
     }
     return stats;
}
于 2012-06-13T01:17:09.417 回答
2

存储过程LINQ to SQL是 C# 中两种流行的数据库访问范例。

有关 LINQ to SQL 的更多信息,请查看Scott Gu 博客上的Using Linq to SQL 。

于 2012-06-13T01:17:01.583 回答