2011-04-22 37 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