Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 将SQL字符串与代码分开_.net_Database - Fatal编程技术网

.net 将SQL字符串与代码分开

.net 将SQL字符串与代码分开,.net,database,.net,Database,我从事一个古老的大型WinForms项目,该项目包含许多win form库 每个表单在方法中使用硬编码的sql命令。有时相同的SQL字符串会被复制,修改其中一个我应该研究表单代码中的“相似”字符串 我知道体系结构不是非常漂亮,但目前我们无法进行大规模的体系结构修改。我想做一些“小步骤”来改善UI和BD逻辑的分离 第一步,例如,我认为sql字符串和代码在某种程度上是“分离”的。如何使用资源、特殊类和XML文件实现这一点 变体I-使用命名资源 ' ancient variant ' _SqlPeri

我从事一个古老的大型WinForms项目,该项目包含许多win form库

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

我知道体系结构不是非常漂亮,但目前我们无法进行大规模的体系结构修改。我想做一些“小步骤”来改善UI和BD逻辑的分离

第一步,例如,我认为sql字符串和代码在某种程度上是“分离”的。如何使用资源、特殊类和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)

我会去上课。首先,您只需用getter调用替换literal sql语句,以集中sql访问。然后可以使用类的init代码在适当的地方(本地路径、日期、用户、其他环境因素)微调语句。稍后,您可以重构getter,以便在第二次调用时返回准备好的语句,或者执行其他有趣的操作


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

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

您可以查看一下使用SP的一些优缺点。

事实上,在这条线索中,反对SP的人拥有最多的选票,但不要因此而让你失望:)


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

如果将整个过程重写到存储过程中,.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));
            }
        }
公共列表用户(Guid pPersonTypeId、字符串pFirstName、字符串pLastName)
{
列表=新列表();
List pa=新列表();
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));
尝试
{
数据集ds=SqlHelper.ExecuteDataset(“proc_USER_list”,pa.ToArray());
如果(ds.Tables.Count==0)
退货清单;
foreach(ds.Tables[0].行中的数据行r)
{
用户u=新用户();
//使用数据行中的数据填充用户对象
...
增加(u);
}
退货清单;
}
捕获(例外情况除外)
{
抛出(newbaseexception(ex));
}
}
当然,您需要实现SQLHelper和SQLParameter类,这相当简单

如果您现在不想这样做,我建议您创建一个XML文档(每个数据访问类一个),将查询存储在其中,然后编写一个非常简单的包装类,根据查询代码或ID获取查询。XML可能如下所示:

<query code="listUsers"> select * from USER where NAME = {0} </query>
select*from USER where NAME={0}
更进一步,我想您可以使用方法名作为查询代码,甚至让包装器使用反射来查看调用它的方法,并使用方法名在XML中查找查询


使用资源类来存储查询甚至更简单,但我不确定这是否有任何好处,因为使用XML至少可以实现一些分离

一般来说,您最关心的是使用
String.Format
来形成SQL查询,因为如果您不显式验证参数,这很容易发生错误。因此,分离这些字符串并不是一种改进(如果它们在运行时得到解决,将它们移动到xml文件中将是一场安全灾难)

为了防止这种情况,您需要创建
SqlCommand
实例并通过编程设置参数(检查),或者使用完全不同的方法切换到ORM框架

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

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

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

\u SqlPeriod=QueryStrings.getperiodsbyd(Me.\u Period)'无论如何,您仍然会有类似“EXEC MY存储过程{arg1}、{arg2}、{argX}”这样的sql字符串。对于我来说,将“select*fromMyTable”的那些命令重写到存储过程中并不是一个小小的“步骤”。至少,目前…@serhio–我认为您不需要这样编写代码。我使用Delphi,所以我不知道.NET中提供了什么,但我认为应该有一种对象来处理SP,将SP名称放在一个属性中,然后将参数添加到参数集合中。在我看来,将SQL从代码移动到SP并不比将其移动到资源或XML文件更麻烦,
_ActualData = Dal.GetPeriodsById(Me._Period) ' <- gets the actual list of periods