2011-04-22 38 views
2

我在一个古老的大型WinForms项目中工作,该项目包含许多win表单库。代码中的单独SQL字符串

每种形式在方法中使用硬编码的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) 

回答

2

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

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

由于如果您以前没有使用过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 
1

我会去的一类。起初,你只需用getter调用来替换字面的sql语句以集中SQL访问。然后,您可以使用该类的init代码来适当调整语句(本地路径,日期,用户和其他环境因素)。稍后,您可以重构获得者,以便在第二次电话会议上返回准备好的语句或做其他奇特的事情。

资源会给你一个字符串列表; .xml文件需要大量的结构规划和支持代码才能利用XML功能。

+0

每个表单类,每个项目或每个解决方案? – serhio 2011-04-22 09:18:53

+0

我以这种方式工作,一个班级做这项工作。每个表格,项目,解决方案的类?取决于您的项目大小和SQL跨项目使用情况(您是否在所有项目之间使用相同的代码?)。如果是的话,一个包含你班级的项目将是最好的选择。 – 2011-04-22 09:51:46

+0

@serhio:如果您将课程放入其自己的项目中并且仅仅使用该项目一次,那么您就没有任何问题。 “每个表格”对我来说似乎“不集中”。 – 2011-04-22 10:09:28

1

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

你可以看看这个使用SP的一些利弊。 What are the pros and cons to keeping SQL in Stored Procs versus Code

事实上,在这个线程中,对SP的家伙拥有最多的选票,但不要让你失望:)。

我看到的最大优点之一是,只要您使用相同的参数并且数据集包含相同的列,就可以在不更改客户端代码的情况下更改和测试SP。另一个原因是在创建它们时检查依赖关系的查询,并且在更改表结构之前,可以很容易地发现某个表或列是否被某个SP使用。当查询存储在远离资源或嵌入代码不是那么容易

+0

无论如何,你仍然会有像“EXEC MY STORED PROCEDURE {arg1},{arg2},{argX}”这样的sql字符串。这对我来说不是一个“步骤”,因为我将“从MYTABLE中选择*”改写为存储过程。至少,目前... – serhio 2011-04-22 09:04:43

+0

@serhio - 我不认为你需要写这样的代码。我使用Delphi,所以我不知道.NET中有什么可用的,但我认为你应该有一些处理SP的对象,将SP名称放在一个属性中,并将这些参数添加到参数集合中。正如我所看到的,将SQL从代码移动到SP,除了将其移动到资源或XML文件之外,没有更多的工作要做。 – 2011-04-22 09:13:25

+0

如果我是(最)票的人,我不是反对SP的人。我只是认为SP不适合serhio的情况。 – 2011-04-22 10:15:53

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

我认为你的建议表明,使用替代方法需要更多的初始工作,然后才能收集好处。 – 2011-04-22 10:12:12

+1

重写应用程序以使用存储过程是很多工作,但在这一点上使用XML方法似乎非常容易。我真的没有看到使用类来存储字符串的好处。 – Filip 2011-04-22 10:38:45