2013-03-17 59 views
1

我的控制檯應用程序正在從文本文件中讀取大量數據,並將這些數據保存到數據庫中。爲此,我將數據存儲到DataTable中,並且我希望每5分鐘將此DataTable轉儲到數據庫(如果我想立即轉儲整個數據,那麼我必須將整個數據填充到DataTable中,在這種情況下我得到OutOfMemoryException)。C#:從一個foreach循環中每5分鐘調用一次方法

public void ProcessData() 
{ 
    string[] files=File.ReadAllLines(path) 
    foreach(var item in files) 
    { 
     DataRow dtRow= dataTable.NewRow(); 
     dtRow["ID"]= .... //some code here; 
     dtRow["Name"]= .... //some code here; 
     dtRow["Age"]= .... //some code here; 

     var timer = new Timer(v => SaveData(), null, 0, 5*60*1000); 
    } 
} 

public void SaveData(string tableName, DataTable dataTable) 
{ 
    //Some code Here 
    //After dumping data to DB, clear DataTable 
    dataTable.Rows.Clear(); 
} 

我想這裏是什麼,代碼將繼續填充DataTable中,每5分鐘便將調用保存數據()方法。這將繼續運行,直到所有文件處理完畢。

但是,我已經看到,當調用SaveData()方法時,它會執行4-5次。有時候,它每5分鐘就會有一次機器人召喚。

我沒有得到如何在這裏進行。如何解決這個問題?這裏可以使用其他方法嗎?任何幫助表示讚賞。

+2

一旦達到皇冠行數後,通過文本文件進行分頁,然後將數據保存到數據庫中。 – 2013-03-17 14:20:26

+2

在時間基礎上將數據轉儲到數據庫時,如果在這些分鐘內以某種方式進行了更多的讀取,仍然可能會導致內存異常。你不應該在foreach中啓動計時器,而是每次你點擊一些任意數量的行時 - 保存。 – Algirdas 2013-03-17 14:26:04

回答

2

這裏將是對如何實現代碼,並從對方的回答中建議,一個建議:

public void ProcessData() 
    { 
     int i = 1; 
     foreach(var item in File.ReadLines(path)) //This line has been edited 
     { 
      DataRow dtRow= dataTable.NewRow(); 
      dtRow["ID"]= .... //some code here; 
      dtRow["Name"]= .... //some code here; 
      dtRow["Age"]= .... //some code here; 
      if (i%25 == 0) //you can change the 25 here to something else 
      { 
       SaveData(/* table name */, /* dataTable */); 
      } 
      i++; 
     } 
     SaveData(/* table name */, /* dataTable */); 
    } 

    public void SaveData(string tableName, DataTable dataTable) 
    { 
     //Some code Here 
     //After dumping data to DB, clear DataTable 
     dataTable.Rows.Clear(); 
    } 
+0

是的,這很好。但是如果文件數量是510,會發生什麼?然後它會錯過最後10條記錄。 – SKJ 2013-03-17 15:08:15

+0

感謝@Bunyip的領導。 – Jsterman 2013-03-17 15:09:54

+0

謝謝@Jsterman ...我喜歡這個...... – SKJ 2013-03-17 15:12:09

4

重要的是你用ReadAllLines完全讀取每個文本文件,這將消耗大量的內存。爲什麼不從文件中讀取x行,保存到數據庫,然後繼續直到文件結束?

+0

是的,也可以。感謝評論:)。但是,我真的有興趣可以使用計時器來完成嗎? – SKJ 2013-03-17 14:36:01

3

你最大的問題是在你的foreach實例化新Timer實例。新的Timer對象在每個foreach調用中意味着多個線程同時調用SaveData,這意味着dataTable被同時多次處理並保存到數據庫,可能(並且可能)在行被清除之前將多個文件複製到數據庫中。

在我提出問題的解決方案之前,我想指出的是,在5分鐘的時間間隔內保存數據具有明顯的代碼異味。正如已經指出的那樣,我會提出一些基於某些數據大小而不是任意時間間隔加載和保存數據的方法。這就是說,我會繼續解決你的問題,假設你有5分鐘間隔保存必須去的原因。

首先,我們需要正確設置我們的Timer,您會發現我在foreach循環之外創建。 Timer在一個時間間隔內繼續運行,不僅僅是等待並執行一次。

其次,我們必須採取措施,以確保我們的中間數據存儲線程安全的數據完整性(在你的情況,你所用DataTable,但我使用的是自定義類的List,因爲DataTable是太昂貴了我們想做)。在更新我們的List之前,您會注意到我通過locking完成了此操作。

更新您的數據處理類:

private bool isComplete = false; 
private object DataStoreLock = new object(); 
private List<MyCustomClass> myDataStore; 
private Timer myTimer; 

public void ProcessData() 
{ 
    myTimer = new Timer(SaveData, null, TimeSpan.Zero, TimeSpan.FromMinutes(5.0)); 
    foreach (var item in File.ReadLines(path)) 
    { 
     var myData = new MyCustomClass() 
      { 
       ID = 0, // Some code here 
       Name = "Some code here", 
       Age = 0 // Some code here 
      }; 
     lock (DataStoreLock) 
     { 
      myDataStore.Add(myData); 
     } 
    } 
    isComplete = true; 
} 

public void SaveData(object arg) 
{ 
    // Our first step is to check if timed work is done. 
    if (isComplete) 
    { 
     myTimer.Dispose(); 
     myTimer = null; 
    } 
    // Our next step is to create a local instance of the data store to work on, which 
    // allows ProcessData to continue populating while our DB actions are being performed. 
    List<MyCustomClass> lDataStore; 
    lock (DataStoreLock) 
    { 
     lDataStore = myDataStore; 
     myDataStore = new List<MyCustomClass>(); 
    } 
    //Some code DB code here. 
} 

編輯:我已經改變了枚舉要經過ReadLines而非ReadAllLines。閱讀備註ReadLines method on MSDNReadAllLines將是阻止呼叫,而ReadLines將允許在讀取文件時處理枚舉。我無法想象一個場景,否則你的foreach將運行超過5分鐘,如果文件已被全部讀到內存。