<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>程式設計 彙整 - 泰克哪裡去</title>
	<atom:link href="https://tech.uccu.website/category/programming/feed" rel="self" type="application/rss+xml" />
	<link>https://tech.uccu.website/category/programming</link>
	<description>一個科技相關的隨手記錄網站</description>
	<lastBuildDate>Sat, 17 Sep 2022 20:51:26 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>
<site xmlns="com-wordpress:feed-additions:1">119574712</site>	<item>
		<title>C#透過Process的StandardInput輸入Stream時卡住的可能問題</title>
		<link>https://tech.uccu.website/process-standard-input-output-deadlock.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=process-standard-input-output-deadlock</link>
					<comments>https://tech.uccu.website/process-standard-input-output-deadlock.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Thu, 10 Mar 2022 04:26:59 +0000</pubDate>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[程式設計]]></category>
		<category><![CDATA[Process]]></category>
		<category><![CDATA[StandardInput]]></category>
		<category><![CDATA[StandardOutput]]></category>
		<category><![CDATA[Tika]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=1712</guid>

					<description><![CDATA[<p>事情是這樣子的…前兩天同事跟我說有一個使用了Tika的程式在處理txt文字檔有時候不知道什麼原因會卡住死掉，問 ... <a title="C#透過Process的StandardInput輸入Stream時卡住的可能問題" class="read-more" href="https://tech.uccu.website/process-standard-input-output-deadlock.html" aria-label="Read more about C#透過Process的StandardInput輸入Stream時卡住的可能問題">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/process-standard-input-output-deadlock.html">C#透過Process的StandardInput輸入Stream時卡住的可能問題</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>事情是這樣子的…<br>前兩天同事跟我說有一個使用了<a href="https://tika.apache.org/" target="_blank" rel="noreferrer noopener">Tika</a>的程式在處理txt文字檔有時候不知道什麼原因會卡住死掉，問我可不可以改用CancellationToken的方式讓它超過時間就取消處理？</p>



<p>程式碼大概是這樣的…</p>



<span id="more-1712"></span>



<pre class="wp-block-prismatic-blocks"><code class="language-csharp">public string Extract(Stream stream)
{
    var Arg = $&quot;-jar {TikaPath} -t --encoding=UTF-16 -&quot;;

    //run Tika

    var p = new Process();
    p.StartInfo.FileName = JavaPath;
    p.StartInfo.Arguments = Arg;
    p.StartInfo.CreateNoWindow = true;
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardInput = true;
    p.StartInfo.RedirectStandardOutput = true;
    p.Start();

    var stdin = p.StandardInput.BaseStream;
    stream.CopyTo(stdin);
    stdin.Close();

    var output = p.StandardOutput.ReadToEnd();
    p.WaitForExit();

    return output;
}</code></pre>



<p>從別的地方讀取了檔案之後呼叫了這個方法，傳入了一個Stream，透過Stream.CopyTo的方式將傳入的內容複製到Tika的StandardInput，用ConsoleApp很簡單的弄了一個測試，逐步執行的時候會發現就卡在Stream.CopyTo這個地方，但是如果是pdf檔案就可以順利執行。</p>



<p>Stream.CopyTo有一個非同步的方法是CopyToAsync，稍微試了一下就算用了CancellationToken嘗試在超過時間要取消操作，實際上仍然是沒辦法中止卡在這個地方的情況。而且這樣根本也不知其所以然，到底為什麼卡住還是不知道原因。</p>



<p>OK，既然是卡在這個Stream複製的地方，而且不同的檔案(txt vs pdf)在相同的程式碼是不同的結果，pdf可以正常執行，txt卻不行，那就是txt檔案的內容在輸入StandardInput的時候發生了問題。</p>



<p>想要知道發生什麼問題，過程中還試了StandardError，要看看是不是發生什麼錯誤輸出了錯誤訊息到這裡面，不過從內容中去找尋線索未果，不過在<a href="https://docs.microsoft.com/zh-tw/dotnet/api/system.diagnostics.process.standarderror?view=net-6.0" target="_blank" rel="noreferrer noopener">官方文件上</a>看到有可能發生死結的情況，不知道為何卻想到試著改用內容較少的txt檔案，在同樣的程式碼運行卻可以正常。</p>



<p>最終在多個不同的假設測試下，發現會卡住的原因是txt檔案太大，內容過多的情況下會卡在Stream.CopyTo這個環節，所以判斷可能是在複製到StandardInput的過程中也會將處理的結果輸出到StandardOutput，但是StandardOutput一直收收收，收到緩衝區滿了沒處理，CopyTo這裡傳給Tika，Tika說「你等等，我在等Output處理」，我們的程式是在後面才呼叫StandardOutput.ReadToEnd，所以在Stream.CopyTo的這裡一直等Tika接收完內容，最終形成了死結…</p>



<p>處理這個問題不困難，除了StandardOutput.ReadToEnd這個方式處理內容之外，它還有個非同步的處理方式，在前面先建立一個StringBuilder來接收內容，最後再改成從StringBuilder取得所有內容，結果就是相同的。最後修改後的程式碼如下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-csharp">public string Extract(Stream stream) 
{
    var Arg = $&quot;-jar {TikaPath} -t --encoding=UTF-16 -&quot;;

    //run Tika

    StringBuilder output = new StringBuilder();

    var p = new Process();
    p.StartInfo.FileName = JavaPath;
    p.StartInfo.Arguments = Arg;
    p.StartInfo.CreateNoWindow = true;
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardInput = true;
    p.StartInfo.RedirectStandardOutput = true;

    p.StartInfo.StandardOutputEncoding = Encoding.BigEndianUnicode;
    p.OutputDataReceived += new DataReceivedEventHandler((s, e) =&gt; output.AppendLine(e.Data));

    p.Start();
    p.BeginOutputReadLine();

    var stdin = p.StandardInput.BaseStream;
    stream.CopyTo(stdin);
    stdin.Close();

    p.WaitForExit();

    return output.ToString();
}</code></pre>



<p>修改後的內容改用OutputDataReceived事件處理每次收到的Output內容放入StringBuilder，只是這邊有個地方要特別注意，那就是StandardOutput的Encoding要特別設定，不然結果會和使用StandardOutput.ReadToEnd的內容不同(編碼不同)。</p>



<p>參考資訊：</p>



<ul class="wp-block-list"><li><a href="https://docs.microsoft.com/zh-tw/dotnet/api/system.diagnostics.process.standarderror" target="_blank" rel="noreferrer noopener">https://docs.microsoft.com/zh-tw/dotnet/api/system.diagnostics.process.standarderror</a></li><li><a href="https://docs.microsoft.com/zh-tw/dotnet/api/system.diagnostics.process.standardinput" target="_blank" rel="noreferrer noopener">https://docs.microsoft.com/zh-tw/dotnet/api/system.diagnostics.process.standardinput</a></li><li><a href="https://stackoverflow.com/questions/26384772/deadlock-while-writing-to-process-standardinput" target="_blank" rel="noreferrer noopener">https://stackoverflow.com/questions/26384772/deadlock-while-writing-to-process-standardinput</a></li><li><a href="https://stackoverflow.com/questions/36918007/beginoutputreadline-and-encoding" target="_blank" rel="noreferrer noopener">https://stackoverflow.com/questions/36918007/beginoutputreadline-and-encoding</a></li><li><a href="https://docs.microsoft.com/en-us/sysinternals/downloads/procmon" target="_blank" rel="noreferrer noopener">https://docs.microsoft.com/en-us/sysinternals/downloads/procmon</a></li><li><a href="https://blog.darkthread.net/blog/977/" target="_blank" rel="noreferrer noopener">https://blog.darkthread.net/blog/977/</a></li></ul>
<p>這篇文章 <a href="https://tech.uccu.website/process-standard-input-output-deadlock.html">C#透過Process的StandardInput輸入Stream時卡住的可能問題</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/process-standard-input-output-deadlock.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1712</post-id>	</item>
		<item>
		<title>Asp.Net Core Kestrel設定Request Body Size</title>
		<link>https://tech.uccu.website/asp-net-core-kestrel-set-max-request-body-size.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=asp-net-core-kestrel-set-max-request-body-size</link>
					<comments>https://tech.uccu.website/asp-net-core-kestrel-set-max-request-body-size.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Thu, 17 Feb 2022 08:58:53 +0000</pubDate>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[程式設計]]></category>
		<category><![CDATA[asp.net core]]></category>
		<category><![CDATA[Kestrel]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=1673</guid>

					<description><![CDATA[<p>前兩天同事利用Web Api Post上傳檔案碰到了Request body too large的問題，我想到 ... <a title="Asp.Net Core Kestrel設定Request Body Size" class="read-more" href="https://tech.uccu.website/asp-net-core-kestrel-set-max-request-body-size.html" aria-label="Read more about Asp.Net Core Kestrel設定Request Body Size">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/asp-net-core-kestrel-set-max-request-body-size.html">Asp.Net Core Kestrel設定Request Body Size</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>前兩天同事利用Web Api Post上傳檔案碰到了Request body too large的問題，我想到過去的經驗就是因為預設值比較小，所以改一下設定應該就可以Work了。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>Request body too large</p><cite>Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Request body too large.<br>at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)<br>at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1ContentLengthMessageBody.OnReadStarting()<br>at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.TryStart()<br>at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.ConsumeAsync()<br>at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication<code>1 application) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequestsAsync[TContext](IHttpApplication</code>1 application)</cite></blockquote>



<p>通常不會把每一個設定值都記在腦中，所以就搜尋了一下Kestrel Server的設定應該在appsettings.json檔案中如何調整，查到了<a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/options?view=aspnetcore-6.0" target="_blank" rel="noreferrer noopener">設定MaxRequestBodySize的C#程式碼</a>，但是翻了很多篇網路上的文章都沒有找到如何在設定檔裡面調整的方法…</p>



<span id="more-1673"></span>



<p>從官方文件和一些文章中看到下面的json設定內容，所以推斷應該同樣在Limits底下加上MaxRequestBodySize和設定值就可以搞定，結果實際上試了之後發現竟然完全沒效…，<a href="https://github.com/aspnet/Announcements/issues/267" target="_blank" rel="noreferrer noopener">雖然是有看到有些設定在Server啟動之後會變成唯讀的情況</a>，之後修改的內容不會更新，不過我一直覺得加在appsettings.json裡面不就是在啟動的時候會載入的設定？</p>



<pre class="wp-block-prismatic-blocks"><code class="language-">{
  &quot;Kestrel&quot;: {
    &quot;Limits&quot;: {
      &quot;MaxConcurrentConnections&quot;: 100,
      &quot;MaxConcurrentUpgradedConnections&quot;: 100
    },
    &quot;DisableStringReuse&quot;: true
  }
}</code></pre>



<p>從這裡的文件內容則是看到下面這麼一段話…，因為Limits的設定是在KestrelServerOptions底下，所以我一直認為應該是可以透過appsettings.json設定，無奈試了幾次之後都沒辦法正常，所以最後只好妥協認命的去改Code&#8230;</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p></p><cite>Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions and endpoint configuration are configurable from configuration providers. Set other Kestrel configuration in C# code.</cite></blockquote>



<p>其實程式碼也不難，作法是在KestrelServerOptions的Limit底下的MaxRequestBodySize設定大小，因為是nullable型態，所以也可以設定為null。</p>



<p>第一段的是官方文件中的程式碼，第二段則是我寫在ConsoleApp(Program.cs)裡的程式碼，在UseKestrel的Extension method中設定。<br>因為是跑在Container裡面的程式，在改Code之前已經在shell script中判斷是否有MAX_REQ_BODY_SIZE這個環境變數來調整appsettings.json的檔案內容，所以是透過判斷是否有MAX_REQ_BODY_SIZE這個環境變數來設定大小，如果沒有找到MAX_REQ_BODY_SIZE就設定100MB的預設值，比官方預設不到30MB的大小再大一些。</p>



<pre class="wp-block-prismatic-blocks"><code class="language-csharp">builder.WebHost.ConfigureKestrel(serverOptions =&gt;
{
    serverOptions.Limits.MaxRequestBodySize = 100_000_000;
});</code></pre>



<pre class="wp-block-prismatic-blocks"><code class="language-csharp">public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
               .ConfigureWebHostDefaults(webBuilder =&gt;
                {
                    webBuilder.UseKestrel(opt =&gt;
                        {
                            long maxReqBodySize = 100 * 1024 * 1024;
                            if (Environment.GetEnvironmentVariable(&quot;MAX_REQ_BODY_SIZE&quot;) != null)
                            {
                                long.TryParse(Environment.GetEnvironmentVariable(&quot;MAX_REQ_BODY_SIZE&quot;), out maxReqBodySize);
                            }
                            opt.Limits.MaxRequestBodySize = maxReqBodySize;
                        })
                        .UseStartup&lt;Startup&gt;();
                });
}</code></pre>



<p>結果最後繞一圈多花了一天的時間還是乖乖的改了程式碼…</p>



<p>對了，上面這樣的作法是整個網站都設定相同大小的限制，或許會有一些其它額外的潛在風險，所以官方建議的作法是只在需要調整的Function加上<a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/options?view=aspnetcore-6.0#maximum-request-body-size" target="_blank" rel="noreferrer noopener">RequestSizeLimit Attribute</a>。</p>



<p>參考資訊：</p>



<ul class="wp-block-list"><li><a href="https://thecodeblogger.com/2021/05/07/certificates-and-limits-for-asp-net-core-kestrel-web-server/">The Code Blogger &#8211; Certificates and Limits for ASP .NET Core Kestrel Web Server</a></li><li><a href="https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/fundamentals/servers/kestrel/options.md">AspNetCore.Docs/options.md at main · dotnet/AspNetCore.Docs · GitHub</a></li><li><a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-6.0#unexpected-not-found-error-with-iis">Upload files in ASP.NET Core | Microsoft Docs</a></li><li><a href="https://www.talkingdotnet.com/how-to-increase-file-upload-size-asp-net-core/">How to increase file upload size in ASP.NET Core (talkingdotnet.com)</a></li><li><a href="https://www.aspsnippets.com/Articles/Set-MaxRequestLength-in-ASPNet-Core.aspx">Set MaxRequestLength in ASP.Net Core (aspsnippets.com)</a></li><li><a href="https://khalidabuhakmeh.com/increase-file-upload-limit-for-aspdotnet">Increase ASP.NET File Upload Limits | Khalid Abuhakmeh</a></li><li><a href="https://github.com/aspnet/Announcements/issues/267">New default 30 MB (~28.6 MiB) max request body size limit · Issue #267 · aspnet/Announcements · GitHub</a></li><li><a href="https://stackoverflow.com/questions/38698350/increase-upload-file-size-in-asp-net-core">c# &#8211; Increase upload file size in Asp.Net core &#8211; Stack Overflow</a></li><li><a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/options?view=aspnetcore-6.0">Configure options for the ASP.NET Core Kestrel web server | Microsoft Docs</a></li><li><a href="https://stackoverflow.com/questions/46738364/increase-upload-request-length-limit-in-kestrel/46738667">asp.net core &#8211; Increase upload request length limit in Kestrel &#8211; Stack Overflow</a></li></ul>
<p>這篇文章 <a href="https://tech.uccu.website/asp-net-core-kestrel-set-max-request-body-size.html">Asp.Net Core Kestrel設定Request Body Size</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/asp-net-core-kestrel-set-max-request-body-size.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1673</post-id>	</item>
		<item>
		<title>Asp.Net Core Kestrel Server關閉Request Info記錄</title>
		<link>https://tech.uccu.website/trun-off-kestrel-web-request-info.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=trun-off-kestrel-web-request-info</link>
					<comments>https://tech.uccu.website/trun-off-kestrel-web-request-info.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Fri, 28 Jan 2022 02:45:21 +0000</pubDate>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[Kestrel]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=1642</guid>

					<description><![CDATA[<p>今天同事詢問是否有辦法停用Kestrel的http trace/track，我第一個想到的是執行時會顯示的Re ... <a title="Asp.Net Core Kestrel Server關閉Request Info記錄" class="read-more" href="https://tech.uccu.website/trun-off-kestrel-web-request-info.html" aria-label="Read more about Asp.Net Core Kestrel Server關閉Request Info記錄">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/trun-off-kestrel-web-request-info.html">Asp.Net Core Kestrel Server關閉Request Info記錄</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>今天同事詢問是否有辦法停用Kestrel的http trace/track，我第一個想到的是執行時會顯示的Request Info記錄(如下圖)，一直在執行的時候沒特別設定時，會在Web Request進來的時候記錄相關資訊，就會有一堆的訊息一直往上跑…</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-recalc-dims="1" decoding="async" width="677" height="50" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/01/d39c6caf-image.png?resize=677%2C50&#038;ssl=1" alt="" class="wp-image-1645" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/01/d39c6caf-image.png?w=677&amp;ssl=1 677w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2022/01/d39c6caf-image.png?resize=300%2C22&amp;ssl=1 300w" sizes="(max-width: 677px) 100vw, 677px" /><figcaption>Kestrel Web Request Info</figcaption></figure></div>


<p>如果要關閉上面這些Request Info記錄的顯示，在appsettings.json檔案中加上Logging的記錄就可以了，參考下面的這段設定內容：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-json">&quot;Logging&quot;: {
    &quot;LogLevel&quot;: {
      &quot;Default&quot;: &quot;Error&quot;,
      &quot;Microsoft.Hosting&quot;: &quot;Warning&quot;
    }
  }</code></pre>



<p>加到appsettings.json檔案之後重新啟動程式再試試，頁面正常顯示卻不會有這些Request Info洗版了。</p>



<p>參考資訊：</p>



<p><a href="https://docs.microsoft.com/zh-tw/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0#configure-logging">https://docs.microsoft.com/zh-tw/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0#configure-logging</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/trun-off-kestrel-web-request-info.html">Asp.Net Core Kestrel Server關閉Request Info記錄</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/trun-off-kestrel-web-request-info.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1642</post-id>	</item>
		<item>
		<title>【2021鐵人賽】CI/CD從這裡：開始之前的準備(範例介紹)</title>
		<link>https://tech.uccu.website/2021ironman-day6-pipeline-samplecodes.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2021ironman-day6-pipeline-samplecodes</link>
					<comments>https://tech.uccu.website/2021ironman-day6-pipeline-samplecodes.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Sat, 18 Sep 2021 14:06:25 +0000</pubDate>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[2021鐵人賽]]></category>
		<category><![CDATA[iThome鐵人賽]]></category>
		<category><![CDATA[2021ironman]]></category>
		<category><![CDATA[azure devops]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=566</guid>

					<description><![CDATA[<p>前面在介紹Git Repo的時候有上傳了幾個C#的Project，裡面只有幾行簡單的程式，是為了接下來的CI/ ... <a title="【2021鐵人賽】CI/CD從這裡：開始之前的準備(範例介紹)" class="read-more" href="https://tech.uccu.website/2021ironman-day6-pipeline-samplecodes.html" aria-label="Read more about 【2021鐵人賽】CI/CD從這裡：開始之前的準備(範例介紹)">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/2021ironman-day6-pipeline-samplecodes.html">【2021鐵人賽】CI/CD從這裡：開始之前的準備(範例介紹)</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>前面在介紹Git Repo的時候有上傳了幾個C#的Project，裡面只有幾行簡單的程式，是為了接下來的CI/CD Pipeline文章而準備，不過當時只有上傳到Git Repo，並沒有介紹說明它們，這邊就用簡短的篇幅來了解一下到底有哪些內容吧！</p>



<p>首先，有個2021ironman的Solution方案檔，裡面有3個C#專案，分別是ConsoleApp、ModuleA、ModuleBase：</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img data-recalc-dims="1" fetchpriority="high" decoding="async" width="433" height="309" src="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2021/09/091276fa-2021ironmanslnandprojects.png?resize=433%2C309&#038;ssl=1" alt="" class="wp-image-572" srcset="https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2021/09/091276fa-2021ironmanslnandprojects.png?w=433&amp;ssl=1 433w, https://i0.wp.com/storage.googleapis.com/stateless-tech-uccu-website/2021/09/091276fa-2021ironmanslnandprojects.png?resize=300%2C214&amp;ssl=1 300w" sizes="(max-width: 433px) 100vw, 433px" /></figure></div>



<p>ModuleBase中只有一個IModuleInfo.cs，程式碼如下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-csharp">using System;
using System.Collections.Generic;
using System.Text;

namespace ModuleBase
{
    public interface IModuleInfo
    {
        public string Name { get; }
        public string Description { get; }

        void EntryPoint();
    }
}</code></pre>



<p>ModuleA裡面只有一個Startup.cs，程式碼如下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-csharp">using System;

namespace ModuleA
{
    public class Startup : ModuleBase.IModuleInfo
    {
        public string Name =&gt; &quot;ModuleA&quot;;
        public string Description =&gt; &quot;ModuleA is a test module.&quot;;

        public void EntryPoint()
        {
            Console.WriteLine(&quot;Hello world, this is ModuleA.&quot;);
        }
    }
}</code></pre>



<p>接著是ConsoleApp的Program.cs，程式碼如下：</p>



<pre class="wp-block-prismatic-blocks"><code class="language-csharp">using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.Loader;
using ModuleBase;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            DirectoryInfo modulesDir = new DirectoryInfo(&quot;modules&quot;);
            Console.WriteLine(&quot;Hello World! This is Console app.&quot;);

            if (modulesDir.Exists)
            {
                var dlls = modulesDir.GetFiles(&quot;*.dll&quot;, SearchOption.AllDirectories);
                List&lt;Assembly&gt; assemblies = new List&lt;Assembly&gt;();

                foreach (var dll in dlls)
                {
                    var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(dll.FullName);

                    foreach (var type in assembly.GetTypes())
                    {
                        if (typeof(IModuleInfo).IsAssignableFrom(type) &amp;&amp; !type.IsInterface)
                        {
                            IModuleInfo moduleInfo = Activator.CreateInstance(type) as IModuleInfo;
                            Console.WriteLine($&quot;Load {moduleInfo.Name}, {moduleInfo.Description}.&quot;);
                            moduleInfo.EntryPoint();
                        }
                    }
                }
            }

            Console.ReadLine();
        }
    }
}</code></pre>



<p>ModuleBase這個Class Library作為定義Module的基礎，雖然目前只有ModuleA，但是之後的ModuleB、ModuleC都要以ModuleBase為基底，所以ModuleBase會在後續用來製作成nuget package，在Azure DevOps中建立私有的nuget套件庫。</p>



<p>ModuleA實作ModuleBase中所定義的介面，如上面所說，之後會有ModuleB、ModuleC，如果每一個Module都替它們建立一個專屬的Pipeline，那數量會很可觀，所以之後建立的Pipeline中，會有一個是設計成可以讓使用者來告訴Pipeline應該要選擇哪一個來建置(Build)。</p>



<p>ConsoleApp的部份則是扮演主程式的角色，這裡也只是把Module的dll載入之後執行輸出結果而已，暫時沒有特別的用途。</p>



<p>初期就先以上面這些東西來準備接下來的Pipeline範例，下一篇我們就要實際來建立Pipeline了。</p>
<p>這篇文章 <a href="https://tech.uccu.website/2021ironman-day6-pipeline-samplecodes.html">【2021鐵人賽】CI/CD從這裡：開始之前的準備(範例介紹)</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/2021ironman-day6-pipeline-samplecodes.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">566</post-id>	</item>
		<item>
		<title>單元測試與資料表自動遞增Id的問題</title>
		<link>https://tech.uccu.website/unittest-with-dbcc-checkident-reseed.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=unittest-with-dbcc-checkident-reseed</link>
					<comments>https://tech.uccu.website/unittest-with-dbcc-checkident-reseed.html#respond</comments>
		
		<dc:creator><![CDATA[鳴人]]></dc:creator>
		<pubDate>Tue, 19 May 2020 06:38:16 +0000</pubDate>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[程式設計]]></category>
		<category><![CDATA[mssql]]></category>
		<category><![CDATA[unittest]]></category>
		<guid isPermaLink="false">https://tech.uccu.website/?p=28</guid>

					<description><![CDATA[<p>今天同事問了一個關於資料庫建立資料表重設Id遞增值的問題，主要是在單元測試中，使用的資料庫並不是每次都重新建立 ... <a title="單元測試與資料表自動遞增Id的問題" class="read-more" href="https://tech.uccu.website/unittest-with-dbcc-checkident-reseed.html" aria-label="Read more about 單元測試與資料表自動遞增Id的問題">閱讀全文</a></p>
<p>這篇文章 <a href="https://tech.uccu.website/unittest-with-dbcc-checkident-reseed.html">單元測試與資料表自動遞增Id的問題</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>今天同事問了一個關於資料庫建立資料表重設Id遞增值的問題，主要是在單元測試中，使用的資料庫並不是每次都重新建立，而是在執行單元測試之前，將資料表中的資料刪除，並且將Id欄位的初始值重設。<br>為了達到上面的需求，所以在一開始的時候會執行刪除資料和<a href="https://docs.microsoft.com/zh-tw/sql/t-sql/database-console-commands/dbcc-checkident-transact-sql?view=sql-server-ver15">DBCC CHECKIDENT (&#8216;TableName&#8217;, RESEED, 0)</a>將資料表的遞增種子重設為0，這樣在新增資料的時候就會+1變成從1開始(一般正常的情況)。</p>



<p>不過問題是，如果資料庫和資料表是第一次剛建立的，使用這個指令會讓Id欄位一開始的編號是0，期望的初始編號是1，但是在之後刪除資料並且使用上面的指令後，再新增資料的初始編號就會從1開始，符合期望的數字。</p>



<span id="more-28"></span>



<p>這個問題我試了一下，發現如果是資料庫與資料表剛建立的時候，若是執行<a href="https://docs.microsoft.com/zh-tw/sql/t-sql/database-console-commands/dbcc-checkident-transact-sql?view=sql-server-ver15">DBCC CHECKIDENT (&#8216;TableName&#8217;, NORESEED</a>)會取得NULL，但是新增資料的時候Id會是1(沒有執行ReSeed的情況下)，這部份感覺上應該是資料庫有自行處理這一個部份，所以若是在一開始執行ReSeed，反而是讓資料庫不處理NULL到1的部份，新增資料時的Id就變成是0。</p>



<p>原本建議同事在執行完單元測試的時候刪除資料時再做ReSeed的動作，不過他說有時候想要在新增資料後看看資料庫的內容，所以不想要立即刪除資料，因此詢問如何在執行單元測試之前判斷資料庫或資料表是否剛建立？</p>



<p>針對這個問題，我回到如何得知資料庫內的種子遞增值的方向來思考，如何在執行ReSeed之前取得目前的值，判斷是否為NULL再來針對接下來執行相對應的動作。</p>



<p>簡單查了一下，可以利用<a href="https://docs.microsoft.com/zh-tw/sql/t-sql/functions/ident-current-transact-sql?view=sql-server-ver15">IDENT_CURRENT( &#8216;table_or_view&#8217; )</a>這個語法來得知目前資料表的遞增值，透過Select IDENT_CURRENT( &#8216;table_or_view&#8217; ) As Seed來取得，後面的As Seed部份是將取得的值放在名為Seed名稱的欄位，沒有這一段也沒關係，只是沒有欄位名稱。</p>



<p>從微軟的文件庫中還有看到IDENT_SEED ( &#8216;table_or_view&#8217; )這個指令，是用來查詢最初設定欄位的遞增值，因為有時候可能會設定自動遞增的欄位不是從1開始，這時候就可以利用這個函數來查詢初始的設定，回傳的結果並不會受到DBCC CHECKIDENT重設的影響，也許以後會用到。</p>



<h3 class="wp-block-heading">2020/05/27 Update:</h3>



<p>後來同事告知使用Select IDENT_CURRENT( &#8216;table_or_view&#8217; ) As Seed的方式仍然會有問題，也就是說當資料表剛被建立，尚未新增資料的時候，IDENT_CURRENT函數取得的是1，新增多筆資料刪除之後再ReSeed設0，IDENT_CURRENT函數取得也會是1。</p>



<p>因為當資料表剛被建立尚未新增資料前，若是使用DBCC CHECKIDENT來ReSeed設0，第一筆資料新增的時候會直接使用ReSeed時給定的初始值(也就是0)。但是若資料表有新增過資料再刪除，重新ReSeed為0的時候，作法變成是給定的初始值+1，從上面的例子ReSeed為0的情況再+1就變成1，所以兩個情況都會取得1。</p>



<p>至於上次為何我會取到NULL值，應該是我當時在測試的時候沒有留意，執行IDENT_CURRENT函數的時候是在資料表尚未建立的時候(不存在的TableName)，所以取得的結果是NULL。</p>



<p>印象中上次在查詢資料的時候有看到另外的解法，就是去撈取系統資料表中的記錄，只是語法麻煩了一點，這次再把它找出來試了一下，並且留意資料表建立前後的問題，結果是可行的，語法如下：</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>IF EXISTS (SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = &#8216;<span class="has-inline-color has-vivid-green-cyan-color">TableName</span>&#8216; AND last_value IS NOT NULL)<br>DBCC CheckIdent(&#8216;<span class="has-inline-color has-vivid-green-cyan-color">TableName</span>&#8216;, ReSeed, 0);<br>Else<br>Print(&#8216;no need reseed&#8217;)</p></blockquote>



<h3 class="wp-block-heading"><strong>參考來源：</strong></h3>



<ul class="wp-block-list"><li><a href="https://docs.microsoft.com/zh-tw/sql/t-sql/functions/ident-current-transact-sql?view=sql-server-ver15">IDENT_CURRENT (Transact-SQL)</a></li><li><a href="https://stackoverflow.com/questions/472578/dbcc-checkident-sets-identity-to-0">DBCC CHECKIDENT Sets Identity to 0</a></li></ul>
<p>這篇文章 <a href="https://tech.uccu.website/unittest-with-dbcc-checkident-reseed.html">單元測試與資料表自動遞增Id的問題</a> 最早出現於 <a href="https://tech.uccu.website">泰克哪裡去</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://tech.uccu.website/unittest-with-dbcc-checkident-reseed.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">28</post-id>	</item>
	</channel>
</rss>
