اشتراک‌ها
GoFarsi/book: کتاب آزاد (آنلاین/آفلاین) زبان برنامه‌نویسی گو فارسی

«... کتاب زبان گو فارسی آموزش زبان گو را به بطور عمیق از مفاهیم پایه تا مفاهیم کاملا پیشرفته و تکنیکی مانند: سینتکس, پارادایم ها, همزمانی (پایه تا پیشرفته) و همچنین ساختار داده و الگوهای طراحی (Design Patterns) و ... می‌پردازد تا گوفرها درک عمیق و کامل از زبان گو داشته باشند...»

GoFarsi/book: کتاب آزاد (آنلاین/آفلاین) زبان برنامه‌نویسی گو فارسی
نظرات مطالب
پیاده سازی اسکرام با شیرپوینت
کاملا با نظرتون در مورد نظریه شیرپوینت و بیکار کردن برنامه نویس ها موافقم، اما یک برنامه نویس ماکروسافت تا جه حد نیاز به دانستن مفاهیم شیر پوینت دارد؟ و آیا مفاهیم کلی، راه اندازی مدیریت، و توسعه شیر پویت با آنجه که ما تا به حال در مورد ماکروسافت و نرم افزار های آن میدانیم خیلی فرق دارد ؟
مطالب
حذف نمودن کاراکتر های ناخواسته توسط Recursive CTE قسمت اول
شاید برایتان تا حالا پیش آمده باشد که بخواهید یکسری کاراکترهای ناخواسته و اضافه را از یک رشته حذف کنید. بطور مثال تمام کاراکتر هایی غیر عددی را باید از یک رشته حذف نمود تا آن رشته قابلیت تبدیل به نوع integer را بدست بیاورد.

اگر تعداد کاراکترهای ناخواسته محدود و مشخص هستند می‌توانید با دستور REPLACE آنها را حذف کنید، مثلا میخواهیم هر سه کاراکتر ~!@ از رشته حذف شوند:
DECLARE @s VARCHAR(50) = '~~~~~~!@@@@@@@ salam';
SET @s = REPLACE(REPLACE(REPLACE(@s, '~', ''), '!', ''), '@', '');
SELECT @s AS new_string

ولی هنگامی که کاراکتر‌ها نامحدود بوده امکان نوشتن تابع REPLACE به کرات بی معنا است در این حالت باید دنبال روشی پویا و تعمیم پذیر بود.
با جستجویی که در اینترنت انجام دادم متوجه شدم تکنیک WHILE یا همون loop یکی از روش‌های رایج برای انجام اینکار هست، که احتمالا به دلیل سهولت در بکارگیری و سادگی آن بوده که عمومیت پیدا کرده است.
مستقل از این صحبتها هدف معرفی یک روش مجموعه گرا (set-based) برای این مساله می‌باشد.

حذف کاراکترها ناخواسته با تکنیک Recursive CTE
راه حل بر اساس جدول زیر است:
CREATE TABLE test_string
(id integer not null primary key,
 string_value varchar(500) not null);
 
INSERT INTO test_string
VALUES (1, '@@@@ #### salam 12345'),
       (2, 'good $$$$$ &&&&&& bye 00000');

حالا فرض کنید می‌خواهیم هر کاراکتری غیر از حروف الفبای انگلیسی و فاصله(space) از رشته حذف شود. پس دو داده فوق به صورت salam و good bye در انتها در خواهند آمد.
برای حذف کاراکتر‌های ناخواسته فوق query زیر را اجرا کنید.

WITH CTE (ID, MyString, Ix) AS
(
    SELECT id,
           string_value,
           PATINDEX('%[^a-z ]%', string_value)                   
    FROM   test_string
     
    UNION ALL
     
    SELECT id,
           CAST(REPLACE(MyString, SUBSTRING(MyString,Ix , 1), '') AS VARCHAR(500)),
           PATINDEX('%[^a-z ]%', REPLACE(MyString, SUBSTRING(MyString,Ix , 1), ''))
    FROM   CTE
    WHERE  Ix > 0
)
SELECT *
FROM   cte
--WHERE  Ix = 0;
ORDER BY id, CASE WHEN Ix = 0 THEN 1 ELSE 0 END, Ix;

توضیح query:
در قسمت anchor اندیس اولین کاراکتر ناخواسته (خارج از رنج حروف الفبا و فاصله) بدست می‌آید. سپس در قسمت recursive هر کاراکتری که برابر باشد با کاراکتر ناخواسته ای که در مرحله قبل بدست آمده از رشته حذف می‌شود این عملیات توسط تابع replace صورت می‌گیرد و اندیس کاراکتر ناخواسته بعدی بعد از حذف کاراکتر ناخواسته قبلی بدست می‌آید که به مرحله بعد منتقل می‌شود. این مراحل تا آنجایی پیش می‌رود که دیگر کاراکتر ناخواسته ای در رشته وجود نداشته باشد.

به جدول زیر توجه بفرمایید (خروجی query فوق)

نتیجه مطلوب ما آن دو سطری است که در کادر بنفش هستند. که اگر به ستون Ix اشان توجه کنید مقدارش برابر با 0 است.

لطفا به سطر اول جدول توجه بفرمایید مشاهده می‌شود که هر 4 کاراکتر @ یکباره از رشته حذف شدند که بدلیل استفاده از تابع REPLACE میباشد.

مطالب
مقید سازی پارامترهای نوع جنریک
احتمالا در بیشتر مقالات (فارسی/انگلیسی) عبارات هایی مثل نمونه‌های زیر را دیده اید :
where T:clas
where T:struc
...
در این مقاله قصد داریم بپردازیم به «مقید سازی پارامتر‌های نوع جنریک» و اینکه چه کاربردی دارند و در چه زمانی بهتر است از آن‌ها استفاده کنیم و نحوه استفاده از آنها چگونه است. فرض میکنیم که خواننده‌ی محترم با مفاهیم جنریک آشنایی دارد. در صورتیکه با جنریک‌ها آشنا نیستید ابتدا مروری داشته باشید بر جنریک‌ها و بعد این مقاله را مطالعه فرمایید؛ به این دلیل که موضوع مورد بحث بر پایه‌ی جنریک‌ها می‌باشد.

همانطور که مطلع هستید هر عنصری جنریکی را که تعریف میکنید حداقل دارای یک پارامتر نوع هست و در زمان بکارگیری آن جنریک باید نوع آن را مشخص نمایید. برای نمونه مثال زیر را در نظر بگیرید :
public  class MyCollection<T>
        {
            private List<T> collections = new List<T>();
            public void Add(T value)
            {
                collections.Add(value);
            }
        }
کلاس فوق یک کلاس جنریک است که در هنگام ساخت نمونه‌ای از آن، باید ابتدا data type نوعی را که که می‌خواهیم با آن کار کنیم، تعیین کنیم. برای مثال در کد فوق در هنگام ساخت نمونه‌ای از آن، نوع int را برای آن مشخص میکنیم و هر وقت بخواهیم متد Add آن را فراخوانی کنیم، فقط نوعی را قبول خواهد کرد که در ابتدا برای آن تعیین کرده ایم (int):
MyCollection<int> myintObj = new MyCollection<int>();
            myintObj.Add(12);
            myintObj.Add(33);
             myintObj.Add(33.3);// ERROR z 
سؤال: می‌خواهیم فقط نوع‌هایی را بتوان به T نسبت داد که از نوع ارجاعی (reference type) هستن و یا فقط نوع هایی را به T نسبت داد که یک سازنده دارند؛ چگونه؟

ایجاد قید‌ها یا محدودیت‌ها بر روی پارامتر‌های جنریک‌ها شامل پنج حالت می‌باشد:

حالت اول : Where T:struct
در این حالت T باید یک ساختار باشد .

حالت دوم : where T:class
T  باید یک نوع ارجاعی باشد. اگر در مثال فوق این قید را به آن اضافه کنیم، در هنگام ساخت نمونه‌ای از کلاس فوق، اگر یک نوع value type را به T نسبت دهیم، در هنگام وارد کردن یک نوع value type با خطا مواجه خواهیم شد. مثال:
public  class MyCollection<T> where T:class
        {
            private List<T> collections = new List<T>();
            public void Add(T value)
            {
                collections.Add(value);
            }
        }
و برای استفاده :
 MyCollection<int> myintObj = new MyCollection<int>(); // ERROR , int is value type

حالت سوم : ()Where T:new
نوعی که به T نسبت داده می‌شود باید یک سازنده‌ی پیش فرض داشته باشد.
داخل پرانتز : سازنده‌ی پیش فرض: زمانی که شما یک کلاس می‌نویسید اگر آن کلاس دارای هیچ سازنده‌ای نباشد، کامپایلر یک سازنده‌ی بدون پارامتر را به کلاس فوق اضافه می‌کند که کار آن مقدار دهی به فیلد‌های کلاس است. در اینجا از مقادیر پیش فرض استفاده می‌شود. مثلا برای int مقدار صفر و برای string مقدار "" و به همین ترتیب.
اگر از مقدار دهی پیش فرض توسط کامپایلر خرسند نیستید، می‌توانید سازنده پیش فرض را تغییر داده و مطابق میل خود فیلد‌ها را مقدار دهی اولیه کنید .


حالت چهارم : where T:NameOfBaseClass
نوعی که به T نسبت داده می‌شود باید از کلاس NameOfBaseClass ارث بری کرده باشد.

حالت پنجم : where T:NameOfInterface
همانند حالت چهارم می‌باشد؛ با این تفاوت: نوعی که به T نسبت داده می‌شود باید واسط NameOfInterface را پیاده سازی کرده باشد.

پنج حالت فوق نمونه‌هایی از ایجاد محدودیت بر روی پرامتر نوع اعضای جنریک بودند و اما در ادامه قصد داریم نکاتی را در این باب، بیان کنیم:

نکته اول : می‌توانید محدودیت‌های فوق را با هم ترکیب کنید برای اینکار آنها را با کاما از هم جدا کنید :
 public  class MyCollection<T> where T:class,IDisposable,new()
        {
   //content
}
نوعی که به T نسبت داده می‌شود
  • باید از نوع ارجاعی باشد.
  • باید واسط IDisposable را پیاده سازی کرده باشد.
  • باید یک سازنده‌ی پیش فرض داشته باشد.

نکته دوم : زمانیکه از چندین محدودیت استفاده می‌کنید مثل مثال فوق، باید محدودیت ()new در آخرین جایگاه محدودیت‌ها قرار گیرد؛ در غیر اینصورت با خطای زمان ترجمه روبه رو خواهید شد .

نکته سوم : می‌توان محدودیت‌های فوق را علاوه بر کلاس، بر روی متد‌های جنریک نیز اعمال کنید:

public void Swap<T>(ref T val1,ref T val2) where T:struct
            {
//content
            }
نکته چهارم : زمانیکه کلاس و یا متدهای شما بیش از یک نوع پارامتر از نوع جنریک را دریافت می‌کنند، باید محدودیت‌های مورد نظر را برای هر کدام به صورت جداگانه قید کنید. به طور مثال به کلاس زیر که دو پارمتر T و K را دارد، باید برای هر کدام جداگانه محدودیت‌های مورد نظر را اعمال کنیم (در صورت نیاز):
 public  class MyCollection<T,K> where T:class where K:IDisposable,new()
        {
//content
}
نظرات مطالب
یکدست کردن "ی" و "ک" در ASP.NET MVC با پیاده‌سازی یک Model Binder
معادل این مطلب برای ASP.NET Core 6x
- روش Model binder دیگر با نگارش‌های جدیدتر ASP.NET Core کار نمی‌کند و همچنین محدودیت‌های زیادی هم مانند عدم پشتیبانی از پارامترهای [FromBody] را دارد؛ چون بکارگیری [FromBody]، استفاده‌ی از تمام Model binders سفارشی را لغو می‌کند.
- در حال حاضر، استفاده از روش Action filters توصیه می‌شود که به این نحو قابل ثبت است.
نظرات مطالب
معرفی کتابخانه Loader برای بارگذاری JS و CSS
ممنون از شما. این روش احتمالا با روش‌های یکی کردن اسکریپت‌ها یا فایل‌های CSS با هم قابل استفاده نیست (کاهش رفت و برگشت به سرور با یکی کردن چند فایل CSS یا اسکریپت با هم مثل روش‌های bundling & minification در ASP.NET MVC).
مطالب
روش بهینه‌ی بررسی خالی بودن مجموعه‌ها و آرایه‌ها در NET 5.0.
پیشتر مطلب «Count یا Any » را در این سایت مطالعه کرده‌اید که در پایان آن این نتیجه گیری صورت گرفته‌است:
«از این پس حین استفاده از انواع و اقسام لیست‌ها، آرایه‌ها، IEnumerable‌ها و امثال آن‌ها، جهت بررسی خالی بودن یا نبودن آن‌ها تنها از متد Any فراهم شده توسط LINQ استفاده نمائید.»

اکنون پس از سال‌ها، قصد داریم صحت این مساله را با NET 5.0. بررسی کنیم که آیا هنوز هم متد Any، بهترین متد بررسی خالی بودن مجموعه‌ها و آرایه‌های NET 5.0. است یا خیر؟


نحوه‌ی بررسی کارآیی روش‌های مختلف خالی بودن مجموعه‌ها و آرایه‌ها در C# 9.0

در ابتدا یک لیست، یک Enumerable و یک آرایه را به صورت زیر مقدار دهی می‌کنیم و هر سه‌ی این‌ها می‌توانند نال هم باشند:
private IList<int>? _idsList;
private IEnumerable<int>? _idsEnumerable;
private int[]? _idsArray;

[GlobalSetup]
public void Setup()
{
    _idsEnumerable = Enumerable.Range(0, 10000);
    _idsList = _idsEnumerable.ToList();
    _idsArray = _idsEnumerable.ToArray();
}

اکنون که C# 9.0 در اختیار ما است به همراه pattern matching و همچنین Null Conditional Operator و غیره، می‌توان روش‌های زیر را برای بررسی خالی بودن این مجموعه‌ها و آرایه‌ها بکار گرفت:
1- استفاده از Null coalescing برای بررسی نال بودن مجموعه و سپس استفاده از متد Any برای بررسی خالی بودن آن:
var list = _idsList ?? new List<int>();
if (list.Any() is false) { }

2- استفاده از pattern matching برای بررسی نال بودن مجموعه و سپس استفاده از متد Any برای بررسی خالی بودن آن:
if (_idsList is null || _idsList.Any() is false) { }

3- استفاده از روش سنتی مقایسه‌ی مستقیم با null و سپس استفاده از متد Any برای بررسی خالی بودن آن:
if (_idsList == null || _idsList.Any() is false) { }

4- استفاده از Null Conditional Operator برای بررسی نال بودن و سپس استفاده از متد Any برای بررسی خالی بودن آن:
if (_idsList?.Any() is false) { }

5- استفاده از pattern matching برای بررسی مقدار خاصیت Count یک لیست یا آرایه. البته Enumerable‌ها به همراه این خاصیت نیستند و یا باید آن‌ها را به لیست و یا آرایه تبدیل کرد و یا می‌توان متد ()Count آن‌ها را فراخوانی نمود:
if (_idsList is { Count: > 0 } is false) { }

6- استفاده از Null Conditional Operator برای بررسی نال بودن و سپس استفاده از مقدار خاصیت Count لیست، برای بررسی خالی بودن آن:
if (_idsList?.Count == 0) { }

7- استفاده از روش سنتی مقایسه‌ی مستقیم با null و سپس استفاده از مقدار خاصیت Count لیست، برای بررسی خالی بودن آن:
if (_idsList == null || _idsList.Count == 0) { }


کدهای کامل این بررسی به صورت زیر هستند: AnyCountBenchmark.zip

ابتدا ارجاعی به BenchmarkDotNet به برنامه اضافه شده‌است:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
    <Nullable>enable</Nullable>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
  </ItemGroup>
</Project>

و سپس کدهای زیر، بررسی کارآیی روش‌های مختلف تعیین خالی بودن مجموعه‌ها را انجام می‌دهند:
using BenchmarkDotNet.Running;

namespace AnyCountBenchmark
{
    public static class Program
    {
        static void Main(string[] args)
        {
#if DEBUG
            System.Console.WriteLine("Please set the project's configuration to Release mode first.");
#else
            BenchmarkRunner.Run<Scenarios>();
#endif
        }
    }
}

به همراه سناریوهای مختلف زیر:
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Order;

namespace AnyCountBenchmark
{
    [SimpleJob(RuntimeMoniker.NetCoreApp50)]
    [Orderer(SummaryOrderPolicy.FastestToSlowest, MethodOrderPolicy.Declared)]
    [RankColumn]
    public class Scenarios
    {
        private IList<int>? _idsList;
        private IEnumerable<int>? _idsEnumerable;
        private int[]? _idsArray;

        [GlobalSetup]
        public void Setup()
        {
            _idsEnumerable = Enumerable.Range(0, 10000);
            _idsList = _idsEnumerable.ToList();
            _idsArray = _idsEnumerable.ToArray();
        }

        #region Any_With_Null_coalescing
        [Benchmark]
        public void List_Any_With_Null_coalescing()
        {
            var list = _idsList ?? new List<int>();
            if (list.Any() is false) { }
        }

        [Benchmark]
        public void Array_Any_With_Null_coalescing()
        {
            var array = _idsArray ?? Array.Empty<int>();
            if (array.Any() is false) { }
        }

        [Benchmark]
        public void Enumerable_Any_With_Null_coalescing()
        {
            var enumerable = _idsEnumerable ?? Enumerable.Empty<int>();
            if (enumerable.Any() is false) { }
        }
        #endregion

        #region Any_With_Is_Null_Check
        [Benchmark]
        public void List_Any_With_Is_Null_Check()
        {
            if (_idsList is null || _idsList.Any() is false) { }
        }

        [Benchmark]
        public void Array_Any_With_Is_Null_Check()
        {
            if (_idsArray is null || _idsArray.Any() is false) { }
        }

        [Benchmark]
        public void Enumerable_Any_With_Is_Null_Check()
        {
            if (_idsEnumerable is null || _idsEnumerable.Any() is false) { }
        }
        #endregion

        #region Any_Any_With_Null_Equality_Check
        [Benchmark]
        public void List_Any_With_Null_Equality_Check()
        {
            if (_idsList == null || _idsList.Any() is false) { }
        }

        [Benchmark]
        public void Array_Any_With_Null_Equality_Check()
        {
            if (_idsArray == null || _idsArray.Any() is false) { }
        }

        [Benchmark]
        public void Enumerable_Any_With_Null_Equality_Check()
        {
            if (_idsEnumerable == null || _idsEnumerable.Any() is false) { }
        }
        #endregion

        #region Any_With_Null_Conditional_Operator
        [Benchmark]
        public void List_Any_With_Null_Conditional_Operator()
        {
            if (_idsList?.Any() is false) { }
        }

        [Benchmark]
        public void Array_Any_With_Null_Conditional_Operator()
        {
            if (_idsArray?.Any() is false) { }
        }

        [Benchmark]
        public void Enumerable_Any_With_Null_Conditional_Operator()
        {
            if (_idsEnumerable?.Any() is false) { }
        }
        #endregion

        #region Count_With_Pattern_Matching
        [Benchmark]
        public void List_Count_With_Pattern_Matching()
        {
            if (_idsList is { Count: > 0 } is false) { }
        }

        [Benchmark]
        public void Array_Length_With_Pattern_Matching()
        {
            if (_idsArray is { Length: > 0 } is false) { }
        }

        [Benchmark]
        public void Enumerable_Count_With_Pattern_Matching()
        {
            var list = _idsEnumerable?.ToList();
            if (list is { Count: > 0 } is false) { }
        }
        #endregion

        #region Count_With_Null_Conditional_Operator
        [Benchmark]
        public void List_Count_With_Null_Conditional_Operator()
        {
            if (_idsList?.Count == 0) { }
        }

        [Benchmark]
        public void Array_Length_With_Null_Conditional_Operator()
        {
            if (_idsArray?.Length == 0) { }
        }

        [Benchmark]
        public void Enumerable_Count_With_Null_Conditional_Operator()
        {
            if (_idsEnumerable?.Count() == 0) { }
        }
        #endregion

        #region Count_With_Null_Equality_Check
        [Benchmark]
        public void List_Count_With_Null_Equality_Check()
        {
            if (_idsList == null || _idsList.Count == 0) { }
        }

        [Benchmark]
        public void Array_Length_With_Null_Equality_Check()
        {
            if (_idsArray == null || _idsArray.Length == 0) { }
        }

        [Benchmark]
        public void Enumerable_Count_With_Null_Equality_Check()
        {
            if (_idsEnumerable == null || _idsEnumerable.Count() == 0) { }
        }
        #endregion
    }
}
یکبار اجرای آن، نتیجه‌ی زیر را به همراه داشت:


نتایج حاصل:

- بررسی خالی بودن آرایه‌ها، بسیار سریعتر از بررسی خالی بودن لیست‌ها و این مورد نیز سریعتر از Enumerable‌ها است.
- اگر از آرایه‌ها و یا لیست‌ها استفاده می‌کنید، بررسی خاصیت Length و یا خاصیت Count آن‌ها، بسیار سریعتر از بکارگیری متد Any بر روی آن‌ها است.
- اگر از Enumerableها استفاده می‌کنید، استفاده از متد Any بر روی آن‌ها، بسیار سریعتر از بکارگیری متد ()Count و یا تبدیل آن‌ها به لیست و سپس بررسی خاصیت Count آن‌ها است.
- بررسی نال بودن با pattern matching یا همان is null، نسبت به روشی سنتی استفاده‌ی از null ==، سریعتر است. علت آن‌را در مطلب «روش ترجیح داده شده‌ی مقایسه مقادیر اشیاء با null از زمان C# 7.0 به بعد» می‌توانید مطالعه کنید.

بنابراین برای بررسی خالی بودن آرایه‌ها و لیست‌ها، بهتر است از خاصیت Length و یا Count آن‌ها استفاده کرد و برای Enumerableها از متد ()Any.
بازخوردهای دوره
شروع به کار با RavenDB
لطفا پیشنیازهای بحث را که در ابتدای مطلب عنوان شده مطالعه کنید تا با مفاهیم اولیه و علت وجودی بانک‌های اطلاعاتی NoSQL آشنا شوید.