2019-06-15

[ASP.NET] FileStream 只讀到第一列

有個需求是這樣的,每 60 秒會接收一次資料進來,每次有 60 筆,收進來的資料要存成檔案,所以用了以下程式來存成 Data.Json 檔:
// 資料 Append 到檔案 Data.Json
ConcurrentDictionary<int, Data> tempData = new ConcurrentDictionary<int, Data>();
...(略)

using (FileStream stream = new FileStream("Data.json", FileMode.Append))
{
    BinaryFormatter bformatter = new BinaryFormatter();
    bformatter.Serialize(stream, tempData);
}

然後等到有需要時,就用以下程式讀取 Data.Json 裡的資料出來:
// 載入Data.Json
ConcurrentDictionary<int, Data> realData = new ConcurrentDictionary<int, Data>();

if (File.Exists("Data.json"))
{
    using (FileStream stream = new FileStream("Data.json", FileMode.Open))
    {
        BinaryFormatter bformatter = new BinaryFormatter();
        realData = (ConcurrentDictionary<int, AccPullerData>)bformatter.Deserialize(stream);  
    }
}

但是讀取到的資料都只有前 60 筆,原來 FileStream 每次 Append 資料時,都屬於不同的 Position,而在 Read 時只會一次讀取一個 Position,所以以上的寫法只會取到第一個 Position。

需加上判斷是否已經讀到最後了才行,如下:
// 載入Data.Json
ConcurrentDictionary<int, Data> realData = new ConcurrentDictionary<int, Data>();

if (File.Exists("Data.json"))
{
    using (FileStream stream = new FileStream("Data.json", FileMode.Open))
    {
        BinaryFormatter bformatter = new BinaryFormatter();

        // 判斷是否已經讀到最後了
        while (stream.Position != stream.Length)
        {
            var temp = (ConcurrentDictionary<int, Data>)(bformatter.Deserialize(stream));
            foreach (var item in temp)
                realData.TryAdd(item.Key, item.Value);
        }
    }
}



參考來源:stackOverflow

2019-05-14

[MVC] Post Data to WebApi 發生缺少 CORS 'Access-Control-Allow-Origin' 檔頭

近日在使用 Angular POST Json 到 MVC 開發的 WebApi 時,出現以下錯誤訊息:

因為之前有遇過,知道是跨網域問題,很自然的使用之前的解法,就是在 WebApi 的 Web.Config 加上如下圖的資訊即可。


但是! 這次居然無效!! 

研究了老半天,實在是不知道哪裡出問題,只好另尋解法,找到使用 Microsoft.AspNet.WebApi.Cors 來設定,兩種方法都先記錄下來,方法如下:
  •  NuGet 下載 Microsoft.AspNet.WebApi.Cors
  • 在 WebApiConfig.cs 加上以下程式
  • 在 Controller 加上下圖紅框字,原因跟以前寫在 Web.Config 的一樣



參考來源:Microsoft

2019-05-11

[Angular] 無法使用 private !?

有個專案使用 ng serve 時,沒有出現 error:

但使用 ng build --prod 時,卻出現 error,一個只在自己 Component 裡使用的 private property 出現 access deny:


查了一下原因,是因為 TypeScript 最後會轉成 JavaScript,但是 JavaScript 不支援 private,才會出現 error,拿掉 private 之後就正常了。



參考來源:yakovfain.com

2019-05-09

[Angular] 部屬到 IIS

在將 Angular 部屬到正式機時,強烈建議使用指令 ng build --prod 將程式 build 過,不這樣做直接將程式放上去也是可以執行,只是 build 過是有好處的:
  • 只保留需要的 ng-modules,省下很多空間,以我的專案而言,空間從 230MB 剩下 1MB。
  • 檔名全部更名,程式碼也全部壓縮,安全性提升,如下圖:

而 build 完後,一樣能針對產生的 dist 資料夾使用 ng serve 來啟動網站,但是難免怕 command 視窗不小心被關閉了而網站無法執行。


所以可以使用以下方式將 dist 資料夾掛到 IIS 來啟動網站,就像 ASP.NET 一樣,但是要調整一些東西才能正常執行:
  • 在 IIS 新增應用程式,並將路徑指到 dist 下面的專案名稱資料夾,假設應用程式名稱為 ngHero。

  • 修改 dist 下面的專案名稱資料夾裡的 index.html,將 base href 改成 /應用程式名稱/,注意前後都有斜線,以這個例子就是 /ngHero/。



如此,在 Browser URL 打上 http://localhost/ngHero/ 就能順利執行了。

官網上有提到 IIS 須設定 URL Rewrite rule,我是沒有做這部份就能執行了,後續如果有什麼問題再來補充。



參考網址:stackOverflow

2019-03-31

[Angular] 中文亂碼問題

在開發 Angular 過程中,偶而會遇到中文顯示變成亂碼的問題,真的是偶而才會遇到,原因不明。

有試過調整 Browser 的編碼是無效的。

目前找到的解法是將 template (.html) 使用記事本打開,再另存新檔,存檔前將編碼選成 UTF-8 即可。




參考來源:stackOverflow

2018-10-20

[DB] Stored Procedure On SQL Server

老實說,我很少寫 Stored Procedure,上次寫也已經是2013年的事了。

最近要寫一張報表,因為表格每一個欄位計算複雜,所以不得不使用到 Stored Procedure 事先計算好,報表負責呈現就好,才不必等太久。

這次 DB 是 SQL Server,想說參考之前 Oracle 的 Stored Procedure 文章看看應該就行了,沒想到差異不小@@,就基本的跑迴圈來說就完成不同了。

Google 了一下大都用 Cursor,但是普遍說效能不好不建議,加上語法不好理解(對我來說...),然後找到以下的方法,覺得很適合我,便記錄下來當參考。

declare @Count

--將要跑迴圈的資料抓出來放到#TempTable
Select Product_ID into #TempTable From Product 

--計算 #TempTable 資料筆數,要用在跑迴圈
Select @Count = count(Product_ID) from #TempTable 

--開始跑迴圈
while(@Count > 0)
begin
    Select top 1 @Product_ID=Product_ID From #TempTable 

    --start do something 

    --將處理好的資料從 #TempTable 刪除
    Delete from #TempTable where Product_ID=@Product_ID   

    --重新計算 #TempTable 資料筆數,要用在跑迴圈
    Select @Count = count(Product_ID) from #TempTable
end


參考來源:遜砲賴的爆肝筆記

2018-09-15

[ASP.NET] 第一次使用 SQLite

  公司很多程式的 Log 因為寫入太頻繁怕影響到 DB Server 的效能而寫成 TXT 文字檔,但是在閱讀上很不方便,而且無法像 SQL 一樣多條件查詢出想找的 Log。 

  基於以上原因,開始研究也是屬於檔案型態的 SQLite,它的寫法跟 ADO.NET 很像,所以很好上手,而此次目的很簡單,只是想將寫 Log 的方式改變一下而已,所以沒有深入研究,以下只是入門款的記錄。

先使用 NuGet 安裝 System.Data.SQLite.Core。

請注意寫日期到 Log 時的方式:
using (SQLiteConnection conn = new SQLiteConnection())
{
    // 指定SQLite檔案的路徑和檔名
    conn.ConnectionString = "data source=D:\\Log\\Log.sqlite";
    conn.Open();

    // Create Table
    SQLiteCommand cmd = conn.CreateCommand();
    cmd.CommandText = @"Create Table Log (
                                           LogID        INTEGER PRIMARY KEY,
                                           Message      TEXT,
                                           CreateDate   DATETIME
                                          )";
    cmd.ExecuteNonQuery();

    // 寫Log。
    // 使用SQLite內建的datetime('now')時,需加8小時(480分)才會是台灣時間
    cmd.CommandText = @"Insert into Log (Message,CreateDate) values (@Message, datetime('now', '+480 minutes'))";
    cmd.Parameters.Clear();
    cmd.Parameters.Add(new SQLiteParameter("Message", strMessage));
    cmd.ExecuteNonQuery();

    // 寫Log,
    // 使用C#的DateTime.Now時,一定要使用SQLite Parameter,如果用字串相加的方式,Insert時不會有問題,但Select時就會因日期格式不符而讀取失敗
    cmd.CommandText = @"Insert into Log (Message,CreateDate) values (@Message, @Now)";
    cmd.Parameters.Clear();
    cmd.Parameters.Add(new SQLiteParameter("Message", strMessage));
    cmd.Parameters.Add(new SQLiteParameter("Now", DateTime.Now)); // 這裡不用再加8小時,雖然存入會少8小時,但C#讀出時會自動加8小時
    cmd.ExecuteNonQuery();
}

可以安裝 DB Browser for SQLite 來開啟 SQLite 檔案觀看內容。
已經將SQLite常用語法打包成 SQLiteLib,用法跟 DBLib 一樣,需要者請自行取用。


參考來源:Log有訊,Bug無邊黑暗執行緒