<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های نگار قاسمی</title>
        <link>https://virgool.io/feed/@m_20671798</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-04-14 17:56:09</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/2165852/avatar/YHDjrv.jpg?height=120&amp;width=120</url>
            <title>نگار قاسمی</title>
            <link>https://virgool.io/@m_20671798</link>
        </image>

                    <item>
                <title>خودروهای نوستالژیک قدیمی در ایران، از ژیان تا بنز کلاسیک</title>
                <link>https://virgool.io/KhodroRadar/%D8%A7%D8%B2-%D8%AE%D9%88%D8%AF%D8%B1%D9%88%D9%87%D8%A7%DB%8C-%D9%82%D8%AF%DB%8C%D9%85%DB%8C-%DA%86%D9%87-%D8%AE%D8%A8%D8%B1-m46gznlsqpuv</link>
                <description>به گفته بعضی  منابع، در حدود ۱۲۳ سال پیش اولین خودرو توسط میرزا علی‌اکبر خان مشیرالدوله، سفیر ایران در آلمان به ایران آمد. بعد ها محمد علی شاه قاجار برای استفاده شخصی چند خودرو وارد ایران کرد. در دوران پهلوی خودروها از برند های مختلف مانند فورد، شورلت، بی‌ام‌و، مرسدس بنز، رنو، پژو و سیتروئن وارد ایران شدند.برخی از این خودروها به دلیل زیبایی، استحکام، سرعت، اقتصادی بودن یا حتی عدم اقتصادی بودن، محبوبیت خاصی پیدا کردند و به خاطره و نوستالژی بسیاری از ایرانیان تبدیل شدند.بعضی از این خودروهای قدیمی هنوز در بازار ایران بین علاقه مندان به خودروهای قدیمی دست به دست می‌شوند.در این مقاله به بررسی قیمت چند خودروی نوستالژیک قدیمی  پرداخته ایم. قیمت خودروها بر اساس گزارش خودرو رادار  در دو ماه اخیر از آگهی های سایت های مرتبط با آگهی خودرو به دست آمده است.ژیانژیان:ژیان خودروی کوچک اقتصادی بود که برای استفاده خانواده‌ ‌های کم جمعیت تولید شده بود. تولید این خودرو در ایران از سال ۱۳۴۷ در شرکت سایپا آغاز شد. پایین بودن حجم (قدرت) موتور ژیان موجب شده بود که این خودرو از توان لازم برای حرکت در خیابان‌های سربالایی برخوردار نباشد. این خودرو در بازار ایران با میانگین حدود ۳۰۰ میلیون تومان معامله می‌شود.فولکس واگن بیتل( فولکس قورباغه ای)فولکس واگن بیتل( فولکس قورباغه ای):فولکس واگن بیتل یکی از خودروهای نوستالژیکی است که در دهه چهل به ایران رسید و خیلی زود به یکی از پرفروش‌ترین‌ها در تاریخ خودرو ایران تبدیل شد. نمونه های کمی از این خودرو در بازار ایران هست که به طور میانگین در حدود ۱۲۰ میلیون تومان معامله می‌شود.رنو ۵رنو ۵:رنو ۵ هاچبک خودروی جمع و جور شهری است که اولین سری آن در سال‌های ۵۵ و ۵۶ وارد ایران شد. این خودروی نوستالژیک قدیمی به با میانگین قیمتی ۲۷۰ میلیون تومان معامله می‌شود.پیکانپیکان: پیکان یک خودروی سواری است که از سال ۱۳۴۶ تا ۱۳۸۴ توسط ایران خودرو تولید می‌شد. پیکان به دلیل زیبایی، استحکام، سرعت و اقتصادی بودن، محبوبیت خاصی در میان مردم پیدا کرد . پس از ۳۸ سال و تولید بیش از ۲ میلیون دستگاه، تولید پیکان در سال ۱۳۸۴ متوقف شد و جای خود را به خودروهای جدیدتر داد. این خودرو در بازار با میانگین قیمت ۲۰۰ میلیون تومان معامله می‌شود.پژو ۵۰۴پژو ۵۰۴:پژو ۵۰۴ در ایران نیز محبوبیت زیادی داشت و از سال ۱۳۴۹ تا ۱۳۶۹ توسط شرکت ایران خودرو مونتاژ و تولید می‌شد. این خودرو به دلیل کم خرج و کم مصرف بودن، مناسب برای قشر متوسط بود. این خودرو در بازار با میانگین قیمت ۲۰۰ میلیون تومان معامله می‌شود.لندرورلندرور:این خودرو از  سال ۱۳۳۶ و توسط شرکت مرتب خودرو وارد ایران شد.  این خودرو به جیپ انگلیسی معروف است. امروز انواع مختلف لندرورهای قدیمی با میانگین قیمت ۲۳۳ میلیون تومان در بازار معامله می‌شود.بنز کلاسیکبنز کلاسیک:بنز کلاسیک در سال ۱۹۵۰ تا ۱۹۷۲ توسط شرکت مرسدس بنز تولید می‌شد. این خودرو در ایران نیز محبوبیت زیادی داشت و از سال ۱۳۳۰ تا ۱۳۵۰ توسط شرکت ایران خودرو مونتاژ و تولید می‌شد. این خودرو امروز با میانگین قیمت ۵۰۰ میلیون تومان معامله می‌شود.</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Fri, 15 Dec 2023 11:01:25 +0330</pubDate>
            </item>
                    <item>
                <title>مدیریت اجرای کارها با Hangfire</title>
                <link>https://virgool.io/@m_20671798/%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA-%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1%D9%87%D8%A7-%D8%A8%D8%A7-hangfire-fstx9rcdj8gk</link>
                <description>آیا تا به حال برایتان پیش آمده است که نیاز داشته باشید یک سری کارهای پس زمینه را در .Net هندل کنید؟ احتمالا نیاز داشته باشید یک سری جاب را Schedule کنید که در زمان های مشخصی کارهای مشخصی انجام دهند.برای هندل کردن این کارها یک راه کار ساده و مفید وجود دارد به نام Hangfire. Hangfire یک فریم‌ورک open-source است که به توسعه‌دهندگان در اجرای، تنظیم و مدیریت background tasks در هر برنامه‌ای .NET کمک می‌کند. Hangfire همچنین پشتیبانی از هر دو .NET Framework و .NET Core را دارد.// Scheduling a background job using Hangfire
BackgroundJob.Enqueue(() =&gt; Console.WriteLine(&amp;quotWelcome to the Hangfire World!&amp;quot));در این مثال ساده، از Enqueue method برای اجرای یک متد به صورت asynchronous در background استفاده شده است.علاوه بر سادگی این ویژگی های بسیار جالب نیز در Hangfire قابل توجه است:ثبات کار: Hangfire از ذخیره ساز پایدار استفاده می کند و وظایف را برای پردازش ذخیره می کند. این وظایف را امن نگه داشته و در برابر بازسازی های فرآیند، راه انداز های دستگاه، یا هر شکست غیرمنتظره دیگر،      حساس نمی شود.سعي خودکار: Hangfire وظایف ناموفق را خودکار بازسازي مي كند، بنابراین شما نيازي نداريد استثناء ها را به صورت دستي كنترل كنيد.داشبورد UI: داشبورد شفاف آن نمایشگر اجرای بصری وظایف زمان بندی شده، پردازش، موفق و شکست خورده است.پشتيباني از عبارات CRON: برای زمان بندي پيچيده، حتي عبارات CRON را پشتيباني مي كند// An example of scheduling recurring email notifications job using Hangfire
RecurringJob.AddOrUpdate(() =&gt; EmailService.SendNotifications(), Cron.Daily);در این قطعه کد Hangfire ، AddOrUpdate EmailService.SendNotifications()  را برای اجرای به عنوان یک کار روزانه تکرار شونده زمانبندی می کند. از کلاس Cron برای بیان زمانبندی های پیچیده به عنوان عبارات ساده Cron استفاده می کند.معماری Hangfire.NETجاب ها در Hangfire نسخه‌بندی می‌شوند، دوباره امتحان می‌شوند، در صف قرار می‌گیرند و بینworker های موجود توزیع می‌شوند.  بیایید اجزای معماریHangfire را ببینیم:نوبت دهی(Enqueuing): هنگامی که یک job ایجاد می شود، به صف ها ارسال می شود.اجرا (Execution):worker ها به طور مداوم Jobها را در این صف ها چک می کنند.تلاش مجدد(Retry): اگر کاری با شکست مواجه شود، به صف تلاش های مجدد منتقل می شود و بعداً تلاش می شود.نسخه‌سازی(Versioning): نسخه‌های قدیمی‌تر Job ها برای یک فرآیند مقیاس‌بندی حفظ می‌شوند.نصب و پیکربندی Hangfire در دات نتپس از نصب بسته Hangfire NuGet، کد زیر را به Startup.cs اضافه کنید:public void Configuration(IAppBuilder app)
{
// Configuring Hangfire job storage. Let&#039;s use SQL Server.
GlobalConfiguration.Configuration.UseSqlServerStorage(&amp;quot&lt;connection_string&gt;&amp;quot);

// Setting up Hangfire Dashboard and Server in a .NET application
app.UseHangfireDashboard();
app.UseHangfireServer();
}به &lt;your_app_url&gt;/hangfire  بروید تا داشبورد داخلی Hanfire را ببینید.آشنایی با ویژگی‌هایHangfire.NETاز ویژگی های Hangfire.NET می توان به موارد زیر اشاره کرد:تلاش مجدد خودکار(Automatic Retry) : Hangfire به طور خودکار کارهای ناموفق را بر اساس یک الگوریتم back-off  دوباره امتحان می کند.فهرست‌های سرور(Server Lists) : سرورهای Hangfire به‌طور خودکار به کلاستر پیوسته یا به صورت پویا از آن خارج می‌شوند تا دسترسی بالا را حفظ کنند.ادامه کار(Job Continuations): با استفاده از عبارتContinueWith می توانید مطمئن شوید که Job B بعد از Job A اجرا می شود.فیلترهای Job پیشرفته (Advanced Job Filters): فیلترهای سفارشی Job ها به شما این امکان را می دهند که رویداد مورد نظر را برای موارد موفقیت و شکست اضافه کنید.نحوه قرار دادن یک کار پس‌زمینه و به دنبال آن کار ادامه‌دار را بررسی کنید:// Let&#039;s queue a background job and a continuation job
var jobId = BackgroundJob.Enqueue(() =&gt; Console.WriteLine(&amp;quotBackground job&amp;quot));
BackgroundJob.ContinueWith(jobId, () =&gt; Console.WriteLine(&amp;quotContinuation job&amp;quot));پیکربندیHangfire در یک پروژه C#// Setting up Hangfire in Startup.cs
public void Configuration(IAppBuilder app)
{
app.UseHangfire(config =&gt; config.UseSqlServerStorage(&amp;quot&lt;your connection string&gt;&amp;quot));
app.UseHangfireDashboard();
app.UseHangfireServer();
}در قطعه کد داده شده، ما Hangfire را با نمونه SQL Server خود پیکربندی می کنیم، داشبورد Hangfire را برای نظارت تنظیم می کنیم و سرور Hangfire را راه اندازی می کنیم.ویژگی های کلیدیHangfire Cویژگی های Hangfire در C#به صورت زیر هستند:تلاش‌های مجدد خودکار: گاهی اوقات وظایف با شکست مواجه می‌شوند، اما Hangfire تضمین می‌کند که آنها شانس دیگری برای موفقیت خواهند داشت. مدیریت استثناها: Hangfire می‌تواند استثنائات را به‌خوبی شناسایی و مدیریت کند و برنامه شما را از خرابی احتمالی نجات دهد.اولویت بندی کارها: از آنجایی که همه وظایف برابر نیستند وبرخی باید با اولویت بالاتری اجرا شوند، Hangfire به ما برای رسیدن به این موضوع کمک می‌کند.. استراتژی‌های شکست سفارشی: اگر یک کار شکست بخورد، شما تصمیم می‌گیرید چه اتفاقی بیفتد. کارهای برنامه ریزی شده: روزانه، هفتگی یا یک بار در ماه آبی، کارهای خود را با سهولت برنامه ریزی کنید.مسائل رایج در Hangfire  و راه حل های آنهاکار تکرارشونده متوقف شده (Stalled Recurring Job ):  معمولاً ناشی از یک خطا در خود Job است . بررسی گزارش استثنا در داشبورد Hangfire معمولاً به مشکل اشاره می کند.//Recurring Job Setup
RecurringJob.AddOrUpdate(() =&gt; Console.Write(&amp;quotDaily&amp;quot), Cron.Daily);
//Let&#039;s assume an exception occurs
RecurringJob.AddOrUpdate(() =&gt; throw new Exception(&amp;quotOops!&amp;quot), Cron.Daily);در این مثال، کار به دلیل یک استثنا با شکست مواجه می شود و Hangfire اجرای آن را تا رفع مشکل به حالت تعلیق در می آورد.صف پردازش نشده (Unprocessed Queue) : در صورت وجود پیکربندی نادرست در سرور Hangfire، صف های سفارشی Hangfire می توانند پردازش نشده باقی بمانند.این مورد به راحتی از طریق یک تنظیم صحیح قابل اجتناب است.//Incorrect Server Setup
var options = new BackgroundJobServerOptions
{
Queues = new[] { &amp;quotcritical&amp;quot, &amp;quotdefault&amp;quot }
};
app.UseHangfireServer(options);این سرور کارها را در یک صف سفارشی به نام&quot;emailSend&quot; پردازش نمی کند. برای رفع آن، &quot;ارسال ایمیل&quot; را در لیست صف قرار دهید.کارهای ناموفق گیر کرده در وضعیت تلاش مجدد: Hangfire  کارهای ناموفق را به طور خودکار دوباره امتحان می کند. با این حال، ممکن است مواردی وجود داشته باشد که جاب ها در حالت تلاش مجدد باقی بمانند. دلیل آن می تواند یک استثناء کنترل نشده در کد جاب شما باشد.نکاتی برای اشکال زدایی کارآمد در Hangfireلاگ گذاری: Hangfire از مکانیسم‌های ثبت داخلی مانند log4net، NLog و Serilog پشتیبانی می‌کند. از این موارد به نفع خود برای نظارت جامع استفاده کنید رسیدگی به استثناها: استثناهای کنترل نشده پردازش جاب شما را مختل می کند. در نظر بگیرید که کدهای اجرای کار خود را در بلوک‌هایtry-catch بپیچید تا از پردازش یکنواخت کار اطمینان حاصل کنید.try
{
BackgroundJob.Enqueue(() =&gt; Console.Write(&amp;quotNo Errors Here!&amp;quot));
}
catch (Exception ex)
{
Console.WriteLine($&amp;quotOops, something went wrong: {ex.Message}&amp;quot);
}از داشبورد استفاده کنید: داشبورد Hangfire  شامل ابزارهای کمکی برای اشکال زدایی و ردیابی پیشرفت Job ، Job های  ناموفق و تلاش های مجدد است.از شیوه های کدنویسی خوب استفاده کنید: مانند همیشه، پیروی از شیوه های کدنویسی خوب به جلوگیری از ورود اشکالات به Job  شما کمک می کند. داده های ورودی خود را به شدت تأیید کنید و خطاها را پیش بینی کنید.نگهداری و نظارت بر جاب ها درHangfireداشبورد Hangfire مانند یک اتاق کنترل است که به تمام نیازهای شما برای نظارت و اعمال قدرت بر روی Job های خود مجهز است. در اینجا چند نکته برای استفاده موثر از آن وجود دارد:همیشه نمودار Job ها را چک کنید: این نمودار میزان موفقیت و شکست ها، در صورت وجود، ارائه می دهد.صف‌ها را بررسی کنید: اگر به نظر می‌رسد کارها گیر کرده‌اند، وضعیت صف ممکن است اشاره‌ای به شما داشته باشد.مشاغل ناموفق: Hangfire  ، Job های شکست خورده را به طور متفاوتی طبقه بندی می کند. برای رفع و از سرگیری آن‌ها، استثناهایی را که در مشاغل «ناموفق» با آن مواجه می‌شوند، بررسی کنید.مقیاس‌بندی با Hangfire – پردازش کار پس‌زمینه توزیع‌شده// Setting up distributed Hangfire servers across different machines
services.AddHangfire(configuration =&gt; configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(Configuration.GetConnectionString(&amp;quotHangfireConnection&amp;quot),
new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
JobExpirationCheckInterval = TimeSpan.FromHours(1),
CountersAggregateInterval = TimeSpan.FromMinutes(5),
PrepareSchemaIfNecessary = true,
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
UsePageLocksOnDequeue = true,
DisableGlobalLocks = true
}));این نمونه ای از نحوه راه اندازی یک سیستم سرورHangfire توزیع شده است . تابع UseSqlServerStorage اتصال به SQL Server را پیکربندی می کند وDisableGlobalLocks قفل های توزیع شده را برای سرورها تسهیل می کند.جنبه های امنیتی استفاده از Hangfireاما امنیت دسترسی به دشبورد Hangfire چطور است؟// Set up strict Dashboard access authorization rules
app.UseHangfireDashboard(&amp;quot/hangfire&amp;quot, new DashboardOptions
{
DashboardTitle = &amp;quotMy Hangfire Dashboard&amp;quot,
Authorization = new[]
{
new HangfireCustomAuthorizeFilter()
}
});

public class HangfireCustomAuthorizeFilter: IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
// Check user authorization here, return true if authorized
return HttpContext.Current.User.IsInRole(&amp;quotAdmin&amp;quot);
}
}این قطعه کد داشبورد Hangfire را پیکربندی می‌کند و دارای یک فیلتر مجوز است. این تضمین می کند که فقط کاربرانی که در نقش &quot;Admin&quot; هستند می توانند به داشبورد دسترسی داشته باشند.پیشرفت ها و برنامه های افزودنی در Hangfireهنگ فایر چیزی بیش از یک ابزار است. این یک زمین بازی برای توسعه دهندگان است. با افزونه های متعددی که در اختیار دارید، می توانید Hangfire را مطابق با نیازهای خاص خود تنظیم کنید. می خواهید ببینید چگونه انجام می شود؟// Logging job events using Hangfire.Console extension
BackgroundJob.Enqueued += (sender, args) =&gt;
{
Log.Information($&amp;quotJob {args.JobId} has been enqueued&amp;quot);
};

// Sample job using Hangfire.Console
BackgroundJob.Enqueue(() =&gt; ConsoleJob());
public void ConsoleJob(PerformContext context)
{
context.WriteLine(&amp;quotHello, Hangfire Console!&amp;quot);
}در این مثال، از پسوند Hangfire.Console برای ثبت رویدادهای Job ها به کنسول استفاده کردیم. ConsoleJob  در حال ثبت یک پیام تبریک هنگام اجرا است.مشاهده نسخه کامل مقاله در اینجا</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Tue, 26 Sep 2023 07:50:52 +0330</pubDate>
            </item>
                    <item>
                <title>چگونه به صورت کارآمد یک آرایه را به صورت تصادفی در C# مرتب کنیم؟!</title>
                <link>https://virgool.io/@m_20671798/%DA%86%DA%AF%D9%88%D9%86%D9%87-%D8%A8%D9%87-%D8%B5%D9%88%D8%B1%D8%AA-%DA%A9%D8%A7%D8%B1%D8%A2%D9%85%D8%AF-%DB%8C%DA%A9-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87-%D8%B1%D8%A7-%D8%A8%D9%87-%D8%B5%D9%88%D8%B1%D8%AA-%D8%AA%D8%B5%D8%A7%D8%AF%D9%81%DB%8C-%D8%AF%D8%B1-c-%D9%85%D8%B1%D8%AA%D8%A8-%DA%A9%D9%86%DB%8C%D9%85-u7usdflpo18p</link>
                <description>در این مقاله، روش‌‌های مختلف تصادفی کردن آرایه را با هم بررسی کردیم.در ابتدا یک متد ایجاد کرده‌ایم که در ورودی یک عدد صحیح به عنوان طول آرایه دریافت می‌کند. و در بدنه ی متد یک آرایه از اعداد صحیح با ترتیب صعودی می‌سازد.public static class ArrayFunctions
{
      public static int[] GetOrderedArray(int numberOfElements)
     { 
            var array = new int[numberOfElements];
             for (int i = 0; i &lt; numberOfElements; i++)
             { 
                     array[i] = i;
             } 
              return array;
      }
}رندم کردن آرایه با استفاده از LINQابزار LINQ یکی از قدرتمندترین امکاناتی است که در اختیار ما در C# قرار دارد. این ابزار به ما روش‌های مختلفی برای کنترل داده‌ها را فراهم می‌کند.معمولاً از آن برای پرس و جو یا فیلتر کردن مجموعه داده ها استفاده می کنیم، اما می توانیم از آن برای تصادفی سازی نیز استفاده کنیم. برای مرتب کردن داده‌ها در LINQ از متد OrderBy استفاده می‌کنیم. این متد داده‌ها را بر اساس کلید ورودی مرتب می‌کند.رندم کردن به وسیله GUIDما یک متد جدید RandomizeWithOrderByAndGuid ایجاد کرده‌ایم. این متد به طور مستقیم آرایه ورودی را با استفاده از Guid.NewGuid() مرتب می کند. در این شیوه Guid.NewGuid() هر بار یک GUID  جدید و منحصر به فرد تولید می‌کند و باعث می‌شود که اعضای ارایه به صورت تصادفی چیده شوند.public static int[] RandomizeWithOrderByAndGuid(int[] array) =&gt;
‍           array.OrderBy(x =&gt; Guid.NewGuid()).ToArray();رندم کردن آرایه با یک کلاس رندمیک متد جدید دیگر به اسم RandomizeWithOrderByAndRandom ایجاد کردیم. در اینجا از متدNext از Random.Shared برای مرتب کردن آرایه استفاده کردیم. این متد هر بار بک عدد تصادفی تولید می‌کند و باعث می‌شود که آرایه به صورت تصادفی مرتب شود.public static int[] RandomizeWithOrderByAndRandom(int[] array) =&gt;
       array.OrderBy(x =&gt; Random.Shared.Next()).ToArray();.رندم کردن ارایه درC# با استفاده از الگوریتم Fisher-Yatespublic static int[] RandomizeWithFisherYates(int[] array)
{
         int count = array.Length;
         while (count &gt; 1)
         { 
                  int i = Random.Shared.Next(count--);
                 (array[i], array[count])= (array[count], array[i]);
         }
          return array;
}ما متد RandomizeWithFisherYates ایجاد می کنیم که یک آرایه صحیح را به عنوان ورودی می گیرد.در داخل متد، متغیر count را با طول آرایه ورودی مقداردهی اولیه می کنیم. در هر تکرار حلقه  while، یک عدد تصادفی تولید می شود و عنصر مربوط به ایندکس عدد تولید شده تصادفی در آرایه با عنصر اشاره شده توسط متغیرcount با استفاده ازTuple جابجا می شود. این کار تا زمانی ادامه پیدا می کند که count برابر 1 شود. در نهایت، آرایه تغییر یافته را برمی گردانیم.این روش به صورت مستقیم روی آرایه ورودی عمل می کند و آن را برمی گرداند، در حالی که روش های OrderBy یک کپی از آرایه را برمی گردانند.نسخه دوم الگوریتم Fisher-Yates :public static int[] RandomizeWithFisherYatesCopiedArray(int[] array)
{
          int count = array.Length;
          var arrayCopy = new int[count];
          Array.Copy(array, arrayCopy, count);
          while (count &gt; 1)
          {
                   int i = Random.Shared.Next(count--);
                   (arrayCopy[i], arrayCopy[count]) = (arrayCopy[count], arrayCopy[i]);
           }
            return arrayCopy;
}این روش تقریباً شبیه به متد اصلی RandomizeWithFisherYates است با این استثنا که از روش Array.Copy برای کپی آرایه ورودی استفاده می کنیم. در بخش دیگری از متد، با متغیرarrayCopy کار می کنیم و پس از پایان کار آن را برمی گردانیم.تست رندم کردن آرایهبعد از داشتن روش های مختلف تصادفی سازی سعی می‌کنیم که روش ها را تست کنیم.public class ArrayFunctionsTests
{
        private readonly int[] _array;
        private const int ARRAY_SIZE = 1000;
        public ArrayFunctionsTests()
         {
                 _array = ArrayFunctions.GetOrderedArray(ARRAY_SIZE);
         }
}در داخل متد تست، از متدRandomizeWithOrderByAndGuid کلاسArrayFunctions برای تصادفی سازی متغیر_array استفاده می کنیم.نتیجه را در متغیر randomizedArray  ذخیره می کنیم. سپس با استفاده از کتابخانه  FluentAssertions، ادعا می کنیم که متغیر randomizedArray نباید خالی باشد، نباید به صورت صعودی و نیز به صورت نزولی مرتب شود.به این روش، اطمینان حاصل می کنیم که متدRandomizeWithOrderByAndGuid() با موفقیت یک ترتیب متفاوت از آرایه اصلی تولید می کند.[Fact]
public void WhenRandomizeWithOrderByAndGuidIsInvoked_ThenArrayIsRandomized()
{
            // Act
            var randomizedArray = ArrayFunctions.RandomizeWithOrderByAndGuid(_array);
            // Assert
            randomizedArray.Should()
                       .NotBeNullOrEmpty()
                       .And.NotBeInAscendingOrder()
                      .And.NotBeInDescendingOrder();
}برای تست سه روش دیگری که در این مقاله ایجاد کرده ایم، می توانیم با استفاده از همین روش عمل کنیم .ملاحظات عملکردی هنگام تصادفی سازی یک آرایه در C#برای تشخیص اینکه کدام روش پرفورمنس بهتری دارد از BenchmarkDotNet برای اندازه گیری عملکرد و تخصیص حافظه استفاده کنیم:بنچمارکی که ما استفاده می کنیم، زمان اجرای میانگین، خطا، انحراف استاندارد و تخصیص حافظه را برای هر روش اندازه گیری می کند.روش RandomizeWithFisherYates تا حد زیادی بهترین عملکرد را دارد و با زمان اجرای حدود 1،400 میکروثانیه، در رتبه اول قرار دارد و تقریباً هیچ تخصیص حافظه ای ندارد.نسخه اصلاح شده الگوریتم Fisher-Yates ،  RandomizeWithFisherYatesCopiedArray) در رتبه دوم قرار دارد و با 1،500 میکروثانیه، عملکرد بسیار خوبی دارد. به دلیل کپی کردن آرایه ورودی، تخصیص حافظه بیشتری نسبت به نسخه اصلی دارد.سپس، روش RandomizeWithOrderByAndRandom با زمان اجرای حدود 17،000 میکروثانیه و تخصیص حافظه قابل توجه بالاتر نسبت به دو روش قبل - در رتبه سوم قرار دارد.و در نهایت، RandomizeWithOrderByAndGuid با زمان اجرای حدود 26،000 میکروثانیه. در میان چهار روش، در رتبه آخر قرار دارد و همچنین بالاترین تخصیص حافظه را دارد.مقاله اصلی را میتوانید در اینجا ببینید.</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Wed, 02 Aug 2023 09:41:54 +0330</pubDate>
            </item>
                    <item>
                <title>C#12 Preview!</title>
                <link>https://virgool.io/@m_20671798/c12-preview-m83ayxpcb82k</link>
                <description>سی‌شارپ آپدیت های خود را در ادامه فعالیت هایش جهت ساده تر کردن کد ها و بهبود در فضاهای اشغال شده توسط کد ارائه می‌کند. برخی از ویژگی های C# 12 در preview ها معرفی شده اند. می‌توانید این ویژگی‌ها را با استفاده از Visual Studio 17.6 Preview   یا NET 8 preview.  امتحان کنید.ویژگی های زیر در C#12 Preview معرفی شده اند. ۱- ویژگی Primary constructors برای کلاس ها و ساختارهای غیر رکورد.  شما اکنون می‌توانید primary constructors را در هر کلاس و ساختار ایجاد کنید. Primary constructors دیگر محدود به record types نیستند. پارامترهای primary constructor در scope بدنه کلاس هستند.با استفاده از (...)this داخل کلاس، می‌توانید از قابلیت primary constructor استفاده کنید.مثال:Public Student(int id, string name) : this(id, name, Enumerable.Empty&lt;decimal&gt;()) { } اضافه کردن primary constructor به یک کلاس، از تعریف ضمنی constructor بدون پارامتر جلوگیری می‌کند. ۲- مقدار پیش فرض برای پارامترهای عبارت lambdaسی‌شارپ قبلا این امکان را فراهم کرده بود که برای پارامتر های ورودی متد مقادیر پیشفرض تعریف کنید. این امکان برای lambda نیز فراهم شده است. مقدار پیش فرض برای پارامترهای عبارت lambda به شما این امکان را میدهد که بتوانید پارامترهایی را که در عبارت lambda استفاده میشوند، با مقدار پیش فرض مقداردهی کنید.۳- استفاده از alias برای هر نوعی در ورژن های قبلی سی شارپ این امکان وجود داشت که شما موقع استفاده از یک کلاس، به جای استفاده مستقیم از آن کلاس، در قسمت using ها، کلاس مورد نظر خود را به صورت مستقیم using کنید و به آن یک نام دلخواه دهید، سپس داخل کد خود برای دسترسی به کلاس مورد نظر از این نام دلخواهی که موقع using معرفی کرده اید استفاده کنید.در این ورژن، شما می‌توانید از دستورالعمل using alias برای  هر نوعی، نه فقط named types، استفاده کنید. این بدان معناست که شما می‌توانید alias های semantic برای tuple types، array types، pointer types یا دیگر unsafe types ایجاد کنید.مثال:using Measurement = (string Units, int Distance);? نسخه کامل این مقاله را ‌می‌توانید در اینجا مطالعه کنید. </description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Thu, 22 Jun 2023 22:20:06 +0330</pubDate>
            </item>
                    <item>
                <title>دیباگ راحت تر با DebuggerDisplayAttribute!</title>
                <link>https://virgool.io/@m_20671798/%D8%AF%DB%8C%D8%A8%D8%A7%DA%AF-%D8%B1%D8%A7%D8%AD%D8%AA-%D8%AA%D8%B1-%D8%A8%D8%A7-debuggerdisplayattribute-esc9khop0k4q</link>
                <description>یکی از کار‌های معمول برای یک دولوپر دیباگ کد ها و تحلیل داده‌ها در زمان اجرای کد‌ها می‌باشد.این موضوع زمانی پیچیده‌تر می‌شود که بخواهید مقدار یک مورد خاص را در مجموعه‌ای حاوی چند صد مورد پیدا کنید.یکی از راه‌هایی که بتوانیم اطلاعات کافی مورد نیاز از یک آبجکت را در حین دیباگ به دست آوریم override کردن متد ToString کلاس است!در این روش دو نکته وجود دارد:۱- از آنجایی که ما متد ToString را override کرده‌ایم، باعث می شود Expression Evaluator تابعی را فراخوانی کند که می تواند باعث کند‌تر شدن دیباگ داده‌ها باشد.۲- نمیتوانیم داده‌های متفاوتی برای دیباگ و ToString داشته باشیم. بنابراین، بهترین راه برای نمایش مقدار مورد نظر داده های شی در زمان اجرا به روشی ساده و معنی دار چیست؟ اتریبیوت DebuggerDisplay  اینجا به کمک می‌آید.استفاده از ویژگی DebuggerDisplayAttribute به Expression Evaluator دستور می دهد تا عبارت ارائه شده را ارزیابی کند و مقدار حاصل را در پنجره دیباگ نمایش دهد. در constructor یک رشته را به عنوان ورودی قبول می‌کند و در آن رشته میتوان هر کدام از پراپرتی های کلاس مورد نظر را استفاده کرد.برای نمونه تکه کد زیر را ببینید.[DebuggerDisplay(&amp;quotName: {FirstName } - {LastName }&amp;quot)] 
public class Customer 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; }
    public Customer(string firstName, string lastName) 
    { 
          FirstName = firstName; 
          LastName = lastName; 
    } 
} این اتریبیوت به آسان‌تر کردن فرآیند اشکال‌زدایی کمک می‌کند، مخصوصاً در مواردی که نوع پیچیده‌ای وجود دارد که انواع دیگر را در بر می‌گیرد و مقادیری که ما به آن‌ها علاقه داریم در سطوح مختلف تودرتو هستند که وقتی بخشی از یک مجموعه باشد، پیچیده‌تر می‌شود.</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Wed, 17 May 2023 19:05:14 +0330</pubDate>
            </item>
                    <item>
                <title>یک .Net و این همه تایمر!</title>
                <link>https://virgool.io/@m_20671798/%DB%8C%DA%A9-net-%D9%88-%D8%A7%DB%8C%D9%86-%D9%87%D9%85%D9%87-%D8%AA%D8%A7%DB%8C%D9%85%D8%B1-by4trva9m6td</link>
                <description>۶ کلاس مختلف تایمربرای .Net وجود دارد. هر تایمر کاربرد مخصوص خودش را دارا می‌باشد.تایمر‌های زیر مخصوص اجرای کد در thread  های مخصوص UI می‌باشد.System.Windows.Forms.TimerSystem.Windows.Threading.DispatcherTimerاین تایمر ها callback  را در UI thread  اجرا می‌کنند. در هر دوی این موارد وقتی در سمت UI  یک ایونت Raise  می‌شود، تنها یک Callback  در لحظه اتفاق می‌افتد. بنابراین Thread safe  هستند.تایمر دیگری که برای وب فرم ها وجود دارد، System.Web.UI.Timer است. این تایمر یک رویداد postback در سرور ایجاد می کند.در نهایت سه تایمر دیگر وجود دارد که مخصوص UI نیستند:System.Threading.TimerSystem.Threading.PeriodicTimerSystem.Timers.Timerساده ترین و ابتدایی ترین نوع تایمر System.Threading.Timer می‌باشد. این تایمر  Callbackرا در ThreadPool برنامه ریزی می کند. اگر اجرای handler در زمان بیشتری نسبت به بازه زمانی مشخص شده برای اجرا طول بکشد، handler دوباره اجرا می شود و در نهایت با چندین handler در حال اجرا به صورت موازی مواجه خواهید شد.تایمرSystem.Timers.Timer به صورت داخلی از System.Threading.Timerاستفاده می کند و دارای چند ویژگی دیگر مانند AutoReset، Enabled، یا SynchronizingObject می‌باشد که امکان پیکربندی نحوه اجرای Callback ها را فراهم می کند. همچنین، رویداد Tick اجازه می دهد تا چندین  handler را ثبت کنید. بنابراین، یک تایمر می تواند چندین handler را فعال کند. همچنین می توانید پس از راه اندازی تایمر، handler را تغییر دهید.آخرین تایمر اضافه شده به کتابخانه های دات نت  System.Threading.PeriodicTimer است. هدف اصلی این تایمر استفاده در حلقه ها و پشتیبانی از رویداد های async است. این یک رویداد Tick ندارد، اما دارای یک متد به نام WaitForNextTickAsync می‌باشد. این متد یک &lt;ValueTask&lt;bool  برمی گرداند که وقتی تیک بعدی آماده شد تکمیل می شود. مقدار bool نشان می دهد که آیا تایمر از بین رفته است یا خیر. بنابراین، می توانید از آن در یک حلقه while استفاده کنید. به لطف این طراحی، callback ها دچار overlap نمی‌شوند.منبع</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Fri, 21 Apr 2023 16:34:02 +0330</pubDate>
            </item>
                    <item>
                <title>ارتباط بین کامپوننت ها در بلیزور</title>
                <link>https://virgool.io/@m_20671798/%D8%A7%D8%B1%D8%AA%D8%A8%D8%A7%D8%B7-%D8%A8%DB%8C%D9%86-%DA%A9%D8%A7%D9%85%D9%BE%D9%88%D9%86%D9%86%D8%AA-%D9%87%D8%A7-%D8%AF%D8%B1-%D8%A8%D9%84%DB%8C%D8%B2%D9%88%D8%B1-n2jeh89icgxj</link>
                <description>بهترین راه ارتباطی بین کامپوننت ها در بلیزور چیست؟سه تکنیک برای انجام این کار وجود دارد:EventCallbacksCascading ValuesState ContainerEventCallbacks:‫‫این ویژگی در NET Core 3 Preview 3. به بلیزور اضافه شد. که باعث می‌شود که بتوان یک کالبک برای کامپوننت ها با استفاده از Action یا Func  تعریف کرد. هنگامی که ازEventCallback برای ارتباط بین کامپوننت ها استفاده شود، متد کالبک، StateHasChanged را برای رندر کردن هرگونه تغییری صدا می‌زند‌. استفاده از EventCallback برای هندل ارتباط کامپوننت های تودر تو بسیار خوب است.برای مثال تکه کد زیر را ببینید:&lt;!-- Child Component --&gt;
&lt;button @=”@(() =&gt; .InvokeAsync(”Hello from ChildComponent”))”&gt;Click me&lt;/button&gt;
@code {
      [Parameter] public EventCallback&lt;string&gt;  { get; set; }
}
&lt;!-- Parent Component --&gt;
&lt;ChildComponent =”ClickHandler”&gt;&lt;/ChildComponent&gt;
&lt;p&gt;@message&lt;/p&gt;
@code {
     string message = ”Hello from ParentComponent”
     void ClickHandler(string newMessage) {
          message = newMessage;
     }
}در مثال بالا، کامپوننت فرزند دارای یک پارامتر EventCallback&lt;string&gt;  است. کامپوننت پدر متدClickHandler خود را در ایونت  فرزند ثبت کرده است. هنگامی که دکمه کلیک می شود، متد ClickHandler کامپوننت پدر با مقدار رشته invoke شده توسط فرزند فراخوانی می شود. سپس مقدار message کامپوننت پدر به طور خودکار به روز می شود.Cascading Values:مقادیر و پارامترهای Cascadeراهی برای ارسال یک مقدار از یک کامپوننت به همه فرزندان آن بدون نیاز به استفاده از پارامترهای سنتی کامپوننت است.بلیزور دارای یک کامپوننت ویژه به نام CascadingValue است. این کامپوننت اجازه می‌دهد تا هر مقداری که به آن داده می‌شود، به همه فرزندانش برساند. سپس کامپوننت های فرزند می‌تواند به ویژگی‌های نوع Cascadeشده با [CascadingParameter] دسترسی داشته باشد.این باعث می‌شود هنگام ساخت کنترل‌های UI که نیاز به مدیریت برخی حالت‌ها دارند، گزینه‌ای عالی باشند. یکی از نمونه های برجسته فرم Blazorو کامپوننت‌های اعتبار سنجی است. کامپوننت EditForm یک مقدار EditContextرا به همه کنترل‌های موجود در فرم cascadeمی‌کند. این ویژگی برای هماهنگی اعتبارسنجی و فراخوانی رویدادهای فرم استفاده می شود.&lt;!-- Tab Container --&gt;
&lt;h1&gt;@SelectedTab&lt;/h1&gt;
&lt;CascadingValue Value=”this”&gt;
        @ChildContent
&lt;/CascadingValue&gt;

@code {
      [Parameter] public RenderFragment ChildContent { get; set; }
      public string SelectedTab { get; private set; }
      public void SetSelectedTab(string selectedTab)
     {
              SelectedTab = selectedTab;
              StateHasChanged();
       }
}

&lt;!-- Tab Component --&gt;
&lt;div @=”SetSelectedTab”&gt;@Title @(TabContainer.SelectedTab == Title ? ”Selected&amp;quot : ””)&lt;/div&gt;
@code {
     [CascadingParameter] TabContainer TabContainer { get; set; }
      [Parameter] public string Title { get; set; }
     void SetSelectedTab()
      {
            TabContainer.SetSelectedTab(Title);
      }
}در کد بالا، ما یک کامپوننت TabContainer داریم که تبی که در حال حاضر انتخاب شده را نمایش می دهد، همچنین یک مقدار Cascade را تنظیم می کند. در این مثال، مقداری که Cascade می‌شود، خود کامپوننتTabContainer است.در کامپوننت Tab، مقدار Cascade را دریافت می کنیم و از آن برای فراخوانی متد SetSelectedTab درTabContainer هر زمان که روی div کلیک می شود استفاده می‌کنیم. همچنین مقدارSelectedTab فعلی را دوباره با استفاده از مقدار cascade شده بررسی می‌کنیم و اگر با عنوان برگه مطابقت داشت، &quot;Selected&quot; را به عنوان برگه اضافه می کنیم.برای بدست آوردن اطلاعات بیشتر در مورد Cascade value میتوانید این لینک  را ببینید.State Container:درجات مختلفی از پیچیدگی وجود دارد که می توانید هنگام اجرای یک State Containerبه آن ها بروید. بسته به اینکه به ترتیب از Blazorسمت کلاینت یا سمت سرور استفاده می‌کنید، این می‌تواند یک کلاس ساده باشد که به‌عنوان سرویس Singleton تزریق می‌شود. یا می توانید الگوی بسیار پیچیده تری مانند Flux  را پیاده سازی کنید.این راه از ۲ راه حل دیگر پیچیده تر است. با این راه حل می توان بسیاری از کامپوننت‌ها را در کل برنامه مدیریت و هماهنگ کرد.در این مثال، به استفاده از یک کلاس AppState ساده به عنوان State Container خود نگاه خواهیم کرد.public class AppState
{
     public string SelectedColour { get; private set; }
    public event Action ;
    public void SetColour(string colour)
    {
           SelectedColour = colour;
           NotifyStateChanged();
    }
    private void NotifyStateChanged() =&gt; ?.Invoke();
}این کلاس AppStateما است. رنگ فعلی انتخاب شده را ذخیره می کند و همچنین روشی را برای به روز رسانی رنگ انتخاب شده دارد. در این کلاس یک رویداد OnChangeوجود دارد، که برای هر کامپوننتی که بخواهد رنگ انتخاب شده را نشان دهد به آن نیاز است.&lt;!-- Red Component --&gt;
@inject AppState AppState
&lt;button @=”SelectColour”&gt;Select Red&lt;/button&gt;
@code {
    void SelectColour() {
          AppState.SetColour(”Red”);
    }
}&lt;!-- Blue Component --&gt;
@inject AppState AppState
&lt;button @=”SelectColour”&gt;Select Blue&lt;/button&gt;
@code {
    void SelectColour() {
          AppState.SetColour(”Blue”);
     }
}ما دو کامپوننت قرمز و آبی داریم. این کامپوننت‌ها فقط با کلیک روی دکمه، رنگ انتخاب شده را در AppState به روز می کنند.@inject AppState AppState
@implements IDisposable
@AppState.SelectedColour
&lt;RedComponent /&gt;
&lt;BlueComponent /&gt;
@code {
     protected override void OnInitialized() {
          AppState. += StateHasChanged;
    }
    public void Dispose() {
          AppState. -= StateHasChanged;
    }
}این کامپوننت آخر همه چیز را به هم پیوند می دهد. این کامپوننت رویداد OnChangeرا مدیریت می کند. هر زمان که رنگ انتخاب شده تغییر کند StateHasChangedفراخوانی می شود و کامپوننت با رنگ انتخابی جدید دوباره رندر می شود.مهم است که به یاد داشته باشید با پیاده سازی IDispsable اشتراک StateHasChanged از رویدادی  را لغو کنید تا دچار Memory leak نشوید.لینک مقاله اصلی را در این آدرس می‌توانید مطالعه کنید</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Fri, 07 Apr 2023 17:26:59 +0330</pubDate>
            </item>
                    <item>
                <title>با حواس جمع از Transient در بلیزور استفاده کنیم!</title>
                <link>https://virgool.io/@m_20671798/%D8%A8%D8%A7-%D8%AD%D9%88%D8%A7%D8%B3-%D8%AC%D9%85%D8%B9-%D8%A7%D8%B2-transient-%D8%AF%D8%B1-%D8%A8%D9%84%DB%8C%D8%B2%D9%88%D8%B1-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%DA%A9%D9%86%DB%8C%D9%85-cu12hlsfyhvc</link>
                <description>در تزریق وابستگی به شکل Transient به ازای هر درخواست دهنده‌ی جدید، یک نمونه‌ی جدید از سرویس، توسط  (Dependency injection container)DI Container ساخته می‌شود و در اختیار آن قرار می‌گیرد.وظیفه‌ی DI Container، ایجاد یک نمونه از سرویس درخواست شده، تزریق آن به کلاس درخواست دهنده و در انتها از بین بردن یا Dispose شیء ایجاد شده از سرویس ثبت شده‌است.ثبت یک وابستگی تزریقی به عنوان Transient باعث می شود DI Container ما به عنوان یک کارخانه برای نمونه هایی از آن نوع عمل کند. یک نمونه را نمی توان به طور خودکار به بیش از یک کلاس مصرف کننده تزریق کرد، هر نمونه تزریق شده همیشه منحصر به فرد خواهد بود.وقتی که DI Container یک نمونه از سرویسی که به صورت Transient رجیستر شده ایجاد می‌کند آن را فراموش می‌کند. این سرویس ها زمانی توسط GC جمع آوری می‌شود که سرویس هایی که درون آن رجیستر شده اند جمع آوری شوند.برای اینکه برنامه نویسان نگران dispose کردن سرویس های رجیستر شده نباشند، هنگامی که کانتینر Dispose می‌شود متد Dispose همه سرویس هایی که IDisposable را Impelement کرده اند را Call می‌کند.برای اینکه بتواند این کار را انجام دهد، وقتی که نمونه ای از یک سرویس که IDisposable  را پیاده سازی کرده است را ایجاد می‌کند، یک رفرنس به این سرویس را در خود نگه می‌دارد.اشیاء Transient معمولاً زمانی برای جمع‌آوری زباله واجد شرایط هستند که شیئی که به آن تزریق شده است برای جمع‌آوری زباله واجد شرایط باشد. مگر اینکه IDisposable را پیاده سازی کرده باشد. که در این حالت یک رفرنس به این نمونه در Container نگه داری می‌شود. بنابراین زمانی کاندید جمع آوری شدن توسط GC می‌باشد که Container  توسط GC جمع آوری شود.و Container  تا زمانی که کاربر برگه برنامه Blazor را نبندد توسط GC جمع آوری نخواهد شد.  و این به این معنی خواهد بود که علاوه بر این که با هر درخواست یک نمونه از شی که به صورت Transient رجیستر شده است ایجاد می‌شود، رفرنس ها هم تا پایان در Container نگه داری خواهد شد و اینجاست که Memory leak رخ می‌دهد!بنابراین اگر می‌خواهید وابستگی‌ها را به‌عنوان Transient ثبت کنید، از انجام این کار برای کلاس‌هایی که IDisposable را به طور کامل پیاده‌سازی می‌کنند اجتناب کنید.?نسخه کامل این مطلب را می‌توانید در این لینک مطالعه کنید.</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Fri, 31 Mar 2023 18:06:51 +0330</pubDate>
            </item>
                    <item>
                <title>فعال سازی Pre rendering  در بلیزور</title>
                <link>https://virgool.io/@m_20671798/%D9%81%D8%B9%D8%A7%D9%84-%D8%B3%D8%A7%D8%B2%DB%8C-pre-rendering-%D8%AF%D8%B1-%D8%A8%D9%84%DB%8C%D8%B2%D9%88%D8%B1-duqhatcxrnwd</link>
                <description>چرا باید در پروژه های بیلزور Pre rendering  را انجام داد؟و علاوه بر این، آیا این برای Blazor Client و همچنینBlazor Server صدق می کند؟یک درخواست به سایت بلیزور بدونprerendering چگونه خواهد بود؟۱- کاربر ما صفحه ما را بازدید می کند. بنابراین مرورگر ما به آدرس داده شده رفته و فایلindex ما را بارگذاری می کند.۲- سرور ما اکنون بسته به اینکه از Client یا Server استفاده کنیم، رفتار متفاوتی خواهد داشت:· کلاینت: فایل blazor.webassembly.js دانلود اسمبلی های برنامه و دات نت را راه اندازی خواهد کرد. سپس کامپایل شده و وب اسمبلی اجرا می‌شود‌· سرور: فایل blazor.server.js ارتباط signalr بین مرورگر و سرور را ایجاد می‌کند. ارتباطsignalr ورودی ها را به سرور می‌رساند و html جدید را به مرورگر تحویل می‌دهد.۳- کامپوننت ها رندر خواهند شد و به کلاینت نمایش داده می‌شوند.در اینجا دو مشکل در بلیزور سرور و کلاینت وجود دارد.بلیزور کلاینت: در این نسخه از بلیزور قبل از اینکه کاربر بتواند تعاملی با سایت داشته باشد همه‌ی content های سایت باید دانلود شود که این مورد زمان گیر است.بلیزور سرور: از آنجایی که ما به جاوا اسکریپت و SignalR  نیاز داریم، در بهینه سازی وب سایت خود برای موتورهای جستجو مشکل خواهید داشت. آنها اغلب جاوا اسکریپت را اجرا نمی کنند یا باWebSockets مشکل دارند.راه حل چیست؟در prerendering همه چیز رندر می‌شود و محتوای یک html استاتیک به سمت کلاینت فرستاده خواهد شد.در نتیجه:برای بلیزور کلاینت به این شکل خواهد بود که اطلاعات اولیه سایت به کاربر نمایش داده خواهد شد بدون اینکه نیاز باشد ابتدا مقدار زیادی دانلود انجام شود. همچنین مشکل سایت با موتورهای جستجو حل خواهد شد.چطور باید prerendering را فعال کرد؟اینکار به سادگی اضافه کردن تکه کد زیر در بالای فایل _Host.cshtml خواهد بود.&lt;component type=&quot;typeof(App)&quot; render-mode=&quot;WebAssemblyPrerendered&quot; /&gt;برای بلیزور وب اسمبلی&lt;component type=&quot;typeof(App)&quot; render-mode=&quot;ServerPrerendered&quot; /&gt;برای بلیزور سرورسپس فایل Startup.cs را ادیت کنید تا به جای فایل index.html  به _Host مپ شود.محدودیت های pre rendering:۱- شما دیگر قادر نیستید بلیزور را به صورت استاتیک فایل ارائه کنید. زیرا pre rendering به فایل razor و فایل razor به دات نت ران تایم نیازمند است.۲- اگر شما یک کد جاوا اسکرسپت را در OnInit/OnInitAsync صدا بزنید با خطا مواجه خواهید شد. وقتی ازpre rendering  استفاده می‌کنید اجرای تمام کد های جاوا اسکریپت باید به متدOnAfterRenderAsync منتقل شود.این متد فقط یک بار بعد از رندر شدن کامل صفحه صدا زده می‌شود.در این آدرس Chris Sainty توضیحات خوبی برای فعال کردن Pre rendering  ارایه کرده است.https://dev.to/chrissainty/prerendering-a-client-side-blazor-application-bf6</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Thu, 23 Mar 2023 10:28:32 +0330</pubDate>
            </item>
                    <item>
                <title>پرفورمنس بهتر با @Key!</title>
                <link>https://virgool.io/@m_20671798/%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-key-%DB%8C%D8%A7-%D8%A7%DB%8C%D9%86%D8%AF%DA%A9%D8%B3-%D9%85%D8%B3%D8%A6%D9%84%D9%87-%D8%A7%DB%8C%D9%86%D8%B3%D8%AA-gxjmuvom39jj</link>
                <description>پروژه Blazor ای را در نظر بگیرید که در آن لیستی از عناصر را در صفحه نمایش می‌دهیم. وقتی یکی از عناصر را ادیت یا عضو دیگری به لیست اضافه می‌کنیم، Blazor  باید تصمیم بگیرد که کدام یک از عناصر قبلی حفظ می‌شوند و چگونه آبجکت مدل باید به آنها مپ شوند. به طور معمول، این فرآیند به صورت خودکار و برای رندر کلی کافی است، اما اغلب مواردی وجود دارد که کنترل فرآیند با استفاده از ویژگی دستوری @key مورد نیاز است.به طور پیش فرض،Blazor   از index عنصر برای مقایسه عناصر استفاده می کند. در حالی که این در اکثر موارد عالی عمل می کند، گاهی اوقات بهینه نیست. به عنوان مثال، اگر یک عنصر را در اول یک مجموعه وارد کنید، Blazor  متوجه می شود که همه عناصر در تمامی ایندکس ها تغییر کرده‌اند و کل لیست را مجدد رندر می‌کند. در واقع Blazor متوجه نمی‌شود که تنها یک عنصر به ابتدای لیست اضافه شده و بقیه عناصر تغییری نداشته‌اند. در حالی که اگر این عنصر به انتهای لیست اضافه می‌شد مشکلی وجود نداشت و Blazor تنها عنصر آخر را رندر می‌کرد و اضافه می‌کرد.برای مثال در کد زیر هر بار که عضو جدیدی به اول لیست اضافه شود تمامی لیست مجدد رندر می‌شود‌.@foreach (var item in items){       &lt;li&gt;@item&lt;/li&gt;    }دستور @key به Blazor اجازه می دهد تا از یک کلید خاص برای مقایسه عناصر به جای ایندکس استفاده کند Blazor آیتم های موجود را با موارد جدید با استفاده از مقدار کلید مقایسه می کند. به این ترتیب اضافه‌ها/اصلاحات/حذف‌ها را بهتر تشخیص می‌دهد.@foreach (var item in items){         &lt;li @key=&quot;item.Id&quot;&gt;@item&lt;/li&gt; }با استفاده از @key اگر یک نمونه از مجموعه حذف شود، فقط همان نمونه از رابط کاربری حذف می شود. موارد دیگر بدون تغییر باقی می‌ماند.اگر ورودی‌های مجموعه دوباره مرتب شوند، نمونه‌های مربوطه در رابط کاربری حفظ و مرتب می‌شوند.چه زمانی از @key استفاده نکنیم؟به طور معمول، استفاده از @key درون یک لیست وقتی مقدار مناسب برای تعریف @key وجود دارد، منطقی است. (به عنوان مثال در یک بلاک foreach)هنگام رندر کردن با @key هزینه عملکردی وجود دارد. هزینه عملکرد زیاد نیست، اما فقط در صورتی @key را مشخص کنید که حفظ عنصر یا جزء به نفع برنامه باشد.برای مطالعه بیشتر می‌توانید لینک های زیر را ببینید:https://www.meziantou.net/optimizing-blazor-performance-using-the-key-directive.htmhttps://blazor-university.com/components/render-trees/optimising-using-key/</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Sat, 11 Mar 2023 20:00:26 +0330</pubDate>
            </item>
                    <item>
                <title>برای نگهداری آخرین وضعیت در بلیزور چه باید کرد؟</title>
                <link>https://virgool.io/@m_20671798/%D8%A8%D8%B1%D8%A7%DB%8C-%D9%86%DA%AF%D9%87%D8%AF%D8%A7%D8%B1%DB%8C-%D8%A2%D8%AE%D8%B1%DB%8C%D9%86-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%D8%AF%D8%B1-%D8%A8%D9%84%DB%8C%D8%B2%D9%88%D8%B1-%DA%86%D9%87-%D8%A8%D8%A7%DB%8C%D8%AF-%DA%A9%D8%B1%D8%AF-uwgyjtqmwjkd</link>
                <description>فرض کنید که در حال پر کردن فرم اطلاعاتی بزرگی هستید، و این عملیات برای شما ۳۰ دقیقه طول می کشد. پس از زدن دکمه ثبت با خطایConnection lost! روبرو می‌شوید. دکمه بک را می‌زنید و به صفحه قبل بر می‌گردید تا مجدد دکمه ثبت را بزنید. اما خبری از آن همه اطلاعاتی که پر کرده بودید نیست!این تجربه‌ی دوست نداشتنی باعث می‌شود که دیگر به این سایت سر نزنید!نگهداشتن state برای داشتن یک تجربه کاربری خوب به هنگاهی که اتصال کاربر به طور موقت قطع می‌شود و یا صفحه را رفرش می‌کند یا به صفحه قبل بر می‌گردد بسیار مهم است.وقتی در مورد نگهداری و حفظ موقعیت صفحه صحبت می‌کنیم منظورمان حفظ موقعیت موارد زیر است:· آبجکت مدل HTML که همان ui صفحه است· فیلد ها و پراپرتی های مورد استفاده· وضعیت سرویس های رجیستر شدهنگهداری وضعیت در بلیزور، بستگی به نوع بلیزور پیاده سازی شده دارد.وضعیت در بلیزور وب اسمبلی در داخل کش مرورگر نگهداری می‌شود. تا زمانی که صفحه رفرش شود.در بلیزور سرور وضعیت در bucket هایی که در هر سشن مخصوص هر کاربر ایجاد شده اند نگهداری می‌شوند و با disconnect  شدن کاربر و یا در شرایطی که مموری سرور محدود است از بین می‌رود.برای نگهداشتن وضعیت راه حل‌های زیر پیشنهاد می‌شود.Service Registration:فرض کنید که در قسمت Code-Behind یک کامپوننت، متغیری تعریف کرده و از آن برای نمایش داده در این کامپوننت استفاده می‌شود. با رفرش کردن صفحه مقدار این متغیر به مقدار اولیه برمی‌گردد. زیرا این متغیر در داخل کامپوننت تعریف شده و هر بار کامپوننت initialize  شود از نو ساخته می‌‌شود.برای حل این مشکل باید یک سرویس ساخته و این متغیر در داخل این سرویس ایجاد و مقدار دهی شود. سپس این سرویس به صورت singleton رجیستر می‌شود. سرویس ساخته شده را در کامپوننت مورد نظر رجیستر و استفاده می‌کنیم‌.با این کار با destroy شدن کامپوننت سرویس در حافظه می‌ماند.Browser Cache:راه حل دیگر استفاده از کش مرورگر با استفاده از HTML5 Web Storage است.در این روش یک فایل js با محتوای زیر ایجاد می‌کنیم.window.stateManager = {save: function (key, str) {localStorage[key] = str;},load: function (key) {return localStorage[key];}};و آدرس فایل را در فایلindex.html وب اسمبلی یا در فایل _Host.cshtml بلیزور سرور اضافه می‌کنیم.&lt;script src=&quot;_content/{assembly}/path_to_file&quot;&gt;یک کامپوننت برای مدیریت وضعیت ایجاد می‌کنیم. در داخل قسمت کد کامپوننت متد OnInitializedAsync را override می‌کنیم.در داخل این متد تلاش می‌کنیم هنگامی که کامپوننت لود شد از کش خوانده شود. وهنکامی که یک پراپرتی تغییر کرد و ویو مدل سریالایز شد در کش ذخیره شود.کامپوننت را بهApp.razor اضافه می‌کنیم.دیتیل این کامپوننت در این آدرس موجود است.با این کار مدیریت وضعیت تمامی صفحات انجام می‌شود. و نیازی به نوشتن کد اضافی دیگری نیست.شما می‌توانید تمامی مقادیر ذخیره شده را در Developer tools مرورگر مشاهده کنید.نکته ای که باید در نظر داشته باشید این است که چنانچه اطلاعات ذخیره شده در مرورگر حساس هستند می‌توانید اطلاعات را  encode کرده و سپس ذخیره کنید.Server-side Management:راه حل بعدی صدا زدن Api و ذخیره اطلاعات در سرور است.این که در کجا ذخیره کنید بستگی به خود شما دارد. ممکن است انتخاب شما  sql، noSql  و یا redis باشد. برای این منظور یک کنترلر ایجاد کنید وapi مورد نظر را پیاده سازی کنید. کد بسیار شبیه به رویکرد قبلی است اما مدل را از فراخوانی API به جای کش محلی بازیابی می کند. کنترل کننده تغییر ویژگی مدل را سریال می کند و به سرور ارسال می کند.برای مشاهده کد ها به این آدرس مراجعه کنید.لینک کامل توضیحات</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Wed, 01 Mar 2023 09:47:39 +0330</pubDate>
            </item>
                    <item>
                <title>مروری بر Blazor</title>
                <link>https://virgool.io/@m_20671798/%D9%85%D8%B1%D9%88%D8%B1%DB%8C-%D8%A8%D8%B1-blazor-rcnkvvnn9jai</link>
                <description>‫بلیزور فریمورک وب رایگان و OpenSource مایکروسافت است که به توسعه دهندگان امکان می دهد برنامه های وب را با استفاده از C# و HTML ایجاد کنند.Blazor WebAssembly:‫ بلیزور WebAssembly (به اختصار &quot;Wasm&quot;) یک مجموعه دستورالعمل است که برای اجرا بر روی هر میزبانی که قادر به تفسیر آن دستورالعمل ها باشد طراحی شده است.‫بلیزور برای اجرا از طریق WebAssembly نیازی به نصب دات نت روی کلاینت ندارد.‫بلیزور Wasm می تواند به صورت آفلاین کار کند. وقتی اتصال شبکه به سرور قطع شود، برنامه کلاینت می‌تواند به کار خود ادامه دهد.فایل blazor.webassembly.js برنامه کلاینت مشتری را بوت استرپ می کند. تمام مجموعه‌های DLL دات‌نت مورد نیاز را دانلود می‌کند، که باعث می‌شود زمان راه‌اندازی برنامه در اولین باری که برنامه شما اجرا می‌شود کندتر از Blazor Server باشد. برای دفعات بعد DLL‌ها توسط مرورگر کش می‌شوند و زمان راه‌اندازی بعدی را بسیار سریع‌تر می‌کند. Wasm فقط بر روی مرورگرهای جدیدتر کار می کند و برای موتورهای جستجو مناسب نیست (مگر اینکه Pre Render سمت سرور را فعال کنیم)بلیزور Wasm تا قبل از دات نت ۷ بیش از یک thread را پشتیبانی نمی کرد، بنابراین تمام پردازش ها در thread UI انجام می شد.https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-7-rc-2Blazor Server:در این روش محتوای HTML/CSS  سمت سرور ساخته شده و به کلاینت ارسال می شود. هرگونه تغییر در ظاهر باید سمت سرور ایجاد و به کلاینت ارسال شوداین عمل باعث می شود که گزینه مناسب تری برای موتور های جستجو باشد. و زمان راه اندازی اولیه قابل توجهی ندارد.برنامه‌های سمت سرور Blazor روی مرورگرهای قدیمی‌تر (مانند اینترنت اکسپلورر 11) کار می‌کنند، زیرا نیازی به اسمبلی وب نیست.بلیزور سمت سرور یک سشن در حافظه برای کلاینت فعلی تنظیم می کند و از SignalR برای برقراری ارتباط بین دات نت در حال اجرا روی سرور و مرورگر کلاینت استفاده می کند.اگر مرورگر کلاینت و سرور نزدیک نباشند یا اتصال شبکه بین آنها کند باشد، به خصوص زمانی که رویدادهایی به صورت پشت هم باعث تغییر حالت می شوند، این رفت و برگشت می تواند کاربر با کندی عملیات مواجه شود.در Blazor سرور بر خلاف وب اسمبلی، هنگامی که اتصال از مرورگر به سرور قطع شود، برنامه پاسخگو نیست. Blazor سعی می کند دوباره اتصال به سرور را برقرار کند، اما تا آن زمان برنامه متوقف می‌شود.Blazor Hybrid :بلیزور Hybrid برای ترکیب فریمورک های ویندوزی و موبایل استفاده می‌شود.در یک برنامه Blazor Hybrid، اجزای Razor به صورت native روی دستگاه اجرا می‌شوند. کامپوننت ها به یک کنترل Web View تعبیه شده ارائه می شوند. کامپوننت ها در مرورگر اجرا نمی شوند و WebAssembly دخیل نیست. اجزای Razor کد را به سرعت بارگذاری و اجرا می کنند و اجزا از طریق پلتفرم دات نت به قابلیت های بومی دستگاه دسترسی کامل دارند.Blazor United:این برنامه‌ها ترکیبی از Blazor Server و Blazor WebAssembly می‌باشد. و اجازه می‌دهد تا توسعه‌دهندگان بتوانند حالت رندر را با دقت بیشتری تنظیم کنند. این رویکرد بر کاستی‌های دانلودی که Blazor WebAssembly به آن نیاز دارد و اتصال SignalR دائماً باز که Server Blazor به آن نیاز دارد، غلبه خواهد کرد. این ورژن از Blazor در دات نت ۸ عرضه خواهد شد.ASP.NET Core updates in .NET 8 Preview 1 - .NET Blog (microsoft.com)</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Thu, 23 Feb 2023 18:33:37 +0330</pubDate>
            </item>
                    <item>
                <title>چرا المنت استیکی من کار نمی‌کند؟!</title>
                <link>https://virgool.io/@m_20671798/%DA%86%D8%B1%D8%A7-%D8%A7%D9%84%D9%85%D9%86%D8%AA-%D8%A7%D8%B3%D8%AA%DB%8C%DA%A9%DB%8C-%D9%85%D9%86-%DA%A9%D8%A7%D8%B1-%D9%86%D9%85%DB%8C-%DA%A9%D9%86%D8%AF-qkutpkxgdikf</link>
                <description>‫‫چرا المنت استیکی  من کار نمی کند؟‫‫اگر شما هم مانند من برای اولین بار یک المنت را به صورت استیکی تعریف کرده باشید؛ از این که ببینید المنت شما به یک گوشه چسبیده و با اسکرول هیچ حرکتی نمی‌کند تعجب می کنید!‫‫مواردی که در این شرایط می توانید چک کنید:‫‫۱- آیا این ویژگی توسط مرورگر شما پشتیبانی می‌شود؟‫‫۲- حتما باید یکی از مقادیر top, bottom، left و right برای المنت استیکی شما پر شده باشد و دارای مقدار  auto نباشد.‫‫۳- برای سافاری ورژن ۱۳ به پایین حتما قسمت زیر رو اضافه کنیدposition: -webkit-sticky;‫‫۴- یکی از المنت های پدر دارای overflow با یکی از مقادیر hidden, scroll و یا auto هست. که این مشکل با مقدار دادن ويژگي height به المنت پدر برطرف میشه.‫‫۵- اگه ویژگی height  المنت پدر پر نشده باشه المنت sticky فضایی برای پیمایش پیدا نمی کند و به یک جا می چسبد.‫‫۶- چنانچه المنت پدر به صورت flex layout تعریف شده باشد باید دو نکته را در نظر بگیرید.‫‫* المنت استیکی به صورت align-self : auto تعریف شده باشد‫‫* المنت استیکی به صورت :align-self: stretch تعریف شده باشد‫‫در این حالات باید المنت استیکی به صورت align-self : flex-start تعریف شود. این کار باعث می‌شود که المنت استیکی در المنت پدر دچار کشیدگی نشود.</description>
                <category>نگار قاسمی</category>
                <author>نگار قاسمی</author>
                <pubDate>Sat, 11 Feb 2023 09:48:41 +0330</pubDate>
            </item>
            </channel>
</rss>