从代码中分离SQL字符串

时间:2011-04-22 08:36:36

标签: .net database

我在一个古老的大型WinForms项目上工作,该项目包含许多win表单库。

每个表单在方法中使用硬编码的sql命令。有时相同的SQL字符串是重复的,修改一个我应该研究表单代码中的“相似”字符串。

我知道架构并不是非常漂亮,但目前我们无法执行大型架构修改。我认为要做一些“小步骤”来改善UI和BD逻辑的分离......

第一个'步骤',例如。我认为sql字符串与代码的方式“分开”。如何使用Resources,一个特殊的类,一个XML文件来实现这一目标?

变体I - 使用命名资源

' ancient variant '
_SqlPeriod = String.Format("SELECT * FROM DBO.GP_PERIOD WHERE PERIOD = {0} ORDER BY LABEL", Me._Period)

' a better way (?) ... '
_SqlPeriod = String.Format(My.Resources.ResourceManager.GetString("SelectAPeriod"), Me._Period)

4 个答案:

答案 0 :(得分:2)

如果要将整个内容重写为存储过程,那么这就是.NET代码的外观:

 public List<User> listUsers(Guid pPersonTypeId, string pFirstName, string pLastName)
        {
            List<User> list = new List<User>();
            List<SqlParameter> pa = new List<SqlParameter>();
                       pa.Add(SqlHelper.createSqlParameter("@ID_PERSONTYPE", SqlDbType.UniqueIdentifier, pPersonTypeId != Guid.Empty ? (Guid?)pPersonTypeId : null));
            pa.Add(SqlHelper.createSqlParameter("@FNAME", SqlDbType.NVarChar, String.IsNullOrEmpty(pFirstName) ? null : pFirstName));
            pa.Add(SqlHelper.createSqlParameter("@LNAME", SqlDbType.NVarChar, String.IsNullOrEmpty(pLastName) ? null : pLastName));

            try
            {
                DataSet ds = SqlHelper.ExecuteDataset("proc_USER_list", pa.ToArray());
                if (ds.Tables.Count == 0)
                    return list;
                foreach (DataRow r in ds.Tables[0].Rows)
                {
                    User u = new User();
                    //populate your User object with data from the DataRow
                    ...
                    list.Add(u);
                }
                return list;
            }
            catch (Exception ex)
            {
                throw (new BaseException(ex));
            }
        }

您当然需要实现SQLHelper和SQLParameter类,这非常简单。

如果这不是你想要的方式,我建议创建XML文档(每个数据访问类一个),将查询存储在其中,并编写一个非常简单的包装类来获取基于查询的内容。在查询代码或ID上。例如,XML可能如下所示:

<query code="listUsers"> select * from USER where NAME = {0} </query>

为了更进一步,我猜你可以使用方法名作为查询代码,甚至让包装器使用反射来查看它被调用的方法,并使用方法的名称在XML中查找查询。

使用资源类来存储您的查询甚至更简单,但我不确定是否有任何好处,因为XML至少会让您分离。

答案 1 :(得分:2)

通常,您最关心的是使用String.Format来形成SQL查询,因为如果您没有明确验证参数,这很容易Sql Injection Attacks。因此,分离这些字符串并不是一种改进(如果在运行时解析它们,将它们移动到xml文件将是一个安全灾难。)

为了防止这种情况,您需要创建SqlCommand个实例并以编程方式设置参数(检查MSDN - How To: Protect From SQL Injection in ASP.NET),或使用完全不同的方法并切换到ORM框架。

如果您之前没有使用过ORM可能会花费相当长的时间,我相信您会选择第一个选项。在这种情况下,如上所述简单地切换到Sql参数将是一种改进(即使sql字符串保留硬编码)。

为防止重复,您应考虑将所有数据库访问调用移动到单独的项目(数据访问层,DAL)中。如果您想以较小的步骤开始,可以考虑(开始)将SQL字符串的创建委托给不同的层,例如:

_SqlPeriod = QueryStrings.GetPeriodsById(Me._Period) ' <- returns an sql string

这将允许您将参数验证移动到一个公共项目中。稍后,您将希望避免将sql字符串完全传递到UI层,只需获取数据:

_ActualData = Dal.GetPeriodsById(Me._Period) ' <- gets the actual list of periods

答案 2 :(得分:1)

我会去上课。首先,您只需使用getter调用替换文字sql语句以集中SQL访问。然后,您可以使用类的初始化代码来适当地调整语句(本地路径,日期,用户,其他环境因素)。稍后你可以重构吸气剂,以便在第二次通话时返回准备好的陈述或做其他奇特的事情。

资源只会给你一个字符串列表;在开发XML功能之前,.xml文件需要大量的结构规划和支持代码。

答案 3 :(得分:1)

我会使用存储过程而不是在代码,XML或资源中使用SQL。

您可以查看一下使用SP的一些优缺点。 What are the pros and cons to keeping SQL in Stored Procs versus Code

实际上,在这个帖子中,反对SP的人拥有最多的选票,但不要让那些让你失望:)。

我看到的最大优势之一是,只要您使用相同的参数并且数据集包含相同的列,您就可以在不更改客户端代码的情况下更改和测试SP。另一个是在创建查询时会根据依赖项检查查询,并且在更改表结构之前,您可以非常轻松地查明某些SP是否使用了表或列。当查询存储在资源中或嵌入代码

时,这并不容易