ShowProgramCode

2022年6月24日 星期五

C# 靜態物件在不同執行緒產生的問題

 工作中遇到了一個問題,為了加快速度,我將某個基礎函式定義為靜態物件,這個靜態物件中會送POST到某個機器上,並在裡面夾帶Cookie作為認證機制。

例如:

Public static class Test
{
private string cookie;
public void Send(string url, string content )
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
WebHeaderCollection headerCollection = request.Headers;
headerCollection.Add("Cookie", cookie);
request.ContentType = "text/xml";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(content);
streamWriter.Flush();
streamWriter.Close();
}

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
resp.httpStatus = response.StatusCode;
if (response.StatusCode == HttpStatusCode.OK)
{
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
resp.xmlRespone = sr.ReadToEnd();
}

if (!string.IsNullOrEmpty(response.GetResponseHeader("Set-Cookie")))
{
cookie = response.Headers.GetValues("Set-Cookie").ToList().FirstOrDefault();
}
}
response.Close();
}
}
}


TestController:
建構式:
private Test test = new Test();
其他呼叫函式TestSend():
test .Send(<<傳送機台網址>>,"sendContent");

一般情況是測不出問題的,可是某天客戶突然說出現了401回覆。

去看了Log才發現,當頻繁的呼叫這個函式(TestSend()),傳送機台那邊回覆401(沒有權限)。也就是雖然我有設定cookie值作為驗證,但是機器不認得這個驗證值,或者認定這個驗證值不屬於我,因此我沒有權限將資料送到機台處理。

為什麼呢?我百思不得其解,甚至還在考慮是否客戶的網路結構跟公司不同,在分流IP時產生了問題,造成去回不同路?

看了N個log,我才驚愕地發現一個事實。

我的驗證cookie在不同執行緒下,被蓋掉了!!!

從來不知道原來靜態物件是這樣設定的,在Controller中呼叫靜態物件後,它會占用同樣的儲存位置,因此不同執行緒執行時,靜態物件內部的設定值,即使是私有變數也可能會被蓋掉。

果然,當我取消靜態物件的設定,即使呼叫方式沒有改變,也不再出現驗證失敗401的錯誤了。

為防自己忘記,特別寫一下。並非設定私有變數就一定沒有問題,靜態物件的使用、呼叫必須謹慎。以上就是這次的教訓了。


2022年2月21日 星期一

EF Core SELECT WHERE IN 的用法

 很不幸的,最近支援一個專案,平時直接下SQL語法的我,對所謂的EF Core沒啥研究,但是卻遇到這次的架構使用了這個技術。然後,更不幸的是,居然要我想辦法幫忙修改查詢指令。

主要的問題是,每個使用者擁有不同權限,進入後看到的資料也不同,必須查詢多張表格才能整理出來,最終我提供了Table的Id List,讓他們把舊的查詢再加上一個條件,必須是Id List內相同的Id才顯示。

當然,最終這個語法還是我查出來了,特別紀錄起來以免下次遇到。

OrderIdList = List<int>{orderId1,orderId2...};
orders = _context.Orders.Include(o => o.OrdersPlaceMapping).Where(row => OrderIdList.Contains(row.Id)).OrderBy(o => o.Id).ToList();

2022年1月20日 星期四

ASP.NET CORE3.1 使用EF Core處理資料庫問題

建立書本的範例,書本使用的是Asp.Net Core 2.2版本,但考量實際專案改使用Asp.Net Core 3.1版本,然後自然會開始遇到一連串狀況了。目前的進度只到使用EF Core自動建立資料庫與資料表,後續的新增、修改、刪除、查詢功能都還沒開始處理,先記錄目前為止的問題。

  • EF Core需要安裝:
第一個狀況就是EF Core安裝,在書中並沒有說明這一段,猜測可能Asp.Net Core 2.2包含EF Core不須安裝此套件,總之第一個遇上的問題就是該安裝EF Core哪一個版本?
之後在同事的建議下,安裝了5.04版,這邊特別說明6.01需要.NET Core 6以上版本才能安裝,我目前的版本裝不了。

EF Core安裝截圖






安裝之後我開始依照範例處理DTO、資料庫連線設定等等,然後問題出現了,在Startup.cs的ConfigureServices中加入SQL服務時Error了,原因是找不到我使用的函式,如下圖:

設定使用EF Core做資料庫連線錯誤










  • EF Core相關套件需要安裝:
後來在同事的幫忙下,了解這是因為我少裝了一個套件,EF Core.SqlServer,如下圖:

安裝EF Core的SqlServer相關套件





安裝版本基本是跟EF Core的版本,安裝完剛剛在Startup.cs的錯誤就沒問題了。
接著我按照書上的說明,準備使用Code First方式,通過Migration建立資料庫與資料表。
首先要先找到使用指令的地方,工具/NuGet事件管理員/套件管理器主控台,順道一提因為書籍是簡體版,兩邊翻譯的不同,這個位置讓我找了很久。

使用命令列位置
















接著下達命令來建置Migration參考檔案,命令如下:

Add-Migration InitalCreation
然後當然又錯誤了(實際的錯誤訊息忘了,這是我將參考拿掉產生的)。

命令錯誤資訊







查了一陣子,發現又少裝一個套件,EF Core Tools。

安裝EF Core的Tools相關套件





緊接著,執行Add Migration沒有問題成功了,得到一個Migrations的資料夾,裡面設定了資料庫與資料表的建置方式。

於是依照範例繼續準備寫入資料庫中,命令如下:

Update-Database

然後又錯誤了,原來的錯誤訊息好像是和Initial Catalog相關,因為已經無法重現原來的問題,講一下印象中大意是說不可使用Initial Catalog字元,或者使用Initial Catalog有問題之類的。

這個錯誤大概是我找最久的,我當時不再公司只能自己尋找問題,偏偏對這個不熟悉找到的資訊也大多有問題,沒有人幫忙的情況下,最終才發現錯誤訊息的問題跟我考慮和尋找的方向有落差。

原因是我資料庫連線設定錯誤,把設定值放上來。

"DefaultConnection": "Data Source=localhost\\資料庫連線;Initial Catalog=資料庫名稱;Integrated Security=SSPI"

這是我從各式各樣的網頁找的答案拼湊出來的,這個設定值並不是給我目前的狀況使用,但範例原先的設定讓我新增成功卻找不到新增的資料庫,總之結論我把正確的設定值貼上。

"DefaultConnection": "server=localhost\\資料庫連線;database=資料庫名稱;Integrated Security=SSPI"

到此為止終於新增成功,且在資料庫連線工具中找到新增的資料庫了,可喜可賀。

繼續補充之後Migration自動建立假資料的動作

在連線設定繼承DbContext的物件中複寫OnModelCreating函式

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<AuthorDBDto>().HasData(List<AuthorDBDto>authors);
            modelBuilder.Entity<BookDBDto>().HasData(List<AuthorDBDto>books);
}

工具/NuGet事件管理員/套件管理器主控台

在此輸入指令 Add-Migration SeedData,在Migration資料夾中會多出SeedData的檔案,內容是List中想要新增的資料。然後在輸入指令Update-Database,將資料新增到資料庫中。如下圖:








要注意的是Add-Migration SeedData之後即便把SeedData檔案刪除,仍然已經將資料寫入某個地方,重新填寫資料時,剛剛已經寫入的資料不會寫入新的SeedData檔案。

接著測試自動刪除檔案,先輸入指令Add-Migration RemoveSeededData,在Migration資料夾中會多出RemoveSeededData的檔案,內容是剛剛新增的資料。然後在輸入指令Update-Database,將資料從資料庫中刪除。如下圖:








到此EF Core的測試完畢,真是太好了。

2022年1月6日 星期四

ASP.NET C# Fotify Cross-Site Scripting: Poor Validation(XSS)問題解決

 這次客戶使用Fortify進行掃描,多數的問題都已經解決,或者將公用函式做成.dll檔後,也都通過掃描標準。唯獨標題的問題,從開始到前日一直沒有解決,今天終於處理完畢,所以特別記錄下來。

依照我之前查詢的諸多參考網頁,多數說明解決這個問題的方法就是使用HtmlEncode()處理input,不讓程式執行含有HTML標籤的內容,也避免資料庫存入。

因此我將被掃描出來的地方,只要有被呼叫到的部分,不管input、output全部都使用HtmlEncode()處理,然而這沒有用,Fortify依舊堅持把這段程式掃出來了。

目前解決方法:
input:依舊使用HttpUtility.HtmlEncode()處理。
output:
HtmlSanitizer sanitizer = new HtmlSanitizer();
output = sanitizer.Sanitize(HttpUtility.HtmlDecode(data));

程式碼無法放上來,只能這樣說明,希望下次要是再遇到這樣的問題能夠提供參考。

後續發生一個意想不到的問題,這個第三方套件和Framework4.6.2的版本居然有參考檔會衝突。

原因是客戶不希望保留Nuget參考,希望所有第三方套件全部轉成.dll來參考,加上客戶的Framework版本是4.5升上來的,所以本機執行一直沒有發現錯誤,等到程式發佈到IIS上後居然無法執行。今天找了一整天終於知道有兩個套件參考的dll與4.6.2衝突了,需要改寫web.config設定。

將web.config修改部分放上來,真是始料未及的問題。

  <runtime>

          <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

<dependentAssembly>

<assemblyIdentity name="AngleSharp.Css" publicKeyToken="XXXXX" culture="neutral"/>

<bindingRedirect oldVersion="0.0.0.0-0.16.3.0" newVersion="0.16.3.0"/>

</dependentAssembly>

                  <dependentAssembly>

                          <assemblyIdentity name="AngleSharp" publicKeyToken="XXXXX" culture="neutral"/>

                          <bindingRedirect oldVersion="0.0.0.0-0.16.1.0" newVersion="0.16.1.0"/>

                  </dependentAssembly>

          </assemblyBinding>

  </runtime>


2021年12月3日 星期五

MS SQL工具改變資料表欄位自動增值失敗問題

今天我發現資料庫沒有建立主Key,且主Key欄位非自動增值造成我新增資料失敗後,便來調整資料庫的欄位設定。 

使用的工具是Microsoft SQL Server Management Studio。

不過當我把主Key調整好,設定自動增值如下圖:

將設定存檔時卻發生錯誤無法調整,如下圖:


最終在同事的幫忙下發現,這個錯誤訊息必須調整『工具』選項中的『設計師』中,把『防止儲存需要資料表重建的變更』這項設定取消。如下圖:













這樣就可以調整資料表的主Key欄位並讓它自動增加數值了。



2021年11月25日 星期四

改變.NET Framework版本

 如何不透過UI直接從檔案更改Framework版本?

原先我都是利用Visual Studio的介面更改Framework的版本,不過這次的專案不知為何改變版本後,Web Reference就發生錯誤,甚至無法編譯。

本來應該設法好好找出Web Reference就發生錯誤的原因,不過由於時程問題,我想到如果不透過介面直接從檔案更改Framework版本,是否可以避免因為重新讀取專案檔,系統檔改寫後編譯錯誤的問題。

終於,那麼更改Framework應該從哪個系統檔處理呢?

基本上就是.csproj檔案。

在這個檔案內容可以找到Framework目前的版本,直接修改版本號碼就可以改變Framework版本。

改變.sln專案檔版本

如何將.sln專案檔提升至2017版本

最近遇到客戶提出了這個要求,順便參考官方文件的說法,整理一下如何提升專案檔版本?

參考網站:https://docs.microsoft.com/zh-tw/visualstudio/extensibility/internals/solution-dot-sln-file?view=vs-2017


先不考慮專案內容是否會有衝突的問題,單純紀錄該如何修改.sln版本。

依照官網文件的說法,基本上相當簡單,只要修改檔案的表頭即可。

下列附上每個不同VS版本的.sln表頭。

  • VS2022

Microsoft Visual Studio Solution File, Format Version 12.00

# Visual Studio Version 16

VisualStudioVersion = 16.0.28701.123

MinimumVisualStudioVersion = 10.0.40219.1


  • VS2019

Microsoft Visual Studio Solution File, Format Version 12.00

# Visual Studio Version 16

VisualStudioVersion = 16.0.28701.123

MinimumVisualStudioVersion = 10.0.40219.1


  • VS2017

Microsoft Visual Studio Solution File, Format Version 12.00

# Visual Studio 15

VisualStudioVersion = 15.0.26730.15

MinimumVisualStudioVersion = 10.0.40219.1


  • VS2013

Microsoft Visual Studio Solution File, Format Version 12.00

# Visual Studio 2013

VisualStudioVersion = 12.0.40629.0

MinimumVisualStudioVersion = 10.0.40219.1

VS2022與VS2019的設定表頭設定基本是相同的,關於這個我暫時沒有找到更詳細的說法,因為我最近修改的版本為VS2013 => VS2017,經過修正之後,客戶那邊確認可以使用。

將官網針對表頭的說明複製上來,若有侵權疑慮請先告知,謝謝!


Microsoft Visual Studio Solution File, Format Version 12.00
定義檔案格式版本的標準標頭。

# Visual Studio 15
Visual Studio 的主要版本, (最近) 儲存此方案檔。 這項資訊會控制解決方案圖示中的版本號碼。

VisualStudioVersion = 15.0.26730.15
Visual Studio 的完整版本, (最近) 儲存的方案檔。 如果解決方案檔是由具有相同主要版本的較新版本 Visual Studio 所儲存,則不會更新此值,以便減少解決方案檔中的流失情形。

MinimumVisualStudioVersion = 10.0.40219.1
可以開啟此方案檔之 Visual Studio 的最小 () 版本。

也就是說,基本上只要置換中間兩行就能將.sln版本更換。

# Visual Studio 15

VisualStudioVersion = 15.0.26730.15

查詢此問題時,還有看到可以將專案拷貝各種不同版本的.sln檔案,方便使用不同的VS版本開啟的說法。

通常專案檔版本還是需要配合客戶需求,所以更改版本是有機會發生的問題,特別記錄下來。