مطالب
CoffeeScript #14

قسمت‌های اصلاح نشده

CoffeeScript در حال رفع برخی از معایب طراحی جاوااسکریپت است و این راه، بس طولانی است. همانطور که قبلا گفته شد، CoffeeScript به شدت به تجزیه و تحلیل استاتیک در زمان طراحی محدود شده است و هیچ بررسی در زمان اجرایی را برای بهبود کارآیی آن انجام نمی‌دهد.
CoffeeScript از یک کامپایلر مستقیم منبع به منبع استفاده می‌کند. با این دیدگاه که هر دستور در CoffeeScript در نتیجه به یک دستور معادل در جاوااسکریپت تبدیل می‌شود.
CoffeeScript برای همه‌ی کلمات کلیدی جاوااسکریپت، کلمه‌ی معادلی ایجاد نمی‌کند، مانند typeof؛ و همچنین برخی از معایب طراحی جاوااسکریپت، به CoffeeScript نیز اعمال می‌شود.
در دو قسمت قبل + و + بر روی معایب طراحی در جاوااسکریپت که توسط CoffeeScript اصلاح شده بود، توضیح دادیم. حال می‌خواهیم درباره برخی از معایب جاوااسکریپت که CoffeeScript تا به حال نتوانسته است آنها را اصلاح کند صحبت کنیم.

استفاده از eval

در حالیکه CoffeeScript برخی از نقاط ضعف جاوااسکریپت را اصلاح کرده است، اما همچنان معایب دیگری نیز وجود دارند، که شما تنها باید از این نقاط ضعف آگاه باشید. یکی از این موارد، تابع eval است. برای استفاده از آن، باید با اشکالاتی که در حین کار با آن مواجه می‌شوید، آگاهی کامل داشته باشید و در صورت امکان از استفاده از آن اجتناب کنید.

تابع eval یک رشته از کد جاوااسکریپت را در حوزه‌ی محلی اجرا می‌کند و توابعی مانند setTimeout و setInterval نیز می‌توانند در آرگومان اولشان یک رشته از کد جاوااسکریپت را دریافت و ارزیابی کنند.

با این حال، مانند eval ،with نیز ردیابی کامپایلر را از کار می‌اندازد و این امر تاثیر بسیار زیادی بر روی کارآیی آن دارد. کامپایلر هیچ ایده ای درباره کدی که درون eval قرار داده شده است، ندارد تا زمانی که آن را اجرا کند. به همین دلیل نمی‌تواند هیچ عمل بهینه سازی را بر روی انجام دهد. یکی دیگر از نگرانی‌های استفاده‌ی از eval، امنیت است. در صورتیکه شما ورودی را به eval ارسال کنید، eval باعث می‌شود که کد شما به راحتی در معرض حملات تزریق کد قرار می‌گیرد. در 99% از مواقع، وقتی شما می‌خواهید از eval استفاده کنید، راه‌های بهتر و امن‌تری وجود دارند ( مانند استفاده از براکت ).

# Don't do this
model = eval(modelName)

# Use square brackets instead
model = window[modelName]

استفاده از typeof

اپراتور typeof احتمالا بزرگترین نقص طراحی جاوااسکریپت است؛ تنها به این دلیل که اساسا به طور کامل شکست خورده است. در واقع از آن فقط یک استفاده می‌شود تا تشخیص داده شود که یک مقدار undefined است یا نه.

typeof undefinedVar is "undefined"
برای چک کردن type همه types، متاسفانه typeof نمی تواند به درستی این کار را انجام دهد و مقدار بازگشتی آن وابسته به مرورگر و چگونگی نمونه سازی آن نمونه است. در این رابطه CoffeeScript هیچ کمکی به شما نمی‌تواند بکند، چرا که قبلا نیز گفته شد، CoffeeScript یک زبان با تجزیه و تحلیل استاتیک است و هیچ بررسی در زمان اجرایی بر روی نوع آن ندارد.
در اینجا لیستی از مشکلات، هنگام استفاده از typeof را مشاهده می‌کنید:
Value               Class      Type
----------------------------------------------
"foo"               String     string
new String("foo")   String     object
1.2                 Number     number
new Number(1.2)     Number     object
true                Boolean    boolean
new Boolean(true)   Boolean    object
new Date()          Date       object
new Error()         Error      object
[1,2,3]             Array      object
new Array(1, 2, 3)  Array      object
new Function("")    Function   function
/abc/g              RegExp     object
new RegExp("meow")  RegExp     object
{}                  Object     object
new Object()        Object     object
همانطور که مشاهده می‌کنید تعریف یک رشته در داخل "" و یا با کلاس String، در نتیجه‌ی استفاده از typeof تاثیر گذار است. به طور منطقی typeof باید "string" را به عنوان خروجی در هر دو حالت نشان دهد، اما برای دومی به صورت "object" باز می‌گرداند.
سوالی که اینجا مطرح می‌شود این است که ما چطور می‌توانیم یک نوع را در جاوااسکریپت چک کنیم؟
خوب، خوشبختانه ()Object.prototype.toString ما را نجات داده است. اگر ما این تابع را بر روی یک شیء خاص فراخوانی کنیم، مقدار صحیح را بر می‌گرداند.
در اینجا مثالی از نحوه‌ی پیاده سازی jQuery.type را مشاهده می‌کنید:
type = do ->
  classToType = {}
  for name in "Boolean Number String Function Array Date RegExp Undefined Null".split(" ")
    classToType["[object " + name + "]"] = name.toLowerCase()

  (obj) ->
    strType = Object::toString.call(obj)
    classToType[strType] or "object"

# Returns the sort of types we'd expect:
type("")         # "string"
type(new String) # "string"
type([])         # "array"
type(/\d/)       # "regexp"
type(new Date)   # "date"
type(true)       # "boolean"
type(null)       # "null"
type({})         # "object"
در صورتیکه بخواهید تشخیص دهید یک متغیر تعریف شده است یا نه، باید از typeof استفاده کنید؛ در غیر این صورت پیام خطای ReferenceError را دریافت خواهید کرد.
if typeof aVar isnt "undefined"
  objectType = type(aVar)
و یا به طور خلاصه‌تر با استفاده از اپراتور وجودی:
objectType = type(aVar?)
راه دیگری برای چک کردن نوع، استفاده از اپراتور وجودی CoffeeScript است. برای مثال: می‌خواهیم یک مقدار را در یک آرایه اضافه کنیم. می‌توان گفت تا زمانیکه تابع push پیاده سازی شده باشد ما باید با آن مانند یک آرایه رفتار کنیم.
anArray?.push? aValue
اگر anArray یک شیء به غیر از آرایه باشد، اپراتور وجودی تضمین خواهد کرد که هیچگاه تابع push فراخوانی نخواهد شد.

استفاده از instanceof

کلمه‌ی کلیدی instanceof نیز تقریبا همانند typeof شکست خورده است. در حالت ایده آل، instanceof، سازنده‌ی دو شیء را با هم مقایسه می‌کند، در صورتیکه یک شیء نمونه‌ای از شیء دیگر باشد، یک مقدار boolean را باز می‌گرداند. در واقع instanceof موقعی کار مقایسه را انجام می‌دهد که اشیاء، سفارشی سازی شده باشند. وقتی عمل مقایسه می‌خواهد بر روی این نوع اشیاء سفارشی سازی شده، انجام شود، استفاده از typeof بی‌فایده است.

new String("foo") instanceof String # true
"foo" instanceof String             # false
علاوه بر این، instanceof همچنین بر روی اشیاء در فریم‌های مختلف مرورگر عمل مقایسه را نمی‌تواند انجام دهد. در واقع instanceof فقط نتیجه‌ی صحیح مقایسه را در اشیاء سفارشی سازی شده برمی‌گرداند؛ مانند کلاس‌های CoffeeScript.
class Parent
class Child extends Parent

child = new Child
child instanceof Child  # true
child instanceof Parent # true
مواقعی از instanceof استفاده کنید که مطمئن هستید بر روی اشیای ساخته شده توسط شما بکار گرفته می‌شود و یا هرگز از آن استفاده نکنید.

استفاده از delete

از کلمه کلیدی delete برای حذف خصوصیات موجود در اشیاء به صورت کاملا مطمئن، می‌توان استفاده کرد.
anObject = {one: 1, two: 2}
delete anObject.one
anObject.hasOwnProperty("one") # false
هر نوع استفاده دیگر، از قبیل حذف متغیرها و یا توابع کار نخواهد کرد.
aVar = 1
delete aVar
typeof Var # "integer"
در صورتیکه می‌خواهید یک اشاره گر به یک متغیر را حذف کنید فقط کافیست مقدار null را به آن انتساب دهید.
aVar = 1
aVar = null

پاسخ به بازخورد‌های پروژه‌ها
درخواست مستندات
دقیقا قصد همین است که کاربر جداول و فیلدها را ببیند و انتخاب کند، طبیعتا اکثر کاربران امکان نوشتن و فهمیدن دستورات SQL را ندارند، حال آنکه اگر کسی توانست که چه بهتر!
علت تولید فایل XML نیز این است که بتوان از آن در زبان‌های مختلف استفاده کرد، مثلا در Applicationهای تحت وب php یا mvc یا هر زبان دیگری ولی در اینجا فعلا قصد ایجاد گزارش با زبان سی شارپ و کتابخانه pdfreport  را داریم.
حالتی که نیز می‌فرمائید از یک سری متغیر در برنامه استفاده کنید نیز به ذهنم رسیده بود، که مثلا یه سری تنظیمات مربوط به گزارشات ایجاد کنیم تا کاربر بتواند قسمت‌های مختلف گزارش را با توجه به این تنظیمات تغییر دهد. مثلا در فوتر گزارش‌ها فلان متن نوشته شود، یا رنگ متون جداول فلان رنگ باشد.
ولی با استفاده از این روش می‌توانید شکل و قیافه گزارش را تغییر دهید نه اینکه یک گزارش جدید تهیه کنید و به برنامه اضافه نمایید.
هدف اصلی ساختن طراح گزارشی تقریبا شبیه به stimulsoft می‌باشد، که در پس زمینه هنگامی که شما اجزای گزارش را به روی صفحه drag&drop می‌کنید، در واقع در حال تهیه فایل کد سی شارپ آن می‌باشد، حال اینجا برای اینکه حالت کلی‌تری باشد از ساختار xml استفاده می‌کنیم تا بعدا هر کسی(برنامه نویس ها) با توجه به نیاز خود providerهایی را برای تفسیر این فایل xml بنویسند.
نظرات نظرسنجی‌ها
ضرورت دانش پایه برای پیشرفت در صنعت نرم افزار کشور

برنامه نویس‌ها سطوح مختلفی دارند. یکی کامپوننت می‌نویسد، یکی استفاده می‌کند. یکی پروتکل طراحی می‌کند، یکی صرفا تنظیمات این پروتکل را در سیستم عامل انجام می‌دهد. بنابراین این دو لازم و ملزوم هستند. فقط بستگی دارد که در چه سطحی می‌خواهید کار کنید. همچنین گیرم دانش پایه تولید کامپایلر را بدست آوردید. آیا می‌توانید با نمونه‌های موجود رقابت کنید؟ گیرم دانش بومی تولید سیستم عامل را به دست آوردید، آیا اصلا هزینه‌ی آن قابل توجیه است (توسعه، نگهداری، رفع نواقص امنیتی، ارائه منظم نگارش‌های جدید، سازگاری با سخت افزارهای مختلف). آیا تمام کشورهای صاحب نام IT در دنیا وارد این بازی شده‌اند؟

به علاوه اندازه‌ی کسب و کارها هستند که تعیین کننده سطح دانش مورد نیاز خودشان هستند. کسی که یک گروه 5 نفره دارد، آیا برایش مقرون به صرفه است که به فکر تولید سیستم عامل باشد؟ برای مثال یک کسب و کار کوچک شاید الزاما نیازی به راه حل‌های NoSQL نداشته باشد؛ اما حتما باید با نحوه‌ی کار با SQLite یا SQL CE آشنا باشد. در یک کسب و کار بزرگ شاید بانک‌های اطلاعاتی موجود پاسخگو نباشند و نیاز باشد تا واحد تحقیق و توسعه‌ی آن‌ها دست به کار شود و بانک اطلاعاتی متناسبی را طراحی کند. برای مثال فیس بوک برداشته تمام قابلیت‌های سی‌شارپ رو به PHP اضافه کرده، یک زبان جدید برای خودشون درست کردند.

مطالب
درباره Iterator methodها و yield return در #C
هنگامیکه می‌خواهید در متدهای خود مقداری (از هر نوع datatype دلخواه) را return نمایید، در حالت عادی قادر خواهید بود که فقط از یک return در بدنه متد خود استفاده نمایید:
 public int Sum(int a, int b)
{
     return a + b;
}
اما چنانچه از متدهای تکرار شونده استفاده نمایید، چطور؟

متدهای تکرار شونده یا Iterator method‌ها ، در داخل یک collection به صورت دلخواه iterate کرده یا به اصلاح پیمایش می‌کنند. این متدها از کلمه کلیدی Yield در هنگام return کردن مقادیر استفاده می‌کنند. (در C# از Yield return و در VB از Yield استفاده می‌شود)  به عبارت دیگر یک متد با خروجی از نوع قابل پیمایش (مانند IEnumerable)، با استفاده از چند yield return، دارای قابلیت پیمایش و بازگرداندن چندین مقدار به جای یک مقدار واحد می‌گردد.

برای درک بهتر مسئله از مثالی برای ادامه توضیحات استفاده می‌کنم. متد پیمایش شونده (Iterate method) زیر را در نظر بگیرید که خروجی IEnumerable دارد:
 public static IEnumerable SomeNumbers()
{
     yield return 3;
     yield return 5;
     yield return 8;
}
برای استفاده از مقادیر بازگشتی متد بالا از حلقه foreach زیر استفاده می‌نماییم:
static void Main()
{
    foreach (int number in SomeNumbers())
    {
        Console.Write(number.ToString() + " ");
    }
    // Output: 3 5 8
    Console.ReadKey();
}
حلقه foreach فوق ، در پایان اولین پیمایش، عدد 3 را باز گردانده و مکان این return را حفظ می‌کند. در چرخه بعدی عدد 5 را باز می‌گرداند و این نقطه را نیز نگه می‌دارد و در چرخه پایانی عدد 8 را برگردانده و سپس حلقه با رسیدن به نقطه پایانی متد، خاتمه می‌یابد.

برای خاتمه پیمایش در Iterator method‌ها ، میتوانید از foreach استفاده کنید و یا اینکه عبارت yield break  را بعد از تمامی yield return‌ها به کار گیرید:
 public static IEnumerable SomeNumbers()
{
   yield return 3;
   yield return 5;
   yield return 8;
   yeild break;
}
نکات:

  - در هنگام ایجاد Iterator method ها، نوع مقادیر خروجی متد ، باید یکی از انواع IEnumerable, IEnumerable<T>, IEnumerator,  و یا IEnumerator<T>. باشد.
 - در هنگام declare کردن ، نمی‌توانید از پارامترهای  ref و out استفاده نمایید.
 - در Anonymous method‌ها (متدهای بی نام) و Unsafe block‌ها نمی‌توانید از yield return (yield در VB ) استفاده نمایید.
 - نمی‌توانید از Yield return در بلوکهای try-catch استفاده کندی. اما می‌توانید در قسمت try بلوک try-finally استفاده نمایید.
 - از yield break  می‌توانید در بلوک try  و یا بلوک catch استفاده نمایید ، اما در بلوک finally خیر.
 - هنگام بروز خطا در foreach هایی که خارج از iterator method‌ها استفاده می‌شوند، بلوک finally داخل این متدها اجرا می‌گردد.

مثالی دیگر با استفاده Iterator method‌ها و yield return جهت بازگرداندن روزهای هفته:
static void Main()
{
  DaysOfTheWeek days = new DaysOfTheWeek();
  foreach (string day in days)
    {
        Console.Write(day + " ");
    }
    // Output: Sun Mon Tue Wed Thu Fri Sat
    Console.ReadKey();
}

public class DaysOfTheWeek : IEnumerable
{
  private string[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  public IEnumerator GetEnumerator()
    {
        for (int index = 0; index < days.Length; index++)
        {
            // Yield each day of the week. 
            yield return days[index];
        }
    }
}
منابع:
 yield ، Iterators
مطالب
CoffeeScript #10

اصطلاحات عمومی CoffeeScript

Destructuring Assignments

با استفاده از Destructuring assignments می‌توانید خصوصیات را از آرایه‌ها یا اشیاء، با هر میزان عمقی استخراج کنید.

someObject = { a: 'value for a', b: 'value for b' }
{ a, b } = someObject
console.log "a is '#{a}', b is '#{b}'"
نتیجه‌ی کامپایل آن می‌شود:
var a, b, someObject;

someObject = {
  a: 'value for a',
  b: 'value for b'
};

a = someObject.a, b = someObject.b;

console.log("a is '" + a + "', b is '" + b + "'");
این موضوع به خصوص در برنامه‌های کاربردی، وقتی نیاز به ماژول‌های دیگر است، مفید خواهد بود.
{join, resolve} = require('path')

join('/Users', 'Vahid')

External libraries

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

# Use local alias
$ = jQuery

$ ->
  # DOMContentLoaded
  $(".el").click ->
    alert("Clicked!")
نتیجه‌ی کامپایل آن می‌شود:
var $;
$ = jQuery;
$(function() {
  return $(".el").click(function() {
    return alert("Clicked!");
  });
});
از آنجاییکه خروجی همه کدهای CoffeeScript در داخل یک تابع بدون نام قرار می‌گیرد، می‌توانیم از یک متغیر محلی به نام $ به عنوان نام مستعار jQuery استفاده کنیم. این کار به شما کمک می‌کند تا حتی وقتی که حالت jQuery.noConflict نیز فراخوانی شده باشد، $ مجدد تعریف شده و اسکریپت ما به طور کامل اجرا شود.

Private variables

کلمه‌ی کلید do در CoffeeScript به ما اجازه می‌دهد تا توابع را مستقیما اجرا کنیم و این روش یک راه خوب برای کپسوله سازی و حفاظت از متغیرهاست. در مثال زیر متغیر classToType را در context یک تابع بدون نام که به وسیله‌ی do فراخوانی می‌شود، تعریف کرده‌ایم. تابع بدون نام دوم، مقدار نهایی از type است را برمی گرداند. از آنجایی که classToType در context تعریف شده است و هیچ ارجایی به آن نگهداری نمی‌شود، پس امکان دسترسی به آن خارج از این scope وجود ندارد.
# Execute function immediately
type = do ->
  classToType = {}
  for name in "Boolean Number String Function Array Date RegExp Undefined Null".split(" ")
    classToType["[object " + name + "]"] = name.toLowerCase()

  # Return a function
  (obj) ->
    strType = Object::toString.call(obj)
    classToType[strType] or "object"
نتیجه‌ی کامپایل آن می‌شود:
var type;

type = (function() {
  var classToType, i, len, name, ref;
  classToType = {};
  ref = "Boolean Number String Function Array Date RegExp Undefined Null".split(" ");
  for (i = 0, len = ref.length; i < len; i++) {
    name = ref[i];
    classToType["[object " + name + "]"] = name.toLowerCase();
  }
  return function(obj) {
    var strType;
    strType = Object.prototype.toString.call(obj);
    return classToType[strType] || "object";
  };
})();
به بیان دیگر classToType به طور کامل private است و امکان دسترسی به آن از طریق تابع بدون نام اجرا کننده وجود ندارد. این الگو راه بسیار خوب و مناسبی برای کپسوله سازی scope و مخفی سازی متغیرها است.
مطالب
جستجوی کاراکترهای wildcards توسط ماده اختیاری ESCAPE
syntax کامل LIKE:
match_expression [ NOT ] LIKE pattern [ ESCAPE escape_character ]

چهار کاراکتر وجود دارد که الگوهای جستجو را توسط آنها تعریف می‌کنند. این کاراکترها wildcard نام دارند.
زمانی که کاراکترهای wildcard موجود در الگوی ما به عنوان کاراکترهای عادی در نظر گرفته شده اند نه wildcard آنگاه باید به SQL اطلاع دهیم که آنها را با کاراکترهای wildcard اشتباه نگیرد، برای این منظور چندین راه حل وجود دارد که در ادامه شرح خواهم داد.

فرض کنید الگوی شما این مقدار باشد:
"تنها دو کاراکتر % کاراکترهای wildcard هستند"
%[abcde_ ]%

روش اول: ESCAPE
WHERE string_value LIKE '%@[abcde@_ ]%' ESCAPE '@'
دو کاراکتر ] و _ علامت گذاری (بوسیله @) و توسط ماده ESCAPE به عنوان کاراکترهای معمولی شناخته شدند.

روش دوم: Bracket
WHERE string_value LIKE '%[[]abcde[_] ]%'
دو کاراکتر ] و _ را داخل [] قرار دادیم.

روش سوم: CHARINDEX
WHERE CHARINDEX('[abcde_ ]', string_value) > 0
عبارت را بدون هیچ گونه تغییری در تابع استفاده کردیم. این روش تنها زمانی مورد استفاده می‌تواند قرار گیرد که کاراکتر % در ابتدا یا انتهای (یا هر دو) آن قرار گرفته باشد.


تمرین:
عباراتی را پیدا کنید که الگوی زیر در آن پیدا شود، فقط آخرین کاراکتر wildcard می‌باشد یعنی کاراکتر %
[[[%^ab'c'de]]]%
برای تست راه حلتان از متغیر جدولی زیر استفاده کنید:

declare @t table (s varchar(50))
insert @t values ('[[[%^ab''c''de]]]%'),
('[[[ %^ab''cde]]%'), ('[[%^ab''c''de]]%'),
('[[[%^abc''de]]]%');



نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 10 - استفاده از امکانات بومی بانک‌های اطلاعاتی
یک نکته‌ی تکمیلی: اهمیت دقت داشتن به امضای متد FromSql در EF Core 2.0

در EF Core 2.0، اگر از String Interpolation معرفی شده‌ی در C# 6.0 استفاده شود، متد FromSql این نوع متغیرها را به صورت خودکار تبدیل به پارامترهای کوئری‌های SQL می‌کند. برای مثال کوئری ذیل که در آن userName از طریق String Interpolation معرفی شده‌است:
var userName = "user 1";
var user = context.Users.FromSql($"select top 1 * from Users where name = {userName} ").FirstOrDefault();
if (user != null)
{
   Console.WriteLine(user.Name);
}
یک چنین خروجی SQL ایی را تولید می‌کند:
SELECT TOP(1) [u].[UserId], [u].[IsAdmin], [u].[Name]
FROM (
    select top 1 * from Users where name = @p0
) AS [u]
همانطور که ملاحظه می‌کنید پارامتر p0@ به صورت خودکار بجای {userName} درج شده‌است.

اما ... اگر این کوئری را به نحو ذیل اجرا کنیم:
var sql = $"select top 1 * from Users where name = {userName} ";
user = context.Users.FromSql(sql).FirstOrDefault();
if (user != null)
{
   Console.WriteLine(user.Name);
}
برنامه کرش می‌کند:
 .SqlException: Incorrect syntax near '1'
علت اینجا است که زمانیکه رشته‌ی Interpolation را مستقیما داخل متد FromSql درج می‌کنیم از overload زیر استفاده می‌کند:
 public static IQueryable<TEntity> FromSql<TEntity>(this IQueryable<TEntity> source, FormattableString sql) where TEntity : class;
در اینجا sql از نوع FormattableString معرفی شده‌است.

اما زمانیکه از var استفاده می‌کنیم، یعنی این رشته به نوع string تبدیل می‌شود. در این حالت دیگر از overload فوق استفاده نشده و عملیات تهیه‌ی کوئری‌های پارامتری انجام نخواهد شد. علت کرش برنامه هم همین مورد است. اگر در این حالت بخواهیم از userName استفاده کنیم، باید آن‌را داخل '' محصور کنیم:
 var sql = $"select top 1 * from Users where name = '{userName}' ";
و در نهایت کوئری تولید شده نیز پارامتری نیست:
SELECT TOP(1) [u].[UserId], [u].[IsAdmin], [u].[Name]
FROM (
   select top 1 * from Users where name = 'user 1'
) AS [u]
به عبارتی این نوع کوئری، مستعد به حملات تزریق اس کیوال است. چون بجای user 1 هر نوع ورودی دیگری را نیز می‌توان درج کرد و به علت پارامتری نبودن، این نوع ورودی‌ها می‌توانند ساختار عبارت SQL نوشته شده را تغییر دهند.
بنابراین در حین کار با متد FromSql به overload در حال استفاده دقت داشته باشید. فقط حالت FormattableString آن است که کار تبدیل String Interpolation را به کوئری‌های پارامتری انجام می‌دهد.
نظرات اشتراک‌ها
PowerShell 7.3 منتشر شد
برای آپگرید به نسخه 7.3 روی macOS میتوانید از brew استفاده کنید:
brew update
brew upgrade powershell --cask
بر روی ویندوز نیز میتوانید از winget استفاده کنید:
winget install Microsoft.Powershell