前幾天為工作中的一個實際問題編寫了一個小工具,但應該說“麻雀雖小, 五髒俱全”,同樣涉及到了設計、編碼、測試幾個過程,並且其中涉及到一些有 趣的知識,拿出來和大家分享一下。
首先來說明一下想要解決的問題:
當前每個月公司中的某個部門都會在特定的一天(一般是月初)下載大量的 數據,這些數據是從數據庫中取得的,生成文本格式的文件,然後用來做後續的 處理,類似於EDI的程序。但是,後續處理的程序需要所有的記錄都是經過排序 的,而現在的下載數據的程序是這樣做的,直接寫帶有Order by的SQL語句,然 後直接生成數據。但是這樣的話,程序運行的時間非常長,因為在服務器中的內 存中進行排序,會耗費大量的資源。這還不是主要問題,主要的問題在於下載數 據的時候會對其他部門進行系統操作造成極大的影響,甚至於會造成系統的停頓 和沒有響應。
因此我考慮在下載數據的時候不對數據進行排序,而是把排序工作拿出來, 用一個專門的小程序來實現,這樣就可以即使會造成性能下降也只局限在運行小 工具程序的那台機器,而不會對公司其他用戶造成影響。(後來發現對本機的性 能也沒有太大的影響)。
遵循這個思路,我所需要做的就是開發一個專門用來對這些數據排序的小工 具。為了提高工作的效率,我是用VS 2005來開發,盡管公司中大多數項目使用 的都是Java,呵呵。
首先是如何對文本文件中的數據進行排序,其實很簡單,我只是將這個文本 文件作為數據源,配置在ODBC中了。
然後呢,在VS 2005的服務器管理器中就可以配置對這個數據源的訪問。
但是,此時會有兩個問題:
一是對身份證號碼這樣的字段,系統會默認為是浮點數,這樣查詢出來看到 的記過是科學計數法的形式,而實際上我想要的是文本的類型。
這個問題可以使用配置數據源的Schema來解決,
裡面可以將每個字段都指定為文本類型,這樣就方便導出了。
但是隨之而來的就會有第二個問題,那就是這個文件必須指定文件名,而每 次需要排序的文件名都是不一樣的。
對這個問題,我是這樣解決的,先將需要排序的文件copy到這個臨時目錄中 ,並指定固定的文件名,然後在這個目錄中針對固定的文件名進行字段類型的配 置就可以了。針對這個文件排序之後,生成一個排序好的文本文件,然後再把這 個文件復制到指定的目標文件中。
這樣基本上技術問題就都解決掉了。最後是對界面的一個設計,先看一下界 面:
這裡我設置了一個小技巧,當用戶選擇了源文件之後,我會自動根據這個名 字,在後面加上_Sorted,作為輸出的文件名,這樣就方便用戶了。
最後,下面是以下關鍵的代碼:
//將選擇的文件名稱顯示在源文件的輸入框中
OpenFileDialog openSourceFile = new OpenFileDialog();
initializeFileOpenDialog(openSourceFile);
if (openSourceFile.ShowDialog() == DialogResult.OK)
{
String fileName = openSourceFile.FileName;
txtSourceFile.Text = fileName;
//如果文件名不為空,則自動生成目標文件的名稱
if (!fileName.Trim().Equals(String.Empty))
{
int extendNamePosition = fileName.IndexOf (".");
String targetFileName = fileName.Substring (0, extendNamePosition) + "_Sorted" + "." + fileName.Substring (extendNamePosition + 1);
txtTargetFile.Text = targetFileName;
}
}
這是排序的操作以及相應的文件操作,其中使用了微軟的Enterprise Library 3.1的DataAccess類庫。而且讀出來的數據直接放在IDataReader中處理 ,比使用DataSet會快不少。
private void btnSort_Click(object sender, EventArgs e)
{
if (txtSourceFile.Text.Trim().Equals(String.Empty))
{
MessageBox.Show("請先選擇源文件。");
return;
}
if (txtTargetFile.Text.Trim().Equals(String.Empty))
{
MessageBox.Show("請先指定目標文件名。");
return;
}
//先將源數據復制到指定的文件夾中
//然後以該文件夾中的數據作為數據源進行操作
CurrentJobInfo.Text = "正在將文件復制到臨時文件夾中… …";
CopyFileToTempPath();
//對臨時文件夾中的數據進行排序
CurrentJobInfo.Text = "正在排序……";
Database db = DatabaseFactory.CreateDatabase ("DataStore");
String strSql = CreateSql();
DbCommand dbCommand = db.GetSqlStringCommand (strSql);
//准備輸出的文件
String sortedFileName = getSortedFileName();
FileInfo sortedFile = new FileInfo(TEMP_DATA_PATH + sortedFileName);
StreamWriter writer = sortedFile.CreateText();
//將數據寫入到文件中
using (IDataReader dr = db.ExecuteReader (dbCommand))
{
while (dr.Read())
{
StringBuilder sbRecord = new StringBuilder ();
for (int i = 0; i < dr.FieldCount - 1; i++)
{
sbRecord.Append(dr[i].ToString() + ",");
}
sbRecord.Append(dr[dr.FieldCount - 1].ToString());
writer.WriteLine(sbRecord.ToString());
}
}
writer.Close();
//將排序好的文件輸出到目標文件中
File.Delete(txtTargetFile.Text);
File.Copy(TEMP_DATA_PATH + sortedFileName, txtTargetFile.Text);
CurrentJobInfo.Text = "完成!";
}
希望對這個問題的解決思路能夠對大家解決類似的問題有些幫助,也希望大 家一起來討論,:)