1

我需要实现下面的功能

     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);,这只是浪费时间。查询可以有不同的参数,我需要不依赖于查询中参数的函数。

4

3 回答 3

2

名为“GetAllPartyDetails”的函数不应接受sql 字符串作为参数。像这样的方法的目的是抽象出应用程序的其余部分了解或关心 sql 的需要,并简单地提供与数据库实现分开的派对详细信息的来源。它应该接受当事方的名称作为参数,但是如果您需要接受 sql 查询,那么您将在错误的位置构建您的 sql。

您需要的是一种可以以通用方式调用的方法,不仅可以从 GetPartyDetails() 调用,还可以从需要来自特定数据源的数据的其他方法调用。如果您在 GetPartyDetails 之外构建查询字符串,则需要稍微重新构建它。

检索任何数据的方法应该是什么样的?当然它需要接受一个 sql 字符串。它还需要某种方式来接受参数信息。这可以像键/值对数组一样简单,但我更喜欢避免两次构建参数集合的代码。这个参数也应该是必需的,而不是可选的或重载的,以鼓励良好的参数使用。

我目前使用这种模式,并且非常喜欢它:

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);
          }
       }
    }
}

这满足了我们通用数据访问方法的所有目标。我唯一不满意的部分是对翻译委托的需求,这并不是什么大的损失,因为无论如何这是您必须在应用程序的某个级别编写的代码。如果您不将每一行复制到该方法内的新对象,您可能会得到意想不到的结果,因此我们需要一种方法将数据记录直接转换为业务对象。

你可以这样称呼它:

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。这会导致注入问题。

您向数据提出的每个问题都属于它自己的方法。理想情况下,这些方法聚集在一个类中,或者将一组类聚集在一个大型应用程序中的单个项目中。

于 2013-05-09T16:40:37.200 回答
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
于 2013-05-09T16:40:25.847 回答
0

您需要将其转换为以name为参数的存储过程。

这是开始阅读的好地方。http://msdn.microsoft.com/en-us/library/ff648339.aspx

于 2013-05-09T16:15:12.507 回答