اشتراک‌ها
ابزار Autorest : کد جنریتوری برای OpenAPI Specification

OpenAPI (f.k.a Swagger) Specification code generator. Supports C#, Go, Java, Node.js, TypeScript, Python, Ruby and PHP. 

AutoRest is an open source tool that generates client libraries for accessing RESTful web services. 

Video Tutorial : https://azure.microsoft.com/en-us/resources/videos/inside-autorest-with-david-justice/ 

ابزار Autorest : کد جنریتوری برای OpenAPI Specification
مطالب
غنی سازی کامپایلر C# 9.0 با افزونه‌ها
از زمانیکه کامپایلر #C، تحت عنوان Roslyn بازنویسی شد، قابلیت افزونه‌پذیری نیز پیدا کرد. برای مثال می‌توان آنالیز کننده‌ای را طراحی کرد که در پروسه‌ی کامپایل متداول کدهای  #C مورد استفاده قرار گرفته و خطاها و یا اخطارهایی را صادر کند که جزئی از پیام‌های استاندارد کامپایلر #C نیستند. در این مطلب نحوه‌ی معرفی آن‌ها را به پروژه‌های جدید NET 5.0.، بررسی می‌کنیم.


معرفی تعدادی آنالیز کننده‌ی کد که به عنوان افزونه‌ی کامپایلر #C قابل استفاده هستند

Microsoft.CodeAnalysis.NetAnalyzers
این افزونه جزئی از SDK دات نت 5 است و نیازی به نصب مجزا را ندارد. البته اگر می‌خواهید نگارش‌های جدیدتر آن‌را پیش از یکی شدن با SDKهای بعدی مورد آزمایش قرار دهید، می‌توان آن‌را به صورت صریحی نیز به کامپایلر معرفی کرد. این افزونه‌ی جایگزین FxCop است و پس از ارائه‌ی آن، FxCop را منسوخ شده اعلام کردند.

Meziantou.Analyzer
یکسری نکات بهبود کیفیت کدها که توسط Meziantou در طی سال‌های متمادی جمع آوری شده‌اند، تبدیل به افزونه‌ی فوق شده‌اند.

Microsoft.VisualStudio.Threading.Analyzers
این افزونه نکاتی را در مورد مشکلات Threading موجود در کدها، گوشزد می‌کند.

Microsoft.CodeAnalysis.BannedApiAnalyzers
با استفاده از این افزونه می‌توان استفاده‌ی از یکسری کدها را ممنوع کرد. برای مثال استفاده‌ی از System.DateTimeOffset.DateTime، در سراسر کدها ممنوع شده و استفاده‌ی از System.DateTimeOffset.UtcDateTime پیشنهاد شود.

AsyncFixer و Asyncify
این دو افزونه، مشکلات متداول در حین کار با کدهای async را گوشزد می‌کنند.

ClrHeapAllocationAnalyzer
این افزونه مکان‌هایی از کد را مشخص می‌کنند که در آن‌ها تخصیص حافظه صورت گرفته‌است. کاهش این مکان‌ها می‌تواند به بالا رفتن کارآیی برنامه کمک کنند.

SonarAnalyzer.CSharp
مجموعه‌ی معروف Sonar، که تعداد قابل ملاحظه‌ای بررسی کننده‌ی کد را به پروژه‌ی شما اضافه می‌کنند.


روش معرفی سراسری افزونه‌های فوق به تمام پروژه‌های یک Solution

می‌توان تنظیمات زیر را به یک تک پروژه اعمال کرد که برای اینکار نیاز است فایل csproj آن‌را ویرایش نمود و یا می‌توان یک تک فایل ویژه را به نام Directory.Build.props ایجاد کرد و آن‌را به صورت زیر تکمیل نمود. محل قرارگیری این فایل، در ریشه‌ی Solution و در کنار فایل sln می‌باشد.
<Project>
  <PropertyGroup>
    <AnalysisLevel>latest</AnalysisLevel>
    <AnalysisMode>AllEnabledByDefault</AnalysisMode>
    <CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
    <EnableNETAnalyzers>true</EnableNETAnalyzers>
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
    <Nullable>enable</Nullable>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
    <RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
    <!--
      CA2007: Consider calling ConfigureAwait on the awaited task
      MA0004: Use Task.ConfigureAwait(false) as the current SynchronizationContext is not needed
      CA1056: Change the type of property 'Url' from 'string' to 'System.Uri'
      CA1054: Change the type of parameter of the method to allow a Uri to be passed as a 'System.Uri' object
      CA1055: Change the return type of method from 'string' to 'System.Uri'
    -->
    <NoWarn>$(NoWarn);CA2007;CA1056;CA1054;CA1055;MA0004</NoWarn>
    <NoError>$(NoError);CA2007;CA1056;CA1054;CA1055;MA0004</NoError>
    <Deterministic>true</Deterministic>
    <Features>strict</Features>
    <ReportAnalyzer>true</ReportAnalyzer>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

    <PackageReference Include="Meziantou.Analyzer" Version="1.0.639">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

    <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="16.8.55">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

    <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.2">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

    <PackageReference Include="AsyncFixer" Version="1.3.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

    <PackageReference Include="Asyncify" Version="0.9.7">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

    <PackageReference Include="ClrHeapAllocationAnalyzer" Version="3.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

    <PackageReference Include="SonarAnalyzer.CSharp" Version="8.16.0.25740">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

  <ItemGroup>
    <AdditionalFiles Include="$(MSBuildThisFileDirectory)BannedSymbols.txt" Link="Properties/BannedSymbols.txt" />
  </ItemGroup>
</Project>
توضیحات:
- در تنظیمات فوق، مواردی مانند AnalysisLevel، در مطلب «کامپایلر C# 9.0، خطاها و اخطارهای بیشتری را نمایش می‌دهد» پیشتر بررسی شده‌اند.
- در اینجا Nullable به true تنظیم شده‌است. اگر قرار است یک پروژه‌ی جدید را شروع کنید، بهتر است این ویژگی را نیز فعال کنید. بسیاری از API‌های دات نت 5 جهت مشخص سازی خروجی نال و یا غیرنال آن‌ها، بازنویسی و تکمیل شده‌اند و بدون استفاده از این ویژگی، بسیاری از راهنمایی‌های ارزنده‌ی دات نت 5 را از دست خواهید داد. اساسا بدون فعالسازی این ویژگی، از قابلیت‌های #C مدرن استفاده نمی‌کنید.
- وجود این PackageReference ها، به معنای بالا رفتن حجم نهایی قابل ارائه‌ی پروژه نیست؛ چون به صورت PrivateAssets و analyzers تعریف شده‌اند و فقط در حین پروسه‌ی کامپایل، جهت ارائه‌ی راهنمایی‌های بیشتر، تاثیرگذار خواهند بود.
- این تنظیمات طوری چیده شده‌اند که تا حد ممکن «درد آور» باشند! برای اینکار CodeAnalysisTreatWarningsAsErrors و TreatWarningsAsErrors به true تظیم شده‌اند تا حتی اخطارها نیز به صورت خطای کامپایلر گزارش شوند؛ تا مجبور به رفع آن‌ها شویم.
- در اینجا فایل BannedSymbols.txt را نیز مشاهده می‌کنید که مرتبط است به BannedApiAnalyzers. می‌توان در کنار فایل Directory.Build.props، فایل جدید BannedSymbols.txt را با این محتوا ایجاد کرد:
# https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md
P:System.DateTime.Now;Use System.DateTime.UtcNow instead
P:System.DateTimeOffset.Now;Use System.DateTimeOffset.UtcNow instead
P:System.DateTimeOffset.DateTime;Use System.DateTimeOffset.UtcDateTime instead
در این حالت برای مثال، از استفاده‌ی از DateTime.Now منع شده و وادار به استفاده‌ی از DateTime.UtcNow می‌شوید.


روش کاهش تعداد خطاهای نمایش داده شده

اگر از فایل Directory.Build.props فوق استفاده کرده و یکبار دستور dotnet restore را جهت بازیابی وابستگی‌های آن اجرا کنید، با تعداد خطاهایی که در IDE خود مشاهده خواهید کرد، شگفت‌زده خواهید شد! به همین جهت برای کنترل آن‌ها می‌توان فایل جدید editorconfig. را به نحو زیر در کنار فایل Directory.Build.props ایجاد و تکمیل کرد:
[*.cs]

# MA0026 : Complete the task
dotnet_diagnostic.MA0026.severity = suggestion

# CA1308: In method 'urlToLower', replace the call to 'ToLowerInvariant' with 'ToUpperInvariant' (CA1308)
dotnet_diagnostic.CA1308.severity = suggestion

# CA1040: Avoid empty interfaces
dotnet_diagnostic.CA1040.severity = suggestion

# CA1829 Use the "Count" property instead of Enumerable.Count()
dotnet_diagnostic.CA1829.severity = suggestion

# Use 'Count' property here instead.
dotnet_diagnostic.S2971.severity = suggestion

# S1135 : Complete the task
dotnet_diagnostic.S1135.severity = suggestion

# S2479: Replace the control character at position 7 by its escape sequence
dotnet_diagnostic.S2479.severity = suggestion

# CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = none

# MA0004: Use Task.ConfigureAwait(false) as the current SynchronizationContext is not needed
dotnet_diagnostic.MA0004.severity = none

# CA1056: Change the type of property 'Url' from 'string' to 'System.Uri'
dotnet_diagnostic.CA1056.severity = suggestion

# CA1054: Change the type of parameter of the method to allow a Uri to be passed as a 'System.Uri' object
dotnet_diagnostic.CA1054.severity = suggestion

# CA1055: Change the return type of method from 'string' to 'System.Uri'
dotnet_diagnostic.CA1055.severity = suggestion

# S4457: Split this method into two, one handling parameters check and the other handling the asynchronous code.
dotnet_diagnostic.S4457.severity = none

# AsyncFixer01: Unnecessary async/await usage
dotnet_diagnostic.AsyncFixer01.severity = suggestion

# AsyncFixer02: Long-running or blocking operations inside an async method
dotnet_diagnostic.AsyncFixer02.severity = error

# VSTHRD103: Call async methods when in an async method
dotnet_diagnostic.VSTHRD103.severity = error

# AsyncFixer03: Fire & forget async void methods
dotnet_diagnostic.AsyncFixer03.severity = error

# VSTHRD100: Avoid async void methods
dotnet_diagnostic.VSTHRD100.severity = error

# VSTHRD101: Avoid unsupported async delegates
dotnet_diagnostic.VSTHRD101.severity = error

# VSTHRD107: Await Task within using expression
dotnet_diagnostic.VSTHRD107.severity = error

# AsyncFixer04: Fire & forget async call inside a using block
dotnet_diagnostic.AsyncFixer04.severity = error

# VSTHRD110: Observe result of async calls
dotnet_diagnostic.VSTHRD110.severity = error

# VSTHRD002: Avoid problematic synchronous waits
dotnet_diagnostic.VSTHRD002.severity = suggestion

# MA0045: Do not use blocking call (make method async)
dotnet_diagnostic.MA0045.severity = suggestion

# AsyncifyInvocation: Use Task Async
dotnet_diagnostic.AsyncifyInvocation.severity = error

# AsyncifyVariable: Use Task Async
dotnet_diagnostic.AsyncifyVariable.severity = error

# VSTHRD111: Use ConfigureAwait(bool)
dotnet_diagnostic.VSTHRD111.severity = none

# MA0022: Return Task.FromResult instead of returning null
dotnet_diagnostic.MA0022.severity = error

# VSTHRD114: Avoid returning a null Task
dotnet_diagnostic.VSTHRD114.severity = error

# VSTHRD200: Use "Async" suffix for async methods
dotnet_diagnostic.VSTHRD200.severity = suggestion

# MA0040: Specify a cancellation token
dotnet_diagnostic.MA0032.severity = suggestion

# MA0040: Flow the cancellation token when available
dotnet_diagnostic.MA0040.severity = suggestion

# MA0079: Use a cancellation token using .WithCancellation()
dotnet_diagnostic.MA0079.severity = suggestion

# MA0080: Use a cancellation token using .WithCancellation()
dotnet_diagnostic.MA0080.severity = error

#AsyncFixer05: Downcasting from a nested task to an outer task.
dotnet_diagnostic.AsyncFixer05.severity = error

# ClrHeapAllocationAnalyzer ----------------------------------------------------
# HAA0301: Closure Allocation Source
dotnet_diagnostic.HAA0301.severity = suggestion

# HAA0601: Value type to reference type conversion causing boxing allocation
dotnet_diagnostic.HAA0601.severity = suggestion

# HAA0302: Display class allocation to capture closure
dotnet_diagnostic.HAA0302.severity = suggestion

# HAA0101: Array allocation for params parameter
dotnet_diagnostic.HAA0101.severity = suggestion

# HAA0603: Delegate allocation from a method group
dotnet_diagnostic.HAA0603.severity = suggestion

# HAA0602: Delegate on struct instance caused a boxing allocation
dotnet_diagnostic.HAA0602.severity = suggestion

# HAA0401: Possible allocation of reference type enumerator
dotnet_diagnostic.HAA0401.severity = silent

# HAA0303: Lambda or anonymous method in a generic method allocates a delegate instance
dotnet_diagnostic.HAA0303.severity = silent

# HAA0102: Non-overridden virtual method call on value type
dotnet_diagnostic.HAA0102.severity = silent

# HAA0502: Explicit new reference type allocation
dotnet_diagnostic.HAA0502.severity = none

# HAA0505: Initializer reference type allocation
dotnet_diagnostic.HAA0505.severity = silent
روش کار هم به صورت است که برای مثال در IDE خود (حتی با VSCode هم کار می‌کند)، خطای کامپایلر مثلا CA1308 را مشاهده می‌کنید که عنوان کرده‌است بجای ToLowerInvariant از ToUpperInvariant استفاده کنید. اگر با این پیشنهاد موافق نیستید (عین خطا را به صورت C# CA1308 در گوگل جستجو کنید؛ توضیحات مایکروسافت را در مورد آن خواهید یافت)، یک سطر شروع شده‌ی با dotnet_diagnostic و سپس ID خطا را به صورت زیر، به فایل editorconfig. یاد شده، اضافه کنید:
dotnet_diagnostic.CA1308.severity = suggestion
به این ترتیب هنوز هم این مورد را به صورت یک پیشنهاد مشاهده خواهید کرد، اما دیگر جزو خطاهای کامپایلر گزارش نمی‌شود. اگر خواستید که به طور کامل ندید گرفته شود، مقدار آن‌را بجای suggestion به none تغییر دهید.

یک نکته: در ویندوز نمی‌توانید یک فایل تنها پسوند دار را به صورت معمولی در windows explorer ایجاد کنید. نام این فایل را به صورت .editorconfig. با دو نقطه‌ی ابتدایی و انتهایی وارد کنید. خود ویندوز نقطه‌ی پایانی را حذف می‌کند.


روش صرفنظر کردن از یک خطا، تنها در یک قسمت از کد

فرض کنید نمی‌خواهید خطای CA1052 را تبدیل به یک suggestion سراسری کنید و فقط می‌خواهید که در قطعه‌ی خاصی از کدهای خود، آن‌را خاموش کنید. به همین جهت بجای اضافه کردن آن به فایل editorconfig.، باید از ویژگی SuppressMessage به صورت زیر استفاده نمائید:
[SuppressMessage("Microsoft.Usage", "CA1052:Type 'Program' is a static holder type but is neither static nor NotInheritable",
  Justification = "We need it for our integration tests this way.")]
[SuppressMessage("Microsoft.Usage", "RCS1102:Type 'Program' is a static holder type but is neither static nor NotInheritable",
  Justification = "We need it for our integration tests this way.")]
[SuppressMessage("Microsoft.Usage", "S1118:Type 'Program' is a static holder type but is neither static nor NotInheritable",
  Justification = "We need it for our integration tests this way.")]
public class Program { }
در اینجا پارامتر اول با Microsoft.Usage مقدار دهی می‌شود. پارامتر دوم آن باید حاوی ID خطا باشد. در صورت تمایل می‌توانید دلیل خاموش کردن این خطا را در قسمت Justification وارد کنید.
اشتراک‌ها
چگونه در dotNET Core از Performance Counters استفاده کنیم

Performance counters are really important for monitoring and troubleshooting problems with your .NET applications. The full .NET Framework provides a wide array of performance counters that are very useful for troubleshooting application problems. 

چگونه در dotNET Core از Performance Counters استفاده کنیم
اشتراک‌ها
ابزار CsharpRepl

زمان هایی نیاز داریم که قطعه کد سی شارپ را تست کنیم، معمولا برای این کار یک Console Application ایجاد میکنیم و کد خود را تست میکنیم، اما اگر با زبان هایی مانند پایتون آشنایی داشته باشید میدانید که میتوانیم بدون ایجاد فایل داخل Console کد‌ها را تست کنیم، این ابزار این امکان را در زبان سی شارپ به ما میدهد

ابزار CsharpRepl
نظرسنجی‌ها
اگر بخواهید کنار دات نت بر روی یک پلتفرم یا زبان دیگری نیز کار کنید کدام را انتخاب می کنید؟
زبان جاوا اسکریپت و پلتفرم node.js
زبان پایتون و کار بر روی فریم‌ورکهای مختلف آن
زبان جاوا
php
دیگر زبانها
Go-Lang
Ruby و پلتفرمهایش
زبان سی پلاس پلاس در محیطهای محتلف مثل visual c++ یا QT
مطالب
روش یکی کردن پروژه‌های React و ASP.NET Core
یک روش کار کردن با پروژه‌های SPA، توسعه‌ی مجزای قسمت‌های front-end و back-end است. برای مثال پروژه‌ی React را به صورت جداگانه‌ای توسعه می‌دهیم، پروژه‌ی ASP.NET Core را نیز به همین صورت. هنگام آزمایش برنامه، در یکی دستور npm start را اجرا می‌کنیم تا وب سرور آزمایشی React، آن‌را در آدرس http://localhost:3000 قابل دسترسی کند و در دیگری دستور dotnet watch run را صادر می‌کنیم تا برنامه‌ی وب ASP.NET Core را بر روی آدرس https://localhost:5001 مهیا کند. سپس برای اینکه از پورت 3000 بتوان با پورت 5001 کار کرد، نیاز خواهد بود تا CORS را در برنامه‌ی ASP.NET Core فعالسازی کنیم. در حین ارائه‌ی نهایی برنامه نیز هر کدام را به صورت مجزا publish کرده و بعد هم خروجی نهایی پروژه‌ی SPA را در پوشه‌ی wwwroot برنامه‌ی وب کپی می‌کنیم تا قابل دسترسی و استفاده شود. روش دیگری نیز برای یکی/ساده سازی این تجربه وجود دارد که در این مطلب به آن خواهیم پرداخت.


پیشنیاز: ایجاد یک برنامه‌ی خالی React و ASP.NET Core

یک پوشه‌ی خالی را ایجاد کرده و در آن دستور dotnet new react را صادر کنید، تا قالب خاص پروژه‌های React یکی سازی شده‌ی با پروژه‌های ASP.NET Core، یک پروژه‌ی جدید را ایجاد کند.


همانطور که در تصویر فوق نیز مشاهده می‌کنید، این پروژه از دو برنامه تشکیل شده‌است:
الف) برنامه‌ی SPA که در پوشه‌ی ClientApp قرار گرفته‌است و شامل کدهای کامل یک برنامه‌ی React است.
ب) برنامه‌ی سمت سرور ASP.NET Core که یک برنامه‌ی متداول وب، به همراه فایل Startup.cs و سایر فایل‌های مورد نیاز آن است.

در ادامه نکات ویژه‌ی ساختار این پروژه را بررسی خواهیم کرد.


تجربه‌ی توسعه‌ی برنامه‌ها توسط این قالب ویژه

اکنون اگر این پروژه‌ی وب را برای مثال با فشردن دکمه‌ی F5 و یا اجرای دستور dotnet run، اجرا کنیم، چه اتفاقی رخ می‌دهد؟
- به صورت خلاصه برنامه‌ی ASP.NET Core شروع به کار کرده و سبب ارائه همزمان برنامه‌ی SPA نیز خواهد شد.
- پورتی که برنامه‌ی وب بر روی آن قرار دارد، با پورتی که برنامه‌ی React بر روی روی آن ارائه می‌شود، یکی است. یعنی نیازی به تنظیمات CORS را ندارد.
- در این حالت اگر در برنامه‌ی React تغییری را ایجاد کنیم (در هر قسمتی از آن)، hot reloading آن هنوز هم برقرار است و سبب بارگذاری مجدد برنامه‌ی SPA در مرورگر خواهد شد و برای اینکار نیازی به توقف و راه اندازی مجدد برنامه‌ی ASP.NET Core نیست.

اما این تجربه‌ی روان کاربری و توسعه، چگونه حاصل شده‌است؟


بررسی ساختار فایل Startup.cs یک پروژه‌ی مبتنی بر dotnet new react

برای درک نحوه‌ی عملکرد این قالب ویژه، نیاز است از فایل Startup.cs آن شروع کرد.
// ...
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;

namespace dotnet_template_sample
{
    public class Startup
    {
        // ...

        public void ConfigureServices(IServiceCollection services)
        {

            services.AddControllersWithViews();

            // In production, the React files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/build";
            });
        }
در ابتدا تعریف فضای نام SpaServices را مشاهده می‌کنید. بسته‌ی متناظر با آن در فایل csproj برنامه به صورت زیر ثبت شده‌است:
<ItemGroup>
   <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.2" />
</ItemGroup>
این بسته، همان بسته‌ی جدید SpaServices است و در NET 5x. نیز پشتیبانی خواهد شد .

در متد ConfigureServices، ثبت سرویس‌های مرتبط با فایل‌های استاتیک پروژه‌ی SPA، توسط متد AddSpaStaticFiles صورت گرفته‌است. در اینجا RootPath آن، به پوشه‌ی ClientApp/build اشاره می‌کند. البته این پوشه هنوز در این ساختار، قابل مشاهده نیست؛ اما زمانیکه پروژه‌ی ASP.NET Core را برای ارائه‌ی نهایی، publish کردیم، به صورت خودکار ایجاد شده و حاوی فایل‌های قابل ارائه‌ی برنامه‌ی React نیز خواهد بود.

قسمت مهم دیگر کلاس آغازین برنامه، متد Configure آن است:
// ...
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;

namespace dotnet_template_sample
{
    public class Startup
    {
        // ...

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // ...
            app.UseStaticFiles();
            app.UseSpaStaticFiles();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller}/{action=Index}/{id?}");
            });

            app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";

                if (env.IsDevelopment())
                {
                    spa.UseReactDevelopmentServer(npmScript: "start");
                }
            });
        }
    }
}
در اینجا ثبت سه میان افزار جدید را مشاهده می‌کنید:
- متد UseSpaStaticFiles، سبب ثبت میان‌افزاری می‌شود که امکان دسترسی به فایل‌های استاتیک پوشه‌ی ClientApp حاوی برنامه‌ی React را میسر می‌کند؛ مسیر این پوشه را در متد ConfigureServices تنظیم کردیم.
- متد UseSpa، سبب ثبت میان‌افزاری می‌شود که دو کار مهم را انجام می‌دهد:
1- کار اصلی آن، ثبت مسیریابی معروف catch all است تا مسیریابی‌هایی را که توسط کنترلرهای برنامه‌ی ASP.NET Core مدیریت نمی‌شوند، به سمت برنامه‌ی React هدایت کند. برای مثال مسیر https://localhost:5001/api/users به یک کنترلر API برنامه‌ی سمت سرور ختم می‌شود، اما سایر مسیرها مانند https://localhost:5001/login قرار است صفحه‌ی login برنامه‌ی سمت کلاینت SPA را نمایش دهند و متناظر با اکشن متد خاصی در کنترلرهای برنامه‌ی وب ما نیستند. در این حالت، کار این مسیریابی catch all، نمایش صفحه‌ی پیش‌فرض برنامه‌ی SPA است.
2- بررسی می‌کند که آیا شرایط IsDevelopment برقرار است؟ آیا در حال توسعه‌ی برنامه هستیم؟ اگر بله، میان‌افزار دیگری را به نام UseReactDevelopmentServer، اجرا و ثبت می‌کند.

برای درک عملکرد میان‌افزار ReactDevelopmentServer نیاز است به سورس آن مراجعه کرد. این میان‌افزار بر اساس پارامتر start ای که دریافت می‌کند، سبب اجرای npm run start خواهد شد. به این ترتیب دیگر نیازی به اجرای جداگانه‌ی این دستور نخواهد بود و همچنین این اجرا، به همراه تنظیمات proxy مخصوصی نیز هست تا پورت اجرایی برنامه‌ی React و برنامه‌ی ASP.NET Core یکی شده و دیگر نیازی به تنظیمات CORS مخصوص برنامه‌های React نباشد. بنابراین hot reloading ای که از آن صحبت شد، توسط ASP.NET Core مدیریت نمی‌شود. در پشت صحنه همان npm run start اصلی برنامه‌های React، در حال اجرای وب سرور آزمایشی React است که از hot reloading پشتیبانی می‌کند.

یک مشکل: با این تنظیم، هربار که برنامه‌ی ASP.NET Core اجرا می‌شود (به علت تغییرات در کدها و فایل‌های پروژه)، سبب اجرای مجدد و پشت صحنه‌ی react development server نیز خواهد شد که ... آغاز برنامه را در حالت توسعه، کند می‌کند. برای رفع این مشکل می‌توان این وب سرور توسعه‌ی برنامه‌های React را به صورت جداگانه‌ای اجرا کرد و فقط تنظیمات پروکسی آن‌را در اینجا ذکر نمود:
// replace
spa.UseReactDevelopmentServer(npmScript: "start");
// with
spa.UseProxyToSpaDevelopmentServer("http://localhost:3000");
در اینجا فقط کافی است سطر UseReactDevelopmentServer را با تنظیم UseProxyToSpaDevelopmentServer که به آدرس وب سرور توسعه‌ی برنامه‌های React اشاره می‌کند، تنظیم کنیم. بدیهی است در اینجا حالت باید از طریق خط فرمان به پوشه‌ی clientApp وارد شد و دستور npm start را یکبار به صورت دستی اجرا کرد، تا این وب سرور، راه اندازی شود.


تغییرات ویژه‌ی فایل csproj برنامه

اگر به فایل csproj برنامه دقت کنیم، دو تغییر جدید نیز در آن قابل مشاهده هستند:
الف) نصب خودکار وابستگی‌های برنامه‌ی client
  <Target Name="DebugEnsureNodeEnv"
     BeforeTargets="Build" 
     Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
    <!-- Ensure Node.js is installed -->
    <Exec Command="node --version" ContinueOnError="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
    </Exec>
    <Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
    <Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
  </Target>
در این تنظیم، در حالت build و debug، ابتدا بررسی می‌کند که آیا پوشه‌ی node_modules برنامه‌ی SPA وجود دارد؟ اگر خیر، ابتدا مطمئن می‌شود که node.js بر روی سیستم نصب است و سپس دستور npm install را صادر می‌کند تا تمام وابستگی‌های برنامه‌ی client، دریافت و نصب شوند.

ب) یکی کردن تجربه‌ی publish برنامه‌ی ASP.NET Core با publish پروژه‌های React
  <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
    <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />

    <!-- Include the newly-built files in the publish output -->
    <ItemGroup>
      <DistFiles Include="$(SpaRoot)build\**" />
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
        <RelativePath>%(DistFiles.Identity)</RelativePath>
        <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
        <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
      </ResolvedFileToPublish>
    </ItemGroup>
  </Target>
میان‌افزار ReactDevelopmentServer کار اجرا و پروکسی دستور npm run start را در حالت توسعه انجام می‌دهد. اما در حالت ارائه‌ی نهایی چطور؟ در اینجا نیاز است دستور npm run build اجرا شده و فایل‌های مخصوص ارائه‌ی نهایی برنامه‌ی React تولید و سپس به پوشه‌ی wwwroot، کپی شوند. تنظیم فوق، دقیقا همین کار را در حین publish برنامه‌ی ASP.NET Core، به صورت خودکار انجام می‌دهد و شامل این مراحل است:
-  ابتدا npm install را جهت اطمینان از به روز بودن وابستگی‌های برنامه مجددا اجرا می‌کند.
- سپس npm run build را برای تولید فایل‌های قابل ارائه‌ی برنامه‌ی React اجرا می‌کند.
- در آخر تمام فایل‌های پوشه‌ی ClientApp/build تولیدی را به بسته‌ی نهایی توزیعی برنامه‌ی ASP.NET Core، اضافه می‌کند.
اشتراک‌ها
نحوه اجرای انتقال محتوا در ASP.NET Core 2.0

We are going to talk about:

What you get out of the box with ASP.NET Core 2.0
How to use Postman to test your API
Changing the default configuration of our project
Testing the content negotiation
Restricting media types
More about formatters
Implementing a custom formatter
Consuming API programmatically 

نحوه اجرای انتقال محتوا در ASP.NET Core 2.0
اشتراک‌ها
راهنمای جامعی از فریم ورک های جاوااسکریپتی

Keeping up with JavaScript frameworks can be a challenge. There are a lot of them, and seemingly another one every month. How do you know which ones might be right for your project? What are their strengths and weaknesses? How do you get started? 

راهنمای جامعی از فریم ورک های  جاوااسکریپتی