我目前有一个数据访问层,它也使用Web服务公开一些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从数据库生成数据的应用程序?
答案 0 :(得分:5)
恕我直言,正确的方法是调用存储过程。在您的C#代码中,您只需引用存储过程并传递正确类型的参数。在C#代码中构建的临时SQL为许多事情打开了大门 - 其中最重要的是SQL注入和计划缓存的低效使用。此外,如果不重新编译和重新部署应用程序,您将很难重构查询。这对于某些更改是必要的(例如,当存储过程的接口发生更改时),但对于许多其他典型的查询更改而言,这不是必需的。
答案 1 :(得分:4)
你提出了几个问题,你向我们展示的代码会带来更多问题。你可能想要考虑的事情:
尝试将以数据库为中心的活动限制在自己的类中。没有其他人需要知道如何返回GlobalStats
对象的行列表,而不是处理从数据库中提取数据并将其作为实际对象的实际类。没有人。如果其他人确实知道,那么你的类没有使用信息隐藏(因为我们使用的是面向对象的语言,你应该这样做。)
如果对象实现了IDisposable
,那么你应该用try {} finally {}
块包装它,或者甚至更好,用using
语句包装它(参见我的第二个例子)下文)。
您的连接字符串只能由实际需要它的类访问(分离您的关注点的一部分)。也许有一个具有该信息的基础DataAccess类?
public abstract class DataAccess
{
protected const string ConnectionString = "YourConnectionStringHere";
}
然后你的存储库可以从这个类继承,并且你没有一个全局静态常量导致你的代码被不必要地耦合。
您的代码现在的方式(如果您的代码确实是您向我们展示的示例),那么您可能已经获得了SQL Injection problem。这值得立即修复(我将在下面向您展示一个例子)。
这是我写你正在写的东西的方式(小心,这段代码并不是真正意义上的用途,仅用于说明目的):
[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;
}
答案 2 :(得分:2)
Stored Procedures或LINQ to SQL是C#中两种流行的数据库访问范例。
有关LINQ to SQL的更多信息,请查看Scott Gu博客上的Using Linq to SQL。