## 前言
最近遇到一個狀況,我包好的 dll 會用到 Newtonsoft.Json.dll (Json.Net),版本是 4.5。拿去給別人(A公司)用,他們也用了別人(B公司)的 dll,也用到 Newtonsoft.Json.dll (Json.Net),但是他們指定的是 9.0.0。當 Visual Studio 把我指定的 Newtonsoft.Json.dll 搬到執行檔旁邊,B公司的 dll 就無法啟動。當換成 B公司指定的,就換我的 dll 無法啟動。A公司的人大叫該怎麼辦?因為我們公司比較小,所以我要解決這個問題。
## 主旨
一般的解決方法都是叫大家重新抓過新版的 dll,像我們這次就是叫我這比較舊的版本要升級。但,支援的 .Net Framework 不能超過 4.5.2,超過會讓其他家的 dll 無法運作。這作法也沒什麼不好,只是沒有一勞永逸。要是將來我的 dll 給了幾家客戶,他們幾家要的 Newtonsoft.Json.dll 都不一樣,或是他們升級了,我這小公司就得配合找到對應的 Newtonsoft.Json.dll 來 rebuild 才行。
網路上用不同關鍵字找過,我認為這個比較好。https://michaelscodingspot.com/how-to-resolve-net-reference-and-nuget-package-version-conflicts/
## 超譯
重點,解法有兩個方向,四個方法。
兩個方向是 (1) 如果可以,全部改成同一個版本。 (2) 使用 dll side-by-side loading。
四個解法之中,作者建議解法(1)
### 解法(1) Use a single assembly version with Binding Redirect
在有 app.config 或 dll.config 的存在時,增加一個 runtime 標籤。舉例:
```
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="1.2.11" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
```
作者舉 log4net 為例,加上 runtime 標籤指示 CLR 使用 1.2.11 版的 log4net。
### 解法(2) Override AssemblyResolve for side-by-side loading (No need for strong names)
使用 AppDomain.CurrentDomain.AssemblyResolve 事件來處理不同版本的尋找問題。
首先將要處理的 dll 的 CopyLocal 設成 False。並將不同版本的 dll 放到不同磁碟位置。然後在執行檔的最開頭註冊 AssemblyResolve 事件。它在 app 找不到 dll 的時候會被呼叫,然後我們就寫好程式來處理它。
範例:
```
static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, resolveArgs) =>
{
string assemblyInfo = resolveArgs.Name;// e.g "Lib1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
var parts = assemblyInfo.Split(',');
string name = parts[0];
var version = Version.Parse(parts[1].Split('=')[1]);
string fullName;
if (name == "Lib1" && version.Major == 1 && version.Minor == 0)
{
fullName = new FileInfo(@"V10\Lib1.dll").FullName;
}
else if (name == "Lib1" && version.Major == 1 && version.Minor == 1)
{
fullName = new FileInfo(@"V11\Lib1.dll").FullName;
}
else
{
return null;
}
return Assembly.LoadFile(fullName);
};
```
### 解法(3) Copy assemblies to different folders and use <codebase> to load them side-by-side (Requires strong names)
這個方法需要 dll 有強式名字。這點 Newtonsoft.Json.dll 或 log4net 這類公開的 dll 做得到。或是自己做的 dll 也來個強式名字也行。將要處理的 dll 的 CopyLocal 設成 False。
舉例:
```
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="StrongNameLib" culture="neutral" publicKeyToken="6a770f8bdf3d476a" />
<codeBase version="1.0.0.0" href="StrongNameLibV10/StrongNameLib.dll"/>
<codeBase version="1.1.0.0" href="StrongNameLibV11/StrongNameLib.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
```
這就是告訴 CLR 按照版本去找 dll。如果沒有強式名字,CLR 會忽略版本,所以總是找到第一個 codeBase 所列的 dll。href 也可以用絕對路徑,像是 @"FILE://c:/Lib/MyLib.dll"
### 解法(4) Install assemblies to the Global Assembly Cache (GAC)
這個方法是把要用到的 dll 註冊到 GAC (Global Assembly Cache) 裡。這會需要 dll 有強式名字,而且還要使用者手動執行指令(或是用安裝程式)將 dll 註冊到 GAC 裡。例如:
```
gacutil.exe -i "c:\Dev\Project\Debug\MyLib.dll"
```
將要處理的 dll 的 CopyLocal 設成 False。依照 runtime 的尋找規則,會先找尋 GAC 有沒有可用 dll,所以因為 GAC 都有註冊,所以各自會找到對應的版本來使用。
這個方法會需要使用者手動註冊或者使用 installer 將 dll 註冊到 GAC。也就這樣而已。但是有些人就是會嫌……。
2019年10月1日 星期二
2019年7月18日 星期四
[點網]從 WSDL 檔產生伺服器端介面
常常會忘記這件事,寫下來提醒自己
重點:
* 用 wsdl.exe 產生 proxy class 時,加上參數 /serverInterface
- 記得還有 asmx 要處理
參考:
https://stackoverflow.com/questions/1394930/how-to-generate-web-service-out-of-wsdl
延申:
self host wcf
https://docs.microsoft.com/zh-tw/dotnet/framework/wcf/how-to-host-a-wcf-service-in-a-managed-application
https://social.msdn.microsoft.com/Forums/vstudio/en-US/aaa6b8d5-fc49-45ca-9cab-6d5bea60fd3b/configuring-in-code-a-selfhosted-web-service?forum=wcf
https://social.msdn.microsoft.com/Forums/en-US/19c23ba7-6de8-4052-86aa-6a81f74327c6/port-issue-on-a-self-hosted-asmx?forum=asmxandxml
https://stackoverflow.com/questions/30332510/running-a-asmx-web-service-as-a-windows-service
https://dottutorials.net/creating-soap-web-services-dot-net-core-tutorial/
重點:
* 用 wsdl.exe 產生 proxy class 時,加上參數 /serverInterface
- 記得還有 asmx 要處理
參考:
https://stackoverflow.com/questions/1394930/how-to-generate-web-service-out-of-wsdl
延申:
self host wcf
https://docs.microsoft.com/zh-tw/dotnet/framework/wcf/how-to-host-a-wcf-service-in-a-managed-application
https://social.msdn.microsoft.com/Forums/vstudio/en-US/aaa6b8d5-fc49-45ca-9cab-6d5bea60fd3b/configuring-in-code-a-selfhosted-web-service?forum=wcf
https://social.msdn.microsoft.com/Forums/en-US/19c23ba7-6de8-4052-86aa-6a81f74327c6/port-issue-on-a-self-hosted-asmx?forum=asmxandxml
https://stackoverflow.com/questions/30332510/running-a-asmx-web-service-as-a-windows-service
https://dottutorials.net/creating-soap-web-services-dot-net-core-tutorial/
2019年1月16日 星期三
[點網] 非同步程式模型簡介
# 點網的異步程式模型
## 前言
從現在回頭看,還存在 .NET 裡面的非同步模型有三種:
* Event-based Asynchronous Model(EAM): methods + event handlers
* Asynchronous Programming Model(APM): Begin + End methods + IAsyncResult
* Task-based Asynchronous Programming(TAP) Model:Task object + (async/await)
在更早之前的就先跳過不討論。從這三種來看看差異在哪裡。
## Event-based Asynchronous Model(EAM)
第一種在 System.Net.WebClient 還看得到,例如,它使用 DownloadDataAsync 與 DownloadDataCompleted 來完成非同步操作。
```
public partial class Form1 : Form
{
System.Net.WebClient webClient;
public Form1()
{
InitializeComponent();
webClient = new System.Net.WebClient();
webClient.DownloadDataCompleted += WebClient_DownloadDataCompleted;
webClient.DownloadDataAsync(new Uri("http://www.google.com"), "[form constructor trigger]");
Debug.WriteLine("[form constructor]");
}
private void WebClient_DownloadDataCompleted(object sender, System.Net.DownloadDataCompletedEventArgs e)
{
Debug.WriteLine((string)e.UserState);
Debug.WriteLine(BitConverter.ToString(e.Result));
}
}
```
首先要先註冊 DownloadDataCompleted 發生時,要用自己寫的哪個函式來處理,範例中是用WebClient_DownloadDataCompleted 來接。當我們呼叫完 DownloadDataAsync 之後,就是等待事情做完,然後 WebClient_DownloadDataCompleted 會被某個 thread 呼叫執行,我們要做的事情就是在這裡寫好。如果有需要分辨不同需求的 DownloadDataAsync 所產生的 WebClient_DownloadDataCompleted,則可以把資訊放在 DownloadDataAsync 的第二個參數,由 e.UserState 拿回攜帶的資訊。
"[form constructor]" 在網頁內容之前就先寫出來,代表不是 blocking。
## Asynchronous Programming Model(APM)
第二種,則在新一點的物件中出現,像是 System.Net.WebRequest。例如,它使用 BeginGetResponse 與 AsyncCallback 這種 delegate callback 合體使用。
```
public Form1()
{
InitializeComponent();
System.Net.WebRequest webRequest = System.Net.WebRequest.Create("http://www.google.com");
webRequest.BeginGetResponse(new AsyncCallback(
(IAsyncResult ar) =>
{
System.Net.WebRequest req = (System.Net.WebRequest)ar.AsyncState;
System.Net.WebResponse resp = req.EndGetResponse(ar);
long cl = resp.ContentLength;
byte[] buffer = new byte[cl];
resp.GetResponseStream().Read(buffer, 0, (int)cl);
Debug.WriteLine(BitConverter.ToString(buffer));
}
), webRequest);
Debug.WriteLine("[form constructor]");
}
```
使用上,在 BeginGetResponse 把 callback 一同放進呼叫參數裡,我在這裡故意使用箭頭函式寫法,這樣可以顯出與 EAM 的不同。關注點就不同跳開在程式碼太遠的地方。當然也可以先準備好一個函式,呼叫 BeginGetResponse 時放進去。這樣的程式長像就會類似 EAM。像這樣,BeginGetResponse 與 callback 的對應就不容易錯亂。callback 攜帶資訊的地方在 BeginGetResponse 的第二個參數,callback 裡則用 ar.AsyncState 拿回來。
## Task-based Asynchronous Programming(TAP) Model
第三種又在新一點,它在 System.Net.Http.HttpClient 有用到。例如它使用 GetAsync 拿到 Task 物件,再利用 task 的操作拿到想要的內容。
```
public Form1()
{
InitializeComponent();
System.Net.Http.HttpClient httpClient;
httpClient = new System.Net.Http.HttpClient();
Task<System.Net.Http.HttpResponseMessage> taskHttpResponseMessage = httpClient.GetAsync("http://www.google.com");
taskHttpResponseMessage.ContinueWith(new Action<Task<System.Net.Http.HttpResponseMessage>>(
(Task<System.Net.Http.HttpResponseMessage> t) =>
{
System.Net.Http.HttpResponseMessage httpResponseMessage = taskHttpResponseMessage.Result;
byte[] c = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
Debug.WriteLine(BitConverter.ToString(c));
}
));
Debug.WriteLine("[form constructor]");
}
```
在這裡,GetAsync 拿到 Task 物件,然後在 ContinueWith 裡使用箭頭函式處理呼叫成功之後該做什麼事。這樣子寫,程式看起來還像是 APM 的樣子,但是 Task 物件有自己攜帶 wait 之類的方法,在控制要不要 blocking 的時候更自由。
## Task-based Asynchronous Programming(TAP) Model + async/await
TAP 模式寫起來還是很像 APM 模式,多層疊或是流程長的話,程式碼也還是不容易看。但是在 async/await 修飾字出現之後,程式碼就好看很多。其原理僅是靠編譯器幫忙。由於 async/await 無法在 constructor 使用,我程式移到一個 getPage() 驅動。上面的例子就變得跟同步程式長得很像。
```
public Form1()
{
InitializeComponent();
getPage();
Debug.WriteLine("[form constructor]");
}
async void getPage()
{
System.Net.Http.HttpClient httpClient;
httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("http://www.google.com");
byte[] c = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
Debug.WriteLine(BitConverter.ToString(c));
}
```
## 總結
有時候會看到上面三述方法混和提供的物件,以後就可以按照需要自由取用。就功能來說這三種做法可以互相取代。當然以團隊來說,可以自己決定的話,就統一一種會比較好。
## 參考
* https://docs.microsoft.com/zh-tw/dotnet/standard/asynchronous-programming-patterns/index
* https://www.infoq.com/articles/Tasks-Async-Await
## 前言
從現在回頭看,還存在 .NET 裡面的非同步模型有三種:
* Event-based Asynchronous Model(EAM): methods + event handlers
* Asynchronous Programming Model(APM): Begin + End methods + IAsyncResult
* Task-based Asynchronous Programming(TAP) Model:Task object + (async/await)
在更早之前的就先跳過不討論。從這三種來看看差異在哪裡。
## Event-based Asynchronous Model(EAM)
第一種在 System.Net.WebClient 還看得到,例如,它使用 DownloadDataAsync 與 DownloadDataCompleted 來完成非同步操作。
```
public partial class Form1 : Form
{
System.Net.WebClient webClient;
public Form1()
{
InitializeComponent();
webClient = new System.Net.WebClient();
webClient.DownloadDataCompleted += WebClient_DownloadDataCompleted;
webClient.DownloadDataAsync(new Uri("http://www.google.com"), "[form constructor trigger]");
Debug.WriteLine("[form constructor]");
}
private void WebClient_DownloadDataCompleted(object sender, System.Net.DownloadDataCompletedEventArgs e)
{
Debug.WriteLine((string)e.UserState);
Debug.WriteLine(BitConverter.ToString(e.Result));
}
}
```
首先要先註冊 DownloadDataCompleted 發生時,要用自己寫的哪個函式來處理,範例中是用WebClient_DownloadDataCompleted 來接。當我們呼叫完 DownloadDataAsync 之後,就是等待事情做完,然後 WebClient_DownloadDataCompleted 會被某個 thread 呼叫執行,我們要做的事情就是在這裡寫好。如果有需要分辨不同需求的 DownloadDataAsync 所產生的 WebClient_DownloadDataCompleted,則可以把資訊放在 DownloadDataAsync 的第二個參數,由 e.UserState 拿回攜帶的資訊。
"[form constructor]" 在網頁內容之前就先寫出來,代表不是 blocking。
## Asynchronous Programming Model(APM)
第二種,則在新一點的物件中出現,像是 System.Net.WebRequest。例如,它使用 BeginGetResponse 與 AsyncCallback 這種 delegate callback 合體使用。
```
public Form1()
{
InitializeComponent();
System.Net.WebRequest webRequest = System.Net.WebRequest.Create("http://www.google.com");
webRequest.BeginGetResponse(new AsyncCallback(
(IAsyncResult ar) =>
{
System.Net.WebRequest req = (System.Net.WebRequest)ar.AsyncState;
System.Net.WebResponse resp = req.EndGetResponse(ar);
long cl = resp.ContentLength;
byte[] buffer = new byte[cl];
resp.GetResponseStream().Read(buffer, 0, (int)cl);
Debug.WriteLine(BitConverter.ToString(buffer));
}
), webRequest);
Debug.WriteLine("[form constructor]");
}
```
使用上,在 BeginGetResponse 把 callback 一同放進呼叫參數裡,我在這裡故意使用箭頭函式寫法,這樣可以顯出與 EAM 的不同。關注點就不同跳開在程式碼太遠的地方。當然也可以先準備好一個函式,呼叫 BeginGetResponse 時放進去。這樣的程式長像就會類似 EAM。像這樣,BeginGetResponse 與 callback 的對應就不容易錯亂。callback 攜帶資訊的地方在 BeginGetResponse 的第二個參數,callback 裡則用 ar.AsyncState 拿回來。
## Task-based Asynchronous Programming(TAP) Model
第三種又在新一點,它在 System.Net.Http.HttpClient 有用到。例如它使用 GetAsync 拿到 Task 物件,再利用 task 的操作拿到想要的內容。
```
public Form1()
{
InitializeComponent();
System.Net.Http.HttpClient httpClient;
httpClient = new System.Net.Http.HttpClient();
Task<System.Net.Http.HttpResponseMessage> taskHttpResponseMessage = httpClient.GetAsync("http://www.google.com");
taskHttpResponseMessage.ContinueWith(new Action<Task<System.Net.Http.HttpResponseMessage>>(
(Task<System.Net.Http.HttpResponseMessage> t) =>
{
System.Net.Http.HttpResponseMessage httpResponseMessage = taskHttpResponseMessage.Result;
byte[] c = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
Debug.WriteLine(BitConverter.ToString(c));
}
));
Debug.WriteLine("[form constructor]");
}
```
在這裡,GetAsync 拿到 Task 物件,然後在 ContinueWith 裡使用箭頭函式處理呼叫成功之後該做什麼事。這樣子寫,程式看起來還像是 APM 的樣子,但是 Task 物件有自己攜帶 wait 之類的方法,在控制要不要 blocking 的時候更自由。
## Task-based Asynchronous Programming(TAP) Model + async/await
TAP 模式寫起來還是很像 APM 模式,多層疊或是流程長的話,程式碼也還是不容易看。但是在 async/await 修飾字出現之後,程式碼就好看很多。其原理僅是靠編譯器幫忙。由於 async/await 無法在 constructor 使用,我程式移到一個 getPage() 驅動。上面的例子就變得跟同步程式長得很像。
```
public Form1()
{
InitializeComponent();
getPage();
Debug.WriteLine("[form constructor]");
}
async void getPage()
{
System.Net.Http.HttpClient httpClient;
httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("http://www.google.com");
byte[] c = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
Debug.WriteLine(BitConverter.ToString(c));
}
```
## 總結
有時候會看到上面三述方法混和提供的物件,以後就可以按照需要自由取用。就功能來說這三種做法可以互相取代。當然以團隊來說,可以自己決定的話,就統一一種會比較好。
## 參考
* https://docs.microsoft.com/zh-tw/dotnet/standard/asynchronous-programming-patterns/index
* https://www.infoq.com/articles/Tasks-Async-Await
2019年1月6日 星期日
[git] 不想一直輸入帳號密碼
如果每次跟遠端更新都要輸入密碼,有好處就是讓自己不會忘記密碼,但是有時要全部專案同時更新備份下來,寫了個批次檔來做這事,卻會停下來一個個輸入密碼就很討厭。這時候就加上一個設定,就可以讓輸入過的密碼自己記下來省掉麻煩。
在 windows 這裡要這樣設定:
在 windows 這裡要這樣設定:
git config --global credential.helper wincred關於 timeout 的設定就沒有看到了。
2019年1月3日 星期四
[點網] Asynchronous Programming Model(APM) 改成 blocking 模式
需求就是要把 BeginInvoke + EndInvoke + IAsyncResult 這種非同步模式改成同步模式。
似乎應該說是阻塞(Blocking)模式。
需要的是用到 System.Threading.AutoResetEvent 這個物件。
注意 signal.Set() 與 signal.WaitOne() 的位置
還有 signal 宣告的範圍
byte[] ReadTheFile(string filepath)
{
var signal = new AutoResetEvent(false);
var buf = new byte[1024];
var stream = File.OpenRead(filepath);
int readCount = 0;
stream.BeginRead(buf, 0, buf.Length, new AsyncCallback(asyncResult =>
{
FileStream myStream = (FileStream)asyncResult.AsyncState;
readCount = myStream.EndRead(asyncResult);
signal.Set();
}), stream);
signal.WaitOne();
stream.Dispose();
var result = new byte[readCount];
buf.CopyTo(result, 0);
return result;
}
我這麼做的原因是想把只有 APM 的改成 TAP,先把 APM 改成 block 再改成 TAP。也許將來有更好的方法。
似乎應該說是阻塞(Blocking)模式。
需要的是用到 System.Threading.AutoResetEvent 這個物件。
注意 signal.Set() 與 signal.WaitOne() 的位置
還有 signal 宣告的範圍
byte[] ReadTheFile(string filepath)
{
var signal = new AutoResetEvent(false);
var buf = new byte[1024];
var stream = File.OpenRead(filepath);
int readCount = 0;
stream.BeginRead(buf, 0, buf.Length, new AsyncCallback(asyncResult =>
{
FileStream myStream = (FileStream)asyncResult.AsyncState;
readCount = myStream.EndRead(asyncResult);
signal.Set();
}), stream);
signal.WaitOne();
stream.Dispose();
var result = new byte[readCount];
buf.CopyTo(result, 0);
return result;
}
我這麼做的原因是想把只有 APM 的改成 TAP,先把 APM 改成 block 再改成 TAP。也許將來有更好的方法。
訂閱:
文章 (Atom)