نظرات مطالب
Blazor 5x - قسمت 27 - برنامه‌ی Blazor WASM - کار با سرویس‌های Web API
یک نکته: تاثیر hot reload دات نت 6 بر روی درخواست‌های HTTP سمت کلاینت

ممکن است پس از ارتقاء به دات نت 6، شاهد خطاهای BadHttpRequestException زیادی در سمت Web API شوید (البته فقط در حالت توسعه):
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unexpected end of request content.
این خطا عنوان می‌کند که درخواست HTTP رسیده ناقص است و در اینجا بیشتر به معنای abort شدن آن در سمت کلاینت است. عکس العمل برنامه‌ی Blazor به یک چنین خطایی که در سمت سرور رخ داده، ریفرش کردن کل آدرس جاری و راه اندازی مجدد برنامه‌است. اما ... چرا این اتفاق رخ داده بود؟

اگر برنامه‌ی شما اطلاعاتی را در پوشه‌ی wwwroot ثبت کند، برای مثال یک بانک اطلاعاتی SQLite را در آن قرار داده‌اید، hot reload حتی این پوشه را هم تحت کنترل خود دارد و با هر تغییری در آن، حتی نوشته شدن یک رکورد در فایل بانک اطلاعاتی، مجددا برنامه را کامپایل می‌کند. همین کامپایل پشت صحنه، سبب abort شدن درخواست‌های HTTP سمت کلاینت آن و مشاهده‌ی خطاهای BadHttpRequestException سمت سرور می‌شود که عدم اطلاع از دلیل آن می‌تواند این تصور را پیش بیاورد که کدهای برنامه مشکلی دارند، اما خیر.
اگر با CLI کار می‌کنید، دستور dotnet watch run --no-hot-reload کمی وضع را بهتر می‌کند و معادل افزودن تنظیم زیر به فایل Properties\launchSettings.json است:
"profiles": {
  "Name.Server": {
      "hotReloadEnabled": false,
راه حل دیگر آن، خارج کردن پوشه‌ی wwwroot از حالت watch است که به صورت زیر در فایل csproj قابل انجام است (در سمت برنامه‌ی web api):
<ItemGroup>
  <Watch Remove="wwwroot\**\*" />
</ItemGroup>
مطالب
معرفی واژه‌ی کلیدی جدید required در C# 11
واژه‌ی کلیدی جدید required در C# 11.0، همانند خواص init-only که پیشتر معرفی شدند، با هدف آغاز و نمونه سازی دقیق‌تر و ساده‌تر اشیایی است که برای اینکار، به تعاریف ویژه‌ی سازنده‌ی کلاس‌ها وابسته نیستند.


امکان نمونه سازی بدون قید و شرط کلاس‌ها

تعریف کلاس Article1 را به صورت زیر درنظر بگیرید:
public class Article1
{
    public string Title { get; set; }
    public string? Subtitle { get; set; }
    public string Author { get; set; }
    public DateTime Published { get; set; }
}
ساختار پروژه‌های دات نت 7 نیز به صورت پیش‌فرض به صورت زیر است:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>
یعنی nullable reference types در آن‌ها فعال است. با این فعال بودن، به اخطارهای زیر می‌رسیم:
Non-nullable property 'Title' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [CS11Tests]csharp(CS8618)
Non-nullable property 'Author' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [CS11Tests]csharp(CS8618)
عنوان می‌کند که خاصیت‌های Title و Author، به صورت غیرنال‌پذیر تعریف شده‌اند (و همانند Subtitle نال‌پذیر نیستند)؛ اما تعریف این کلاس به نحوی است که این مساله را الزامی نمی‌کند. یعنی می‌توان نمونه‌ای از Article1 را ایجاد کرد که در آن، هر دوی این خواص نال باشند؛ هرچند در این حالت مشکلی از لحاظ کامپایل وجود نخواهد داشت، اما ممکن است به علت اشتباه استفاده‌ی از آن‌ها، به null reference exceptions برسیم. چون یکی از مهم‌ترین اهداف استفاده از یک چنین تعاریفی و فعال سازی nullable reference type در یک پروژه، ارائه‌ی متادیتای بهتری جهت خواص و پارامترها و خروجی‌های متدهاست تا استفاده کننده دقیقا بداند که آیا این خواص می‌توانند نال باشد یا خیر. اگر  public string ای تعریف شده، یعنی این خاصیت قطعا نال نخواهد بود و می‌توان بدون مشکل و بدون بررسی مقدار آن، از آن استفاده کرد و اگر ?public string ای تعریف شده، یعنی ممکن است مقدار آن نال نیز باشد و بهتر است پیش از استفاده‌ی از آن، حتما مقدار آن بررسی شود. اکنون مشکل اینجا است که هیچگونه قیدی، جهت اجبار به مقدار دهی خواص غیرنال پذیر در اینجا وجود ندارند و می‌توان نمونه‌ای از شیء Article1 را ایجاد کرد که در آن متادیتای خواص غیرنال پذیر تعریفی در آن، نقض شوند.


مدیریت کردن نحوه‌ی نمونه سازی کلاس‌ها، با وابستگی به سازنده‌های آن

یکی از روش‌های مدیریت مشکلی که تا اینجا بررسی شد، تعریف سازنده‌های متعددی برای یک کلاس است؛ تا توسط آن بتوان مقدار دهی یک سری از خواص را اجباری کرد:
public class Article2
{
    public Article2(string title, string subtitle, string author, DateTime published)
    {
        Title = title;
        Subtitle = subtitle;
        Author = author;
        Published = published;
    }

    public Article2(string title, string author, DateTime published)
    {
        Title = title;
        Author = author;
        Published = published;
    }

    public string Title { get; set; }
    public string? Subtitle { get; set; }
    public string Author { get; set; }
    public DateTime Published { get; set; }
}
در این کلاس، نمونه‌ی بهبود یافته‌ی Article1 را مشاهده می‌کنید که استفاده کننده را وادار به مقدار دهی title و author می‌کند. در این حالت اخطارهای کامپایلری را که مشاهده کردید، رفع می‌شوند؛ اما به همراه این مسایل است:
- تعداد سطرهای تعریف این کلاس، به شدت افزایش یافته‌است.
- با اضافه شدن خواص بیشتری به کلاس، به تعاریف بیشتری نیاز خواهد بود.
- سازنده‌ها کار خاصی را بجز نگاشت مقادیر ارائه شده، به خواص کلاس، انجام نمی‌دهند.
- نمونه سازی این کلاس‌ها، شکل طولانی و غیرواضح زیر را پیدا می‌کند و زیبایی inline object initializers را ندارند:
 Article2 article = new("C# 11 Required Keyword", "A new language feature", "Name",  new DateTime(2022, 11, 12));

البته روش دیگر مدیریت یک چنین اخطارهایی، استفاده از مقدار ویژه‌ی !default است که سبب محو اخطارهای یاد شده می‌شود؛ اما باز هم مقدار دهی آن‌را الزامی نمی‌کند. فقط به این معنا است که قول می‌دهیم این خاصیت را در جای دیگری مقدار دهی کنیم و هیچگاه نال نباشد!
 public string Title { get; set; } = default!;


مدیریت کردن نحوه‌ی نمونه سازی کلاس‌ها، بدون وابستگی به سازنده‌های آن در C# 11.0

C# 11 به همراه واژه‌ی کلیدی جدیدی به نام required است تا دیگر نیازی نباشد همانند راه حل فوق، سازنده‌های متعددی را جهت اجبار به مقدار دهی خواص یک شیء، تعریف کنیم. در این حالت تعریف کلاس Article به صورت زیر خلاصه می‌شود و دیگر به همراه اخطارهای کامپایلر نمایش داده شده نیز نیست:
public class Article3
{
    public required string Title { get; set; }
    public string? Subtitle { get; set; }
    public required string Author { get; set; }
    public DateTime Published { get; set; }
}
به این ترتیب هنوز می‌توان از زیبایی و خوانایی به همراه نمونه سازی توسط روش inline object initializers بهره‌مند شد و همچنین مطمئن بود که اگر استفاده کننده خاصیت غیرنال‌پذیر Title را مقدار دهی نکند، اینبار با یک خطای کامپایلر متوقف خواهد شد:



معرفی ویژگی جدید SetsRequiredMembers

کلاس Book زیر را که به همراه یک خاصیت required و دو سازنده‌است، درنظر بگیرید:
public class Book
{
    public Book() => Name = string.Empty;

    public Book(string name) => Name = name;

    public required string Name { get; set; }
}
اکنون فرض کنید که بر این اساس، شیء‌ای را به صورت زیر نمونه سازی کرده‌ایم:
Book book = new("Book's Name");
این قطعه کد با خطای زیر کامپایل نمی‌شود:
Required member 'Book.Name' must be set in the object initializer or attribute constructor. [CS11Tests]csharp(CS9035)
عنوان می‌کند که باید خاصیت Name را حتما مقدار دهی کرد؛ چون از نوع required است. هرچند سازنده‌‌ای که از آن استفاده شده، این مقدار دهی را انجام داده‌است و مشکلی از لحاظ عدم مقدار دهی خاصیت Name در اینجا وجود ندارد. برای رفع این مشکل، باید تغییر زیر را اعمال کرد:
public class Book
{
    [SetsRequiredMembers]
    public Book() => Name = string.Empty;

    [SetsRequiredMembers]
    public Book(string name) => Name = name;

    public required string Name { get; set; }
}
با استفاده از ویژگی جدید SetsRequiredMembers عنوان می‌کنیم که این سازنده‌ی خاص، حتما خواص از نوع required را نیز مقدار دهی می‌کند و نیازی به صدور خطای یاد شده نیست. در این حالت بررسی خواص required توسط کامپایلر غیرفعال می‌شود.


محدودیت‌های کار با خواص required

- واژه‌ی کلیدی required را می‌توان تنها به خواص و فیلدهای نوع‌های class, record, record struct اعمال کرد. امکان اعمال این واژه‌ی کلیدی به اجزای یک اینترفیس وجود ندارد.
- میدان دید اعضای required باید حداقل در حد نوع‌های دربرگیرنده‌ی آن‌ها باشند. برای مثال اگر کلاسی public است، نمی‌توان در آن یک فیلد required با میدان دید protected را تعریف کرد.
- نوع‌های مشتق شده‌ی از یک نوع پایه، نمی‌توانند اعضای required آن‌را مخفی کنند و اگر قصد بازنویسی آن‌را دارند، باید حتما واژه‌ی کلیدی required را لحاظ کنند.
- اگر سازنده‌ای به سازنده‌ی دیگری از طریق ذکر ()base و یا ()this زنجیر شده باشد نیز باید ویژگی SetsRequiredMembers مرتبط را تکرار کند.
مطالب
سطح دوم cache در NHibernate

عموما دو الگوی اصلی caching در برنامه‌ها وجود دارند: cache aside و cache trough .
در الگوی cache trough ، سیستم caching داخل DAL (که در اینجا همان NHibernate است)، تعبیه می‌شود؛ مانند سطح اول caching که پیشتر در مورد آن صحبت شد. در این حالت cache از دید سایر قسمت‌های برنامه مخفی است و DAL به صورت خودکار آن‌را مدیریت می‌کند.
در الگوی cache aside ، کار مدیریت سیستم caching دستی است و خارج از NHibernate قرار می‌گیرد و DAL هیچگونه اطلاعی از وجود آن ندارد. در این حالت لایه caching موظف است تا هنگام به روز شدن بانک اطلاعاتی، اطلاعات خود را نیز به روز نماید. این لایه عموما توسط سایر شرکت‌ها یا گروه‌ها برنامه نویسی تهیه می‌شود.
NHibernate جهت سهولت کار با این نوع cache providers خارجی، نقاط تزریق ویژه‌ای را تدارک دیده است که به second level cache معروف است. هدف از second level cache فراهم آوردن دیدی کش شده از بانک اطلاعاتی است تا فراخوانی‌های کوئری‌ها به سرعت و بدون تماس با بانک اطلاعاتی صورت گیرد.
در حال حاضر (زمان نگارش این مطلب)، entity framework این لایه‌ی دوم caching یا به عبارتی دیگر، امکان تزریق ساده‌تر cache providers خارجی را به صورت توکار ارائه نمی‌دهد.
در NHibernate طول عمر second level cache در سطح session factory (یا به عبارتی طول عمر تمام برنامه) تعریف می‌شود و برخلاف سطح اولیه caching محدود به یک سشن نیست. در این حالت هر زمانیکه یک موجودیت به همراه ID منحصربفرد آن تحت نظر NHibernate قرارگیرد و همچنین سطح دوم caching نیز فعال باشد، این موجودیت در تمام سشن‌های برنامه بدون نیاز به مراجعه به بانک اطلاعاتی در دسترس خواهد بود (بنابراین باید دقت داشت که هدف از این سیستم، کار سریعتر با اطلاعاتی است که سطح دسترسی عمومی دارند).

در ادامه لیستی از cache providers خارجی مهیا جهت استفاده در سطح دوم caching را ملاحظه می‌نمائید:

  • AppFabric Caching Services : بر اساس Microsoft's AppFabric Caching Services که یک پلتفرم caching محسوب می‌شود (+). (این پروژه پیشتر به نام Velocity معروف شده بود و قرار بود تنها برای ASP.NET ارائه شود که سیاست آن به گونه‌ای جامع‌تر تغییر کرده است)
  • MemCache : بر اساس سیستم معروف MemCached تهیه شده است (+).
  • NCache : (+)
  • ScaleOut : (+)
  • Prevalence : (+)
  • SysCache : بر اساس همان روش آشنای متداول در برنامه‌های ASP.NET به کمک System.Web.Caching.Cache کار می‌کند؛ یا به قولی همان IIS caching
  • SysCache2 : همانند SysCache است با این تفاوت که SQL dependencies ویژه SQL Server را نیز پشتیبانی می‌کند.
  • SharedCache : یک سیستم distributed caching نوشته شده برای دات نت است (+).
این موارد و پروایدرها جزو پروژه‌ی nhcontrib در سایت سورس فورج هستند (+).


مطالب تکمیلی:



مطالب
معرفی Async Parallel.ForEach در دات نت 6
عموما زمانیکه می‌خواهیم تمام وظایف مدنظر، به صورت موازی اجرا شوند، آن‌ها را Task.WhenAll می‌کنیم. برای مثال 10 هزار درخواست HTTP را به صورت وظایفی، WhenAll می‌کنیم و ... در این حالت ... سرور ریموت، IP شما را خواهد بست! چون کنترلی بر روی تعداد وظیفه‌ی در حالت اجرای موازی وجود ندارد و یک چنین عملی، شبیه به یک حمله‌ی DDOS عمل می‌کند! برای مدیریت بهتر یک چنین مواردی، در دات نت 6 متدهای Parallel.ForEachAsync ارائه شده‌اند تا دیگر نیازی به استفاده از راه‌حل‌های ثالثی که عموما آنچنان بهینه هم نیستند، نباشد.
public static Task ForEachAsync<TSource>(IEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IEnumerable<TSource> source, CancellationToken cancellationToken, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IEnumerable<TSource> source, ParallelOptions parallelOptions, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IAsyncEnumerable<TSource> source, ParallelOptions parallelOptions, Func<TSource, CancellationToken, ValueTask> body)
این مجموعه متدها از ValueTaskها بجای Taskها استفاده می‌کند تا سربار ایجاد Taskها در حلقه‌ها کاهش یابد. همچنین در اینجا degree of parallelism به صورت پیش‌فرض به تعداد هسته‌های سی‌پی تنظیم شده‌است (Environment.ProcessorCount)؛ چون عموما توسعه دهنده‌ها نمی‌دانند که چه عددی را باید برای آن انتخاب کنند. هر چند امکان تنظیم دستی آن‌ها هم وجود دارد (یکی از مهم‌ترین مشکلات کار با WhenAll).

یک مثال: در اینجا می‌خواهیم به صورت موازی، مشخصات کاربرانی از Github را توسط HttpClient دریافت کنیم. هر بار هم فقط می‌خواهیم سه وظیفه اجرا شوند و نه بیشتر
using System.Net.Http.Headers;
using System.Net.Http.Json;
 
var userHandlers = new []  { "users/VahidN", "users/shanselman", "users/jaredpar", "users/davidfowl" };
 
using HttpClient client = new()
{
    BaseAddress = new Uri("https://api.github.com"),
};
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("DotNet", "6"));
 
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = 3 };
 
await Parallel.ForEachAsync(userHandlers, parallelOptions, async (uri, token) =>
{
    var user = await client.GetFromJsonAsync<GitHubUser>(uri, token); 
    Console.WriteLine($"Name: {user.Name}\nBio: {user.Bio}\n");
});
 
public class GitHubUser
{
    public string Name { get; set; }
    public string  Bio { get; set; }
}
در این مثال، نمونه‌ای از کارکرد متد جدید Parallel.ForEachAsync را مشاهده می‌کنید که اینبار، MaxDegreeOfParallelism آن قابل تنظیم است. یعنی با تنظیم فوق، هربار فقط سه وظیفه به صورت موازی اجرا خواهند شد. البته تنظیم آن به منهای یک، همان حالت WhenAll را سبب خواهد شد؛ یعنی محدودیتی وجود نخواهد داشت.
متد Parallel.ForEachAsync، آرایه‌ای را که باید بر روی آن کار کند، دریافت می‌کند. سپس تنظیمات اجرای موازی آن‌ها را هم مشخص می‌کنیم. در ادامه آن‌ها را در دسته‌های مشخصی، به صورت موازی بر اساس منطقی که مشخص می‌کنیم، اجرا خواهد کرد.


وضعیت امکان اجرای موازی متدهای async همزمان، تا پیش از دات نت 6

<List<T به همراه متد الحاقی ForEach است که می‌تواند یک <Action<T را بر روی المان‌های این لیست، اجرا کند و ... عموما زمانیکه به وظایف async می‌رسیم، به اشتباه مورد استفاده قرار می‌گیرد:
customers.ForEach(c => SendEmailAsync(c));
مثال فوق، با اجرای حلقه‌ی زیر تفاوتی ندارد:
foreach(var c in customers)
{
    SendEmailAsync(c); // the return task is ignored
}
یعنی یک عملیات async، بدون await فراخوانی شده‌است و تا پایان عملیات مدنظر، صبر نخواهد شد. حداقل مشکل آن این است که اگر در این بین استثنایی رخ دهد، هیچگاه متوجه آن نخواهید شد و حتی می‌تواند کل پروسه‌ی برنامه را خاتمه دهد. شاید عنوان کنید که می‌شود این مشکل را به صورت زیر حل کرد:
customers.ForEach(async c => await SendEmailAsync(c));
اما ... این روش هم تفاوتی با قبل ندارد. از این لحاظ که متد ForEach یک <Action<T را دریافت می‌کند که خروجی آن void است. یعنی در نهایت با راه حل دوم، فقط یک async void ایجاد می‌شود که باز هم قابلیت صبر کردن تا پایان عملیات را ندارد. نکته‌ی مهم اینجا است که اجرای موازی آن‌ها توسط متد Parallel.ForEach نیز دقیقا همین مشکل را دارد.
تنها راه حل پذیرفته‌ی شده‌ی چنین عمل async ای، فراخوانی آن‌ها به صورت متداول زیر و بدون استفاده از متد ForEach است:
foreach(var c in customers)
{
   await SendEmailAsync(c);
}
و یا Task.WhenAll کردن آن‌ها، با علم به این موضوع که MaxDegreeOfParallelism آن قابل کنترل نیست (حداقل به صورت استاندارد و بدون نیاز به کتابخانه‌های جانبی). برای مثال بجای نوشتن:
foreach(var o in orders)
{
    await ProcessOrderAsync(o);
}
می‌توان آن‌را به صورت زیر درآورد:
var tasks = orders.Select(o => ProcessOrderAsync(o)).ToList();
await Task.WhenAll(tasks);
در این حالت عملیات ProcessOrderAsync را تبدیل به لیستی از وظایف مدنظر کرده و به متد Task.WhenAll ارسال می‌کنیم تا به صورت موازی اجرا شوند. اما ... اگر 10 هزار Task وجود داشته باشند، کنترلی بر روی تعداد وظایف در حال اجرای موازی وجود نخواهد داشت و این مورد نه تنها سبب بالا رفتن کارآیی نخواهد شد، بلکه می‌تواند سرور را هم با اخلال پردازشی، به علت کمبود منابع در دسترس مواجه کند.

دات نت 6، هم کنترل MaxDegreeOfParallelism را میسر کرده‌است و هم اینکه اینبار نگارش async واقعی Parallel.ForEachAsync را ارائه داده‌است تا دیگر همانند حالت قبلی Parallel.ForEach، به async void‌ها و مشکلات مرتبط با آن‌ها نرسیم.
نظرات مطالب
هزینه استفاده از دات نت فریم ورک چقدر است؟
سلام دوست عزیز بنده قصد تخریب یا نفی گفته های شما رو ندارم فقط نکاتی که به نظرم میرسه بیان می کنم:

نسخه express ویژوال استودیو به گفته خود مایکروسافت هم در سطحی نیست که توسعه دهنده های حرفه ای نرم افزار ازش استفاده کنن و بیشتر برای علاقه مندان و دانشجو ها و یا تست پروژه کاربردش توصیه شده. برای مثال می تونید از همین شرکت های ایرانی فعال در نرم افزار بپرسیم که کدومشون می تونن از نسخه express بدون مشکل استفاده کنن. IDE های دیگر هم برای دات نت به پای ویژوال استودیو نمی رسن اکثرا هم این مورد رو موافقن که بهترین IDE برای دات نت همون ویژوال استودیو هست. اون مورد هم که به صورت دستی کامپایل و دیباگ رو انجام بدیم که به این موضوع بر می گرده که اصلا از IDE استفاده بکنیم یا نه؟ هدف تو دات نت اینه که توسعه دهنده فکر و نیروش روی طراحی و منطق برنامه بگذاره و تا حد امکان درگیر این مسایل نشه بنابراین کار با IDE برای هماهنگی با این موضوع ضروری هست. اگه توسعه دهنده ها بخوان زحمت این ریزه کاری ها رو تحمل کنن خیلی بهتره که به جای دات نت سراغ یه پلتفرم و زبان دیگه برن چون عملا از خیلی از مزایاش استفاده نمی کنن.

در مورد پروژه مونو هم بیشتر میشه گفت این تعریف ها اسم هست تا واقعی. برای مثال میشه به این نگاه کرد که چه پروژه ی بزرگی تا به حال با مونو پیاده سازی شده؟ به جز چند برنامه متن باز و پروژه های کوچیک دیگه شرکتی پیدا نمیشه که بخواد روی مونو سرمایه گذاری کلانی کنه. یکی از دلایل این عدم استقبال از مونو مربوط میشه به ترس از این که امکان داره مایکروسافت با شکایت علیه مونو تمام مجوز های استفاده از اون رو سلب کنه و اون وقته که هر چی رشته بودن پنبه میشه. حتی پروژه های متن باز هم سعی می کنن تا جایی که امکان داره از مونو دوری کنن. واقعا به طور عملی نمیشه فعلا روی مونو حسابی باز کرد مخصوصا از نوع تجاری.

این موارد رو فقط برای این گفتم که با دید بازتری به مساله نگاه کنیم نه این که حرف های شما رو نقض کنم. ولی باید این مورد هم در نظر داشت که با وجود این که سری رایگان  محصولات express مایکروسافت و پروژه مونو وجود دارن آیا در یک محیط کاری و تجاری میشه از اون ها استفاده کرد یا خیر. چیزی که تو تقریبا تمام شرکت ها مشاهده می کنیم که که عملا همه از نسخه های پولی ویژوال استودیو و SQL server استفاده می کنن و خبری هم از مونو نیست.

حرف آخرم اینه منظورم این نیست که بگیم باید از مایکروسافت دوری کرد. مایکروسافت یه پلتفرم درست کرده برای یه عده از توسعه دهنده ها که تونسته موفق هم باشه و خیلی ها هم جذب کنه. اتفاقا خیلی هم خوبه که در دات نت توی ایران پر بار هستیم. ولی آیا زبان ها و پلتفرم های دیگه هم به اون جایگاهی که دارن در ایران رسیدن؟ بازار کار دات نت در ایران خوبه اما آیا این تک قطبی شدن بازار به نفع توسعه دهنده یا مشتری هست؟ منابع غیر دات نت در ایران کم هست اما این دلیل میشه که زبان های دیگرو نفی کنیم و همه منابع انگلیسی و دیگر اون ها رو نادیده بگیرم؟ اگه فقط بخوایم به چیزی که جا افتاده هست مهر تایید بزنیم و بقیه رو نادیده بگیریم خب حرفی باقی نمی مونه اما اگه می خوایم دانش کامپیوتر رو گسترش بدیم به بقیه هم باید همون قدر بها بدیم.
با احترام
نظرات مطالب
هزینه استفاده از دات نت فریم ورک چقدر است؟
سلام دوست عزیز بنده قصد تخریب یا نفی گفته های شما رو ندارم فقط نکاتی که به نظرم میرسه بیان می کنم:

نسخه express ویژوال استودیو به گفته خود مایکروسافت هم در سطحی نیست که توسعه دهنده های حرفه ای نرم افزار ازش استفاده کنن و بیشتر برای علاقه مندان و دانشجو ها و یا تست پروژه کاربردش توصیه شده. برای مثال می تونید از همین شرکت های ایرانی فعال در نرم افزار بپرسیم که کدومشون می تونن از نسخه express بدون مشکل استفاده کنن. IDE های دیگر هم برای دات نت به پای ویژوال استودیو نمی رسن اکثرا هم این مورد رو موافقن که بهترین IDE برای دات نت همون ویژوال استودیو هست. اون مورد هم که به صورت دستی کامپایل و دیباگ رو انجام بدیم که به این موضوع بر می گرده که اصلا از IDE استفاده بکنیم یا نه؟ هدف تو دات نت اینه که توسعه دهنده فکر و نیروش روی طراحی و منطق برنامه بگذاره و تا حد امکان درگیر این مسایل نشه بنابراین کار با IDE برای هماهنگی با این موضوع ضروری هست. اگه توسعه دهنده ها بخوان زحمت این ریزه کاری ها رو تحمل کنن خیلی بهتره که به جای دات نت سراغ یه پلتفرم و زبان دیگه برن چون عملا از خیلی از مزایاش استفاده نمی کنن.

در مورد پروژه مونو هم بیشتر میشه گفت این تعریف ها اسم هست تا واقعی. برای مثال میشه به این نگاه کرد که چه پروژه ی بزرگی تا به حال با مونو پیاده سازی شده؟ به جز چند برنامه متن باز و پروژه های کوچیک دیگه شرکتی پیدا نمیشه که بخواد روی مونو سرمایه گذاری کلانی کنه. یکی از دلایل این عدم استقبال از مونو مربوط میشه به ترس از این که امکان داره مایکروسافت با شکایت علیه مونو تمام مجوز های استفاده از اون رو سلب کنه و اون وقته که هر چی رشته بودن پنبه میشه. حتی پروژه های متن باز هم سعی می کنن تا جایی که امکان داره از مونو دوری کنن. واقعا به طور عملی نمیشه فعلا روی مونو حسابی باز کرد مخصوصا از نوع تجاری.

این موارد رو فقط برای این گفتم که با دید بازتری به مساله نگاه کنیم نه این که حرف های شما رو نقض کنم. ولی باید این مورد هم در نظر داشت که با وجود این که سری رایگان  محصولات express مایکروسافت و پروژه مونو وجود دارن آیا در یک محیط کاری و تجاری میشه از اون ها استفاده کرد یا خیر. چیزی که تو تقریبا تمام شرکت ها مشاهده می کنیم که که عملا همه از نسخه های پولی ویژوال استودیو و SQL server استفاده می کنن و خبری هم از مونو نیست.

حرف آخرم اینه منظورم این نیست که بگیم باید از مایکروسافت دوری کرد. مایکروسافت یه پلتفرم درست کرده برای یه عده از توسعه دهنده ها که تونسته موفق هم باشه و خیلی ها هم جذب کنه. اتفاقا خیلی هم خوبه که در دات نت توی ایران پر بار هستیم. ولی آیا زبان ها و پلتفرم های دیگه هم به اون جایگاهی که دارن در ایران رسیدن؟ بازار کار دات نت در ایران خوبه اما آیا این تک قطبی شدن بازار به نفع توسعه دهنده یا مشتری هست؟ منابع غیر دات نت در ایران کم هست اما این دلیل میشه که زبان های دیگرو نفی کنیم و همه منابع انگلیسی و دیگر اون ها رو نادیده بگیرم؟ اگه فقط بخوایم به چیزی که جا افتاده هست مهر تایید بزنیم و بقیه رو نادیده بگیریم خب حرفی باقی نمی مونه اما اگه می خوایم دانش کامپیوتر رو گسترش بدیم به بقیه هم باید همون قدر بها بدیم.
با احترام
نظرات مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
روش استفاده از TypeScript در پروژه‌های Blazor
شاید علاقمند باشید تا اسکریپت‌های مورد نیاز یک پروژه‌ی Blazor را با TypeScript تهیه کنید؛ تا از مزایای بررسی نوع‌ها، intellisense قوی، null checking و غیره بهره‌مند شوید و سپس توسط کامپایلر آن، حاصل را به کدهای نهایی js تبدیل کنید. برای اینکار می‌توان مراحل زیر را طی کرد:

الف) تهیه فایل تنظیمات کامپایلر TypeScript
نیاز است فایل tsconfig.json را در ریشه‌ی پروژه، جائیکه فایل csproj قرار دارد، با محتوای زیر ایجاد کرد:
{
  "compilerOptions": {
    "strict": true,
    "removeComments": false,
    "sourceMap": false,
    "noEmitOnError": true,
    "target": "ES2020",
    "module": "ES2020",
    "outDir": "wwwroot/scripts"
  },
  "include": [
    "Scripts/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}
در این حالت فرض بر این است که فایل‌های ts. در پوشه‌ی scripts قرار گرفته‌اند و فایل‌های نهایی کامپایل شده در پوشه‌ی wwwroot/scripts تولید خواهند شد.

ب) فعالسازی کامپایلر TypeScript به ازای هر بار build برنامه
برای اینکار نیاز است فایل csproj را به صورت زیر تکمیل کرد:
<Project Sdk="Microsoft.NET.Sdk.Razor">
  <ItemGroup>
    <PackageReference Include="Microsoft.TypeScript.MSBuild" Version="4.3.5">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <Content Remove="tsconfig.json" />
  </ItemGroup>
  <ItemGroup>
    <TypeScriptCompile Include="tsconfig.json">
      <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    </TypeScriptCompile>
  </ItemGroup>
</Project>
با اینکار ابزار TypeScript.MSBuild اضافه شده، بر اساس tsconfig.json قسمت الف، کار کامپایل فایل‌های ts را به صورت خودکار انجام می‌دهد.

ج) یک مثال از تبدیل کدهای js به ts
فرض کنید کدهای سراسری زیر را داریم که به شیء window اضافه شده‌اند:
window.exampleJsFunctions = {
  showPrompt: function (message) {
    return prompt(message, 'Type anything here');
  }
};
اکنون برای تبدیل آن به ts.، می‌توان به صورت زیر، فضای نام و کلاسی را ایجاد کرد:
namespace JSInteropWithTypeScript {
   export class ExampleJsFunctions {
        public showPrompt(message: string): string {
            return prompt(message, 'Type anything here');
        }
    }
}

export function showPrompt(message: string): string {
   var fns = new JSInteropWithTypeScript. ExampleJsFunctions();
   return fns.showPrompt(message);
}
قسمت مهم آن، export function انتهایی است. این موردی است که توسط Blazor قابل شناسایی و استفاده است.

د) روش استفاده از خروجی کامپایل شده‌ی TypeScript در کامپوننت‌های Blazor
پس از کامپایل قطعه کد فوق، ابتدا مسیر قابل دسترسی به فایل js قرار گرفته شده در پوشه‌ی wwwroot را مشخص می‌کنیم که همواره با الگوی زیر است. همچنین اینبار IJSObjectReference است که امکان دسترسی به export function یاد شده را میسر می‌کند:
private const string ScriptPath = "./_content/----namespace-here---/scripts/file.js";
private IJSObjectReference scriptModule;
دو تعریف فوق، فیلدهایی هستند که در سطح کامپوننت تعریف می‌شوند. سپس مقدار دهی آن‌ها در OnAfterRenderAsync صورت می‌گیرد تا کار import ماژول را انجام دهد:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
  if (scriptModule == null)
    scriptModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", ScriptPath);
پس از این مرحله، امکان کار با ماژول بارگذاری شده، به صورت متداولی میسر می‌شود و می‌توان export function‌ها را در اینجا فراخوانی کرد:
await scriptModule.InvokeVoidAsync("exported fn name", params);
در آخر کار هم باید آن‌را dispose کرد؛ که روش آن به صورت زیر است:
- ابتدا باید این کامپوننت، IAsyncDisposable را پیاده سازی کند:
public partial class MyComponent : IAsyncDisposable
سپس پیاده سازی آن به صورت زیر انجام می‌شود:
public async ValueTask DisposeAsync()
{
  if (scriptModule != null)
  {
    await scriptModule.DisposeAsync();
  }
}
مطالب
استفاده از dll های دات نت در الکترون
یکی از جذاب‌ترین کارهایی که در کار برنامه نویسی می‌توان انجام داد این است که بتوانیم از کدهای یک زبان دیگر، در زبانی دیگر استفاده کنیم. بسیاری از کاربران این سایت مدت‌هاست که از دات نت استفاده می‌کنند و ممکن است بخواهند از dll‌های آن در الکترون بهره ببرند. در این مقاله بررسی می‌کنیم که چگونه از کدهای دات نت در الکترون استفاده کنیم. ابتدا یک پروژه‌ی Class Library جدید را برای برنامه‌ی فاکتوریل با کد زیر تولید می‌کنیم:
namespace electron_factorial
{
    public class MyMath
    {
        public int Factorial(int number)
        {
            if (number < 2)
                return number;
            return Factorial(number - 1)*number;
        }

        public Task<object> CalcFactorial(object obj)
        {
            return Task.Factory.StartNew(() => Factorial((int) obj) as object);
        }
    }
}
باید توجه داشته باشید متدی که در الکترون صدا میزنید باید دارای آرگومان‌های object و خروجی <Task<Object باشد. برای آشنایی با دستور task مقالات اصول کدنویسی موازی در سایت جاری را مطالعه فرمایید.

سپس کتابخانه تولید شده را به داخل دایرکتوری پروژه انتقال می‌دهیم. پروژه electron-edge را در پروژه صدا می‌زنیم؛ این پروژه که برای الکترون بهینه شده است از روی پروژه Edge فورک شده است که برای خروجی دات نت در node.js تولید شده بود.
npm install electron-edge

حالا کد زیر را می‌نویسیم:
  <script>
  function  myFunction()
    {
      var edge = require('electron-edge');
      var CalcFactorial = edge.func({
           assemblyFile: 'electron-factorial.dll',
           typeName: 'electron_factorial.MyMath',
           methodName: "CalcFactorial"
      });

      document.getElementById("calc").addEventListener("click", function (e) {
           var inputText = document.getElementById("txtnumber").value;
           CalcFactorial(Number(inputText), function (error, result) {
                document.getElementById("factorial").value=result;
           });
      });
    }

    </script>
در ابتدای امر، کلاسی از جنس edge را می‌سازیم و با استفاده از متد func، اطلاعات کتابخانه‌ی دات نت را به عنوان آرگومان وارد می‌کنیم. از این پس CalcFactirial به متد مورد نظر ما در dll اشاره می‌کند. هنگام استفاده از آن هم باید مدنظر داشته باشید با توجه به استفاده از برنامه نویسی موازی، پاسخ تابع به صورت یک callback در اختیار ما قرار می‌گیرد که اولین پارامتر آن خطاست و در صورت رخ دادن، مزین به اطلاعات خطا شده که می‌توانید آن را Throw کنید و در غیر این صورت نال بازگشت داده می‌شود و پارامتر دوم هم در صورتیکه به خطایی برخورد نکنیم، پاسخ تابع را برای ما ارسال خواهد کرد.
مطالب
کاربرد Action ها در Github - خودکار سازی فرآیند کامپایل و آپلود فایل در Release گیت‌هاب
نکته: این آموزش مبتنی بر دات نت نسخه 5 می‌باشد (قابل استفاده در نسخه 3.0 و 3.1 نیز می‌باشد اما تست نشده است). در این آموزش فرض شده‌است که شما توانایی کار کردن با git و گیت‌هاب را دارید. همچنین دقت کنید که گزینه‌های زیر در فایل csproj شما موجود باشد، در غیر این صورت ممکن است با خطا مواجه شوید:
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>

چند وقتی می‌شود که گیت‌هاب اکشن‌ها را معرفی کرده که با استفاده از این اکشن‌ها میتوان برخی فعالیت‌های تکراری را، خودکار سازی کرد و در وقت و انرژی صرفه جویی کرد. ما چه کارهایی را میتوانیم با کمک اکشن‌ها انجام بدهیم؟ تقریبا میشود گفت هرکاری را میتوان با اکشن‌ها انجام داد. کامپایل کردن پروژه، تست کردن، پابلیش کردن و...
من معمولا فایل‌های خروجی که برای پروژه‌هایم میگیرم، بدلیل استفاده از ویژگی SelfContained و ایمیج‌های ReadyToRun برای اجرای سریعتر، معمولا حجمی حدود 140 مگ دارند. حالا وقتی آنها را فشرده میکنم، به حدود 50 مگ کاهش پیدا میکنند که اگر من بخواهم هر دفعه که از برنامه خروجی میگیرم، فایل خروجی را فشرده کنم و آن را در گیت‌هاب آپلود کنم، هم فرآیندی زمانبر هست و هم اینکه تکراری و خسته کننده؛ در نتیجه تصمیم گرفتم که از اکشن گیت‌هاب برای خودکارسازی این فرآیند استفاده کنم. 
در این نوشته میخواهیم یک اکشن بنویسیم که بر اساس سورس کد موجود بر روی گیت‌هاب، برنامه را کامپایل کند، فایل اجرایی را بصورت فایل فشرده Zip ایجاد کرده و در نهایت این فایل فشرده را در قسمت Release گیت‌هاب منتشر کند.
برای شروع کار اول باید در مخزن پروژه، در سایت گیت‌هاب، به قسمت Action برویم:

حالا روی قسمت New workflow کلیک میکنیم
 
شما میتونید از قالب‌های پیش‌فرض موجود، هر کدام را که خواستید انتخاب کنید. من ترجیح میدهم با یک قالب خالی شروع کنم. پس بر روی set up a workflow yourself کلیک میکنیم:
 
کدهای پیش‌فرضی که وجود دارند را پاک کنید، تا مثل تصویر زیر، یک فایل کاملا تمیز و خالی را داشته باشیم. نکته‌ای که باید دقت کنید، اسم و مسیر فایل می‌باشد. مسیر فایل را اصلا نباید تغییر داد، ولی اسم فایل را میتوانید اصلاح کنید؛ ولی توجه کنید که پسوند فایل باید yml باشد.
حالا نوبت نوشتن کدهای اکشن رسیده:
اول از همه کد زیر را مینویسیم:
name: "Publish"

این خط، اسم اکشن مارا مشخص میکند که قرار است در لیست workflow‌ها نمایش داده شود:
در کد بعدی که باید بنویسیم (در خط بعدی) باید مشخص بکنیم که این اکشن چه زمانی اجرا بشود. گزینه‌های زیر استفاده بیشتری دارند:
  • push = هر زمان که کامیتی روی گیتهاب پوش شود، اکشن اجرا میشود.
  • pull_request = هر زمانی که یک پول ریکوئست مرج شود، اکشن اجرا میشود.
  • workflow_dispatch = برنامه نویس خودش میتواند با کلیک بر روی دکمه‌ی مشخصی در قسمت اکشن‌ها، اکشن موردنظر را اجرا کند.
لیست کامل تریگر‌ها را میتوانید اینجا مطالعه کنید.
ما از push استفاده میکنیم؛ البته مقداری آن را تغییر میدهیم تا زمانیکه شامل تگ بود، اکشن اجرا شود.
on:
  push:
    tags:
      - "v*"
نکته‌ای که وجود دارد، ما در آخر این دستور، از *v استفاده کردیم که اشاره میکند اگر تگی بصورت v1.0.0 بود، اجرا بشود. * میتواند هر عددی باشد.
3 متغیر ایجاد میکنیم تا محل فایل پروژه، اجرایی و فشرده را نگه دارد، تا فایل اکشن زیاد شلوغ نباشد. 
env:
  PROJECT_PATH: src/HandySub/HandySub.csproj
  ZIP_PATH: src/HandySub/bin/Release/net5.0-windows/win-x86/publish/HandySub-x86.zip
  EXE_PATH: src/HandySub/bin/Release/net5.0-windows/win-x86/publish/HandySub.exe
حالا باید دستورات خودکار سازی را بنویسیم. همه دستورات باید در قسمت jobs نوشته شوند:
 jobs:
  deploy:
    runs-on: windows-latest
به قسمت runs-on توجه کنید. این گزینه مشخص میکند که اکشن ما بر روی سرور ویندوزی و آخرین نسخه‌ی از آن اجرا بشود؛ در صورت نیاز میتوانید از linux نیز استفاده کنید.
در خط بعدی باید قدم به قدم دستورات را بنویسیم. ما برای هر قدم، از name استفاده میکنیم که هنگام اجرای اکشن، بصورت مرتب و خوانا بتوانیم بفهمیم که در چه مرحله‌ای از اجرا هستیم.
قدم اول باید اکشن را آماده کنیم. اکثر دستورات مهم در این اکشن موجود است:
 steps:
      - name: Initialize Actions
        uses: actions/checkout@v2
قدم بعدی باید sdk دات نت را بر روی سرور، دانلود و نصب کنیم:
     - name: Initialize .Net
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 5.0.x
قبل از بیلد پروژه، باید کتابخانه‌ها و بسته‌های نیوگت را restore کنیم، تا بیلد، خطا نداشته باشد:
  - name: Restore Project
        run: dotnet restore ${{ env.PROJECT_PATH }}
حالا میتوانیم دستور خروجی گرفتن را بنویسیم (حتما باید در فایل csproj پروژه خود، runtimeidentifier  پروژه مشخص باشد که اینجا ما از win-x86 استفاده کردیم؛ در غیر اینصورت خطا میگیرید)  چون در مرحله قبل پروژه را restore کردیم، هنگام خروجی گرفتن، دستور no-restore را مینویسیم تا دوباره بسته‌های نیوگت ری‌استور نشود.
 - name: Publish Project
        run: dotnet publish ${{ env.PROJECT_PATH }} -c Release --self-contained -r win-x86 --no-restore
حالا که فایل اجرایی را ایجاد کردیم، باید آن را بصورت فشرده و zip در بیاوریم. برای اینکار از یک اکشن دیگر کمک میگیریم. اول آن را به اصطلاح using میکنیم؛ سپس از آن استفاده میکنیم. این اکشن 2 ورودی دارد: files و dest که به ترتیب باید آدرس فایل‌ها و محل ذخیره زیپ را به آن بدهیم که ما از متغیرهایی که قبلا ایجاد کرده‌ایم، استفاده میکنیم. 
 - name: Create Zip File
        uses: papeloto/action-zip@v1
        with:
          files: ${{ env.EXE_PATH }}
          dest: ${{ env.ZIP_PATH }}
در مرحله بعدی باید از یک اکشن دیگر برای ساخت Release در قسمت گیت‌هاب کمک بگیریم. دقت کنید که برای آپلود کردن فایل زیپ داخل این Release، ما باید توکن و id این ریلیز را ذخیره کنیم که در اینجا در متغیر GITHUB_TOKEN و id ذخیره میکنیم. توکن را میتوانیم از قسمت secrets خود گیت‌هاب دریافت کنیم. همچنین ما اسم تگ را از طریق github.ref دریافت میکنیم (که مقدار v1.0.0 را به عنوان مثال برای ما برگشت خواهد داد (شماره نسخه همان تگی است که پوش کرده‌ایم) 
 - name: Initialize Release
        uses: actions/create-release@v1
        id: create_release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: ${{ github.ref }}
حالا نوبت این است که فایل زیپ را آپلود کنیم. دوباره باید از اکشن دیگری برای این امر کمک بگیریم. توکنی را که قبلا ذخیره کردیم، همراه با فایل زیپ، به این اکشن میدهیم و در پایان به کمک id که قبلا ذخیره کردیم، لینک فایل آپلود شده را از آن دریافت میکنیم.  
 - name: Create Release 
        uses: csexton/release-asset-action@v2
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          file: ${{ env.ZIP_PATH }}
          release-url: ${{ steps.create_release.outputs.upload_url }}
کد کامل را اینجا ببینید: 
name: "Publish"

on:
  push:
    tags:
      - "v*"

env:
  PROJECT_PATH: src/HandySub/HandySub.csproj
  ZIP_PATH: src/HandySub/bin/Release/net5.0-windows/win-x86/publish/HandySub-x86.zip
  EXE_PATH: src/HandySub/bin/Release/net5.0-windows/win-x86/publish/HandySub.exe
  

jobs:
  deploy:
    runs-on: windows-latest
    steps:
      - name: Initialize Actions
        uses: actions/checkout@v2

      - name: Initialize .Net
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 5.0.x
      
      - name: Restore Project
        run: dotnet restore ${{ env.PROJECT_PATH }}

      - name: Publish Project
        run: dotnet publish ${{ env.PROJECT_PATH }} -c Release --self-contained -r win-x86 --no-restore

      - name: Create Zip File
        uses: papeloto/action-zip@v1
        with:
          files: ${{ env.EXE_PATH }}
          dest: ${{ env.ZIP_PATH }}
          
      - name: Initialize Release
        uses: actions/create-release@v1
        id: create_release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: ${{ github.ref }}
      
      - name: Create Release    
        uses: csexton/release-asset-action@v2
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          file: ${{ env.ZIP_PATH }}
          release-url: ${{ steps.create_release.outputs.upload_url }}

در آخر بر روی دکمه سبز رنگ بالا سمت راست start commit کلیک کنید تا تغییرات ثبت شوند.
به پروژه خود برگردید و ترمینال را باز کنید و یک تگ جدید را ایجاد کنید. 
git tag v1.0.0
سپس تگ ایجاد شده را به مخزن گیت‌هاب پوش کنید:
git push origin tag v1.0.0

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

دو تنظیم زیر را در نظر بگیرید:
<add key="nhibernate-logger" value="NHibernate.Helper.Logging.LoggerFactory, NHibernate.Helper" />
و یا
<add name="StaticContentCacheModule" type="StaticContentCacheModule.StaticCache, StaticContentCacheModule"/>
این نوع موارد را در فایل‌های app.config و یا web.config زیاد می‌توان یافت.
الان فرض کنید کلاس StaticCache مربوط به StaticContentCacheModule فرضی فوق را به صورت دستی به برنامه‌ی خود اضافه کرده‌اید. همچنین سطر فوق را نیز بدون هیچ تغییری در قسمت http modules مربوط به web.config برنامه معرفی نموده‌اید. برنامه را اجرا می‌کنید، اما ماژول ذکر شده کار نمی‌کند! چرا؟
چون نام رشته‌ای متناظر با کلاس StaticCache ایی که اکنون به پروژه‌ی خود اضافه کرده‌اید، با توجه به فضاهای نام پروژه‌ی جدید، کاملا دگرگون شده است. بنابراین، سؤال مهم اینجا است که این نام را بر اساس تنظیمات پروژه‌ی جاری چگونه می‌توان یافت؟
خوشبختانه دات نت فریم ورک، ابزاری توکار را برای تولید این نام رشته‌ای، به همراه دارد:
class Test
{
static void Main()
{
string name = typeof(System.Data.DataView).AssemblyQualifiedName;
Console.WriteLine(name);
}
}
خاصیت AssemblyQualifiedName ذکر شده در مثال فوق، دقیق‌ترین نامی است که می‌توانید در پروژه‌ی خود استفاده نمائید.
خروجی این مثال جهت نمایش نام رشته‌ای معادل کلاس System.Data.DataView به صورت زیر است:
System.Data.DataView, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

سؤال: از کجا متوجه شوم که رشته‌ی فوق واقعا کار می‌کند؟
مقدار متغیر name مثال فوق باید پس از بکارگیری در متد Type.GetType ، حاصلی غیر null را بازگشت دهد.
var name = typeof(System.Data.DataView).AssemblyQualifiedName;
var type = Type.GetType(name);
اگر حاصل نال بود، یعنی همان مشکلی که در ابتدای مطلب ذکر شد: ماژول مشخص شده در web.config برنامه توسط رشته‌ی مورد نظر کار نخواهد کرد.

نکته: اگر قصد معرفی اسمبلی دیگری را به برنامه دارید و این اسمبلی امضای دیجیتال دارد (strong name signature)، باید تمام اطلاعات حاصل را ذکر کنید (مانند مثال فوق که شامل Version ، Public key token و غیره است). در غیر اینصورت (عدم وجود امضای دیجیتال) ذکر دو قسمت اول خروجی خاصیت AssemblyQualifiedName کافی خواهند بود.