نظرات مطالب
طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت اول
«... به خوبی در پروژه اصلی بار گذاری میشود ...»
یعنی منوی پویای افزونه‌ی مرتبط در پروژه‌ی اصلی کار می‌کند و اضافه می‌شود و همچنین با کلیک بر روی آن، صفحه‌ی اصلی افزونه ظاهر می‌شود؟ اگر بله، یعنی مشکلی در یافتن آن نبوده‌است و مسیریابی آن اضافه شده‌است. اگر مسیریابی آن خوانده نشود، با کلیک بر روی منوی پویای آن، صفحه‌ی اصلی افزونه ظاهر نمی‌شود.
در کل بررسی کنید:
- آیا پروژه‌ی افزونه‌ای که ایجاد کردید از نوع ASP.NET MVC است یا خیر؟
- آیا فایل‌های پوشه‌ی bin آن در پوشه‌ی bin پروژه‌ی اصلی کپی شده‌اند یا خیر؟
- اگر این افزونه یک سری وابستگی اضافه‌تر دارد که در پروژه‌ی اصلی ارجاعی ندارند، این فایل‌ها هم باید در پوشه‌ی bin پروژه‌ی اصلی کپی شوند وگرنه این افزونه بارگذاری نخواهد شد.

دو مثال افزونه به همراه کدهای این پروژه  هست، سورس خودتان را با آن انطباق دهید.
نظرات مطالب
طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت سوم
قسمت post build برای کپی فایل‌ها مهم است. اگر این قسمت اجرا نمی‌شود، یعنی یا مسیرها را اشتباه وارد کرده‌اید و یا پروژه‌ی مدنظر build نمی‌شود.
- مسیرها را بررسی کنید.
- کل solution را build کنید.
- روی پروژه اصلی کلیک راست کنید و گزینه‌ی project dependencies را انتخاب کنید. در اینجا می‌شود مشخص کرد، زمانیکه این پروژه build می‌شود، چه پروژه‌هایی حتما باید build شوند (در قسمت depends on آن، تیک همه‌ی موارد را قرار دهید).
مطالب
PowerShell 7.x - قسمت ششم - ایجاد Cmdletها توسط #C
تا اینجا با کمک توابع توانستیم PowerShell را به اصطلاح extend کنیم. نوع دیگر دستورات، command letها هستند. این نوع دستورات را با کمک یک زبان دات‌نتی میتوانیم ایجاد کنیم. به این نوع دستورات complied cmdlet گفته میشود. در بیشتر مواقع با کمک advanced functionها میتوانید بیشتر کارها را انجام دهید؛ چراکه به صورت مستقیم امکان استفاده از دات‌نت را درون PowerShell دارید. اما شاید ترجیح دهید از سی‌شارپ یا دیگر زبان‌ها دات‌نتی برای ایجاد یک تابع استفاده کنید.

نحوه‌ی ایجاد یک cmdlet با کمک #C
ابتدا یک دایرکتوری جدید را ایجاد کرده و درون آن یک پروژه‌ی از نوع class library را ایجاد کنید. سپس پکیج PowerShellStandard.Library را درون پروژه ایجاد شده با کمک dotnet cli به پروژه اضافه کنید: 
mkdir ps_cmdlet_with_csharp && cd "$_"
dotnet new classlib
dotnet add package PowerShellStandard.Library
mv Class1.cs GetHelloCommand.cs
در پایان دستورات فوق، نام فایل پیش‌فرض Class1 را نیز به GetHelloCommand تغییر داده‌ایم. در ادامه محتویات فایل را اینگونه ویرایش خواهیم کرد: 
namespace ps_cmdlet_with_csharp;
using System.Management.Automation;

[Cmdlet(VerbsCommon.Get, "Hello")]
public class GetHelloCommand : PSCmdlet
{
    [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)]
    public string Name { get; set; }
    protected override void BeginProcessing()
    {
        WriteObject("Start processing");
    }
    protected override void ProcessRecord()
    {
        WriteObject("Hello " + Name);
    }
    protected override void EndProcessing()
    {
        WriteObject("End processing");
    }
}
همانطور که مشاهده میکنید، کلاس فوق را از PSCmdlet ارث‌بری کرده‌ایم. در کل برای ایجاد یک command let، از یکی از انواع Cmdlet یا PSCmdlet میتوانیم ارث‌بری کنیم. Cmdlet یک کلاس پایه است که PSCmdlet نیز از آن ارث برده است و یکسری امکانات بیشتری را جهت تعامل با PowerShell ارائه میدهد: 
using System.Collections.ObjectModel;
using System.Management.Automation.Host;

namespace System.Management.Automation
{
    public abstract class PSCmdlet : Cmdlet
    {
        protected PSCmdlet();

        public PSEventManager Events { get; }
        public PSHost Host { get; }
        public CommandInvocationIntrinsics InvokeCommand { get; }
        public ProviderIntrinsics InvokeProvider { get; }
        public JobManager JobManager { get; }
        public JobRepository JobRepository { get; }
        public InvocationInfo MyInvocation { get; }
        public PagingParameters PagingParameters { get; }
        public string ParameterSetName { get; }
        public SessionState SessionState { get; }

        public PathInfo CurrentProviderLocation(string providerId);
        public Collection<string> GetResolvedProviderPathFromPSPath(string path, out ProviderInfo provider);
        public string GetUnresolvedProviderPathFromPSPath(string path);
        public object GetVariableValue(string name);
        public object GetVariableValue(string name, object defaultValue);
    }
}
در نهایت command let باید به یک DLL تبدیل شود؛ چون همانطور که قبلآً نیز اشاره شد، هر command let در واقع یک شیء دات‌نتی است. در ادامه پروژه جاری را بیلد کرده و توسط دستور Import-Module فایل DLL تولید شده را درون Shell ایمپورت خواهیم کرد: 
PS /> dotnet build
PS /> Import-Module ./bin/Debug/net7.0/ps_cmdlet_with_csharp.dll
سپس توسط دستور Get-Command میتوانیم مطمئن شویم که ماژول موردنظر با موفقیت ایمپورت شده‌است: 
PS /> Get-Command -Module ps_cmdlet_with_csharp

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Get-Hello                                          1.0.0.0    ps_cmdlet_with_csharp
همانطور که مشاهده میکنید، درون ماژول ps_cmdlet_with_csharp تنها یک command let تعریف شده‌است. درون یک namespace میتوانیم چندین command let را تعریف کنیم. به عنوان مثال برای مثال قبل میتوانیم: 
namespace ps_cmdlet_with_csharp;
using System.Management.Automation;

[Cmdlet(VerbsCommon.Get, "Hello")]
public class GetHelloCommand : PSCmdlet
{
    // as before
}

[Cmdlet(VerbsCommon.Get, "Greetings")]
public class GetGreetingsCommand : PSCmdlet
{
    [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)]
    public string Name { get; set; }
    protected override void BeginProcessing()
    {
        WriteObject("Start processing");
    }
    protected override void ProcessRecord()
    {
        WriteObject("Greetings " + Name);
    }
    protected override void EndProcessing()
    {
        WriteObject("End processing");
    }
}
اکنون اگر پروژه را بیلد کنیم و ماژول بیلد شده را ایمپورت کنیم، با خطای زیر مواجه خواهیم شد: 
Import-Module: Invalid assembly public key.
برای رفع این مشکل، فایل csproject را باز کرده و خط زیر را به آن اضافه کنید: 
<Project Sdk="Microsoft.NET.Sdk">

  <!-- other tags -->

  <ItemGroup>
    <Compile Include="./GetHelloCommand.cs" />
  </ItemGroup>

</Project>
اکنون پروژه با موفقیت بیلد و ایمپورت خواهد شد: 
PS /> Get-Command -Module ps_cmdlet_with_csharp

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Get-Greetings                                      1.0.0.0    ps_cmdlet_with_csharp
Cmdlet          Get-Hello                                          1.0.0.0    ps_cmdlet_with_csharp

یک مثال تکمیلی
درون یک کلاس Cmdlet، امکان استفاده از تمامی annotationهایی را که در قسمت قبل بررسی کردیم، اینجا نیز در اختیار داریم؛ بنابراین نیاز به توضیح مجدد آن نیست. در ادامه میخواهیم یک دستور را با عنوان Push-SlackMessage تهیه کنیم که کار ارسال یک پیام را به یک کانال Slack، انجام میدهد: 
namespace ps_cmdlet_with_csharp;
using System.Management.Automation;
using System.Net.Http.Headers;


[Cmdlet(VerbsCommon.Push, "SlackMessage")]
[Alias("ssm")]
[OutputType(typeof(string))]
public class SlackMessageCommand : PSCmdlet
{
    [Parameter(Mandatory = true)]
    [Alias("m")]
    public string Message { get; set; }

    [Parameter(Mandatory = true)]
    [Alias("t")]
    [ValidatePattern(@"xoxp-[0-9]{11}-[0-9]{12}-[0-9]{13}-[0-9a-zA-Z]{32}")]
    public string Token { get; set; }

    [Parameter(Mandatory = true)]
    [Alias("cid")]
    public string ChannelID { get; set; }
    protected async override void ProcessRecord()
    {
        base.ProcessRecord();
        try
        {
            using var client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", this.Token);
            var values = new Dictionary<string, string>
            {
                { "channel", this.ChannelID },
                { "text", this.Message }
            };
            var content = new FormUrlEncodedContent(values);
            var response = await client.PostAsync("https://slack.com/api/chat.postMessage", content);
            var responseString = await response.Content.ReadAsStringAsync();
            WriteObject("Message sent");
        }
        catch (Exception ex)
        {
            WriteObject(ex.Message);
        }
    }
}

یک نکته در مورد ایمپورت کردن ماژول‌ها
دستوراتی که تاکنون ایجاد کردیم (هم توابع و هم compiled cmletها) برای اجرا باید یکبار درون حافظه قرار بگیرند و سپس امکان اجرای آنها را خواهیم داشت. دلیل آن نیز این است که همه چیز درون سشن جاری انجام خواهد شد و به محض بستن آن، تغییرات نیز ار حافظه خارج خواهند شد. یعنی برای command letی که ایجاد کردیم، با هربار باز کردن یک سشن جدید مجبور خواهیم بود که مجدداً آن را ایمپورت کنیم. برای رفع این مشکل میتوانیم از پروفایل‌ها استفاده کنیم. توسط پروفایل، امکان سفارشی‌سازی شل را خواهیم داشت. پروفایل در واقع یک اسکریپت PowerShell است که به محض اجرای PowerShell فراخوانی خواهد شد. بنابراین درون پروفایل این فرصت را خواهیم داشت تا متغیرها، ماژول‌ها، aliaseها و… را قبل از باز کردن شل، درون سشن PowerShell بارگذاری کنیم. PowerShell از چندین نوع پروفایل پشتیبانی میکند و توسط متغیر خودکار PROFILE$ میتوانیم لیست مسیرهای پروفایل‌ها را مشاهده کنیم:  
PS /> $PROFILE | Get-Member -Type NoteProperty | Select-Object Name, Definitionbject Name, Definition

Name                   Definition
----                   ----------
AllUsersAllHosts       string AllUsersAllHosts=/usr/local/microsoft/powershell/7/…
AllUsersCurrentHost    string AllUsersCurrentHost=/usr/local/microsoft/powershell…
CurrentUserAllHosts    string CurrentUserAllHosts=/Users/sirwanafifi/.config/powe…
CurrentUserCurrentHost string CurrentUserCurrentHost=/Users/sirwanafifi/.config/p…
برای ویرایش هر کدام از پروفایل‌های موردنظر میتوانید اینگونه عمل کنید: 
$SlackProjectPath = "/Users/sirwanafifi/Desktop/ps_cmdlet_with_csharp/bin/Debug/net7.0/ps_cmdlet_with_csharp.dll"
Import-Module $SlackProjectPath
اکنون با هر بار باز کردن PowerShell به command let جدید دسترسی خواهید داشت: 
PS /> Get-Command -Module ps_cmdlet_with_csharp

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ----
Cmdlet          Push-SlackMessage                                  1.0.0.0    ps_…
پیشنهادها
نحوه‌ی صحیح کار کردن با بوت استرپ
استفاده از فریم ورک‌های CSS هرچند امکان استفاده مجدد از کدها را به همراه یک سری از نکات پیشرفته‌ی طراحی، به صورت خودکار فراهم می‌کنند، اما ... در دراز مدت مانند یک ویروس در سراسر کدهای صفحات برنامه پخش می‌شوند. نیاز است این مساله را کنترل کرد.

منابع پیشنهادی
اشتراک‌ها
پروژه Strong Namer
 نیاز هست پروژه‌ی شما امضای دیجیتال داشته باشد و همچنین از یک کتابخانه‌ی سورس باز بدون امضای دیجیتال استفاده می‌کنید؟ چنین کاری در حالت متداول ممکن نیست. پروژه‌ی Strong Namer قصد دارد این مشکل را حل کند.
پروژه Strong Namer
مطالب
تولید MiniDump در حین کرش برنامه‌های دات نت
با مطالعه‌ی سورس‌های محصولات اخیرا سورس باز شده‌ی مایکروسافت، نکات جالبی را می‌توان استخراج کرد. برای نمونه اگر سورس پروژه‌ی Orleans را بررسی کنیم، در حین بررسی اطلاعات استثناءهای رخ داده‌ی در برنامه، متد TraceLogger.CreateMiniDump نیز بکار رفته‌است. در این مطلب قصد داریم، این متد و نحوه‌ی استفاده‌ی از حاصل آن‌را بررسی کنیم.


تولید MiniDump در برنامه‌های دات نت


خلاصه‌ی روش تولید MiniDump در پروژه‌ی Orleans به صورت زیر است:
الف) حالت‌های مختلف تولید فایل دامپ که مقادیر آن قابلیت Or شدن را دارا هستند:
    [Flags]
public enum MiniDumpType
{
    MiniDumpNormal = 0x00000000,
    MiniDumpWithDataSegs = 0x00000001,
    MiniDumpWithFullMemory = 0x00000002,
    MiniDumpWithHandleData = 0x00000004,
    MiniDumpFilterMemory = 0x00000008,
    MiniDumpScanMemory = 0x00000010,
    MiniDumpWithUnloadedModules = 0x00000020,
    MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
    MiniDumpFilterModulePaths = 0x00000080,
    MiniDumpWithProcessThreadData = 0x00000100,
    MiniDumpWithPrivateReadWriteMemory = 0x00000200,
    MiniDumpWithoutOptionalData = 0x00000400,
    MiniDumpWithFullMemoryInfo = 0x00000800,
    MiniDumpWithThreadInfo = 0x00001000,
    MiniDumpWithCodeSegs = 0x00002000,
    MiniDumpWithoutManagedState = 0x00004000
}

ب) متد توکار ویندوز برای تولید فایل دامپ
public static class NativeMethods
{
    [DllImport("Dbghelp.dll")]
    public static extern bool MiniDumpWriteDump(
        IntPtr hProcess,
        int processId,
        IntPtr hFile,
        MiniDumpType dumpType,
        IntPtr exceptionParam,
        IntPtr userStreamParam,
        IntPtr callbackParam);
}

ج) فراخوانی متد تولید دامپ در برنامه
در اینجا نحوه‌ی استفاده از enum و متد MiniDumpWriteDump ویندوز را مشاهده می‌کنید:
public static class DebugInfo
{
    public static void CreateMiniDump(
        string dumpFileName, MiniDumpType dumpType = MiniDumpType.MiniDumpNormal)
    {
        using (var stream = File.Create(dumpFileName))
        {
            var process = Process.GetCurrentProcess();
            // It is safe to call DangerousGetHandle() here because the process is already crashing.
            NativeMethods.MiniDumpWriteDump(
                            process.Handle,
                            process.Id,
                            stream.SafeFileHandle.DangerousGetHandle(),
                            dumpType,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            IntPtr.Zero);
        }
    }
 
    public static void CreateMiniDump(MiniDumpType dumpType = MiniDumpType.MiniDumpNormal)
    {
        const string dateFormat = "yyyy-MM-dd-HH-mm-ss-fffZ"; // Example: 2010-09-02-09-50-43-341Z
        var thisAssembly = Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly();
        var dumpFileName = string.Format(@"{0}-MiniDump-{1}.dmp",
                    thisAssembly.GetName().Name,
                    DateTime.UtcNow.ToString(dateFormat, CultureInfo.InvariantCulture));
 
        var path = Path.Combine(getApplicationPath(), dumpFileName);
        CreateMiniDump(path, dumpType);
    }
 
    private static string getApplicationPath()
    {
        return HttpContext.Current != null ?
            HttpRuntime.AppDomainAppPath :
            Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    }
}
متد MiniDumpWriteDump نیاز به اطلاعات پروسه‌ی جاری، به همراه هندل فایلی که قرار است اطلاعات را در آن بنویسد، دارد. همچنین dump type آن نیز می‌تواند ترکیبی از مقادیر enum مرتبط باشد.

یک مثال:
class Program
{
    static void Main(string[] args)
    {
        try
        {
            var zero = 0;
            Console.WriteLine(1 / zero);
        }
        catch (Exception ex)
        {
            Console.Write(ex);
            DebugInfo.CreateMiniDump(dumpType:
                                MiniDumpType.MiniDumpNormal |
                                MiniDumpType.MiniDumpWithPrivateReadWriteMemory |
                                MiniDumpType.MiniDumpWithDataSegs |
                                MiniDumpType.MiniDumpWithHandleData |
                                MiniDumpType.MiniDumpWithFullMemoryInfo |
                                MiniDumpType.MiniDumpWithThreadInfo |
                                MiniDumpType.MiniDumpWithUnloadedModules);
 
            throw;
        }
    }
}
در اینجا نحوه‌ی فراخوانی متد CreateMiniDump را در حین کرش برنامه مشاهده می‌کنید. پارامترهای اضافی دیگر سبب خواهند شد تا اطلاعات بیشتری از حافظه‌ی جاری سیستم، در دامپ نهایی قرار گیرند. اگر پس از اجرای برنامه، به پوشه‌ی bin\debug مراجعه کنید، فایل dmp تولیدی را مشاهده خواهید کرد.


نحوه‌ی بررسی فایل‌های dump


الف) با استفاده از Visual studio 2013

از به روز رسانی سوم VS 2013 به بعد، فایل‌های dump را می‌توان داخل خود VS.NET نیز آنالیز کرد (^ و ^ و ^). برای نمونه تصویر ذیل، حاصل کشودن فایل کرش مثال فوق است:


در اینجا اگر بر روی لینک debug managed memory کلیک کنید، پس از چند لحظه، آنالیز کامل اشیاء موجود در حافظه را در حین تهیه‌ی دامپ تولیدی، می‌توان مشاهده کرد. این مورد برای آنالیز نشتی‌های حافظه‌ی یک برنامه بسیار مفید است.


ب) استفاده از برنامه‌ی Debug Diagnostic Tool

برنامه‌ی Debug Diagnostic Tool را از اینجا می‌توانید دریافت کنید. این برنامه نیز قابلیت آنالیز فایل‌های دامپ را داشته و اطلاعات بیشتری را پس از آنالیز ارائه می‌دهد.


برای نمونه پس از آنالیز فایل دامپ تهیه شده توسط این برنامه، خروجی ذیل حاصل می‌شود:



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
MiniDumpTest.zip
اشتراک‌ها
باید از View Componentها به جای Partial Viewها در ASP.NET Core استفاده کنید

Why use View Components and not Partial Views? The biggest reason is that when inserting a Partial View into a Razor page, all the ViewData associated with the calling View is automatically associated with the Partial View. This means that a Partial View may behave very differently on one Razor page than on another. With View Components, you control what gets shared to your View Components. 

باید از View Componentها به جای Partial Viewها در ASP.NET Core استفاده کنید