我需要实现以下功能
public PartyDetails GetAllPartyDetails(string name)
{
try
{
String query = "select * from [Party Details] where name=@name ";
pd = new PartyDetails();
com = new SqlCeCommand(query, con);
com.Parameters.AddWithValue("@name", name);
con.Open();
sdr = com.ExecuteReader();
while (sdr.Read())
{
pd.name = sdr.GetString(0);
}
con.Close();
return pd;
}
catch (Exception e)
{
con.Close();
throw e;
}
}
但是这个函数对我来说效率不高,因为我不需要仅仅因为查询的改变而编写不同的函数代码。
现在我需要的是
public PartyDetails GetAllPartyDetails(string query)
{
try
{
pd = new PartyDetails();
com = new SqlCeCommand(query, con);
con.Open();
sdr = com.ExecuteReader();
while (sdr.Read())
{
pd.name = sdr.GetString(0);
}
con.Close();
return pd;
}
catch (Exception e)
{
con.Close();
throw e;
}
}
但它增加了sql注入的风险,因为它没有使用com.Parameters.AddWithValue("@name", name);
。是否可以通过调用函数来停止sql注入来实现这一点。
对于那些不理解我的问题的人
例如,我有另一个查询select * from [party details] where address=@address and name=@anme
,为此,我需要再次编写一个函数,其中我使用com.Parameters.AddWithValue("@address", address);
com.Parameters.AddWithValue("@name", name);
,这只是浪费时间。查询可以有不同的参数,我需要的函数不依赖于查询中的参数。
答案 0 :(得分:2)
名为“GetAllPartyDetails”的函数应该不接受sql字符串作为参数。这样的方法的目的是抽象出应用程序的其余部分需要知道或关心sql,并简单地提供与数据库实现分开的派对详细信息的源。它应该接受派对的名称作为参数,但是如果你需要接受一个sql查询,你就是在错误的地方构建你的SQL。
您需要的是一种方法,不仅可以通过GetPartyDetails()以及其他需要来自特定数据源的数据的方法以通用方式调用。如果您在GetPartyDetails之外构建查询字符串,则需要重新构建一点。
检索任何数据的方法应该是什么样的?当然它需要接受一个sql字符串。它还需要某种方式来接受参数信息。这可能就像键/值对的数组一样简单,但我更喜欢避免需要两次构建参数集的代码。此参数也应该是 required ,而不是可选或重载,以鼓励使用良好的参数。
我目前使用这种模式,我非常喜欢它:
private IEnumerable<T> GetData(string sql, Action<SqlParameterCollection> addParams, Func<IDataRecord, T> translate)
{
using (var cn = new SqlConnection("connection string here"))
using (var cmd = new SqlCommand(sql, cn))
{
addParams(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return translate(rdr);
}
}
}
}
这符合我们的通用数据访问方法的所有目标。我不感兴趣的唯一部分是对翻译代表的需求,并没有那么大的损失,因为这是你必须在你的应用程序的某个级别编写的代码。如果不将每一行复制到该方法中的新对象,则可能会产生意外结果,因此我们需要一种方法将datarecord转换为业务对象。
你会这样称呼:
public string GetPartyDetailsByName(string name)
{
return GetData("select * from [Party Details] where name=@name", p =>
{
p.Add("@name", SqlDbType.NVarChar, 50).Value = name;
}, row =>
{
row.GetString(0);
}).First();
}
如果您有另一个带参数的查询,您可以这样称呼它:
public string GetPartyDetailsByNameAddress(string name, string address)
{
return GetData("select * from [Party Details] where name=@name and address=@address", p =>
{
p.Add("@name", SqlDbType.NVarChar, 50).Value = name;
p.Add("@address", SqlDbType.NVarChar,200).Value = address;
}, row =>
{
row.GetString(0);
}).First();
}
不带任何参数的方法如下所示:
public IEnumerable<string> GetAllPartyDetails()
{
return GetData("select * from [Party Details]", p => {}, row =>
{
row.GetString(0);
});
}
这有点尴尬,但这就是重点。你希望人们故意关于不使用参数,所以他们不禁绊倒了正确的方式。
我知道您希望避免编写两种方法,但这是处理数据访问的正确方式。是的,有一个与数据库对话的方法,以帮助抽象出一些样板代码。但是为每个sql查询添加一个额外的方法仍然是正确的做法。
您不需要完全遵循我的GetData()方法:功能样式对某些人来说有点多。但是,您确实需要一个可以向数据库发送查询的唯一方法,并且此方法必须具有一些接受参数数据的机制。其他方法应该不传递sql。这导致了注射问题。
您询问数据的每个问题都属于它自己的方法。理想情况下,这些方法聚集在一个类或一组类中,这些类聚集在一个项目中,用于更大的应用程序。
答案 1 :(得分:1)
我建议使用可选参数和条件逻辑的组合。这是一般的想法。我并不担心语法。
public PartyDetails GetAllPartyDetails(string name = string.Empty,
string address = string.Empty)
{
String query="select * from [Party Details] where 1=1 ";
if (name != string.Empty)
{
query = query + " and name = @name";
code to add parameter
}
repeat for all arguments
rest of function
答案 2 :(得分:0)
您需要将此转换为以name
作为参数的存储过程。
这是一个开始阅读的好地方。 http://msdn.microsoft.com/en-us/library/ff648339.aspx