مایکروسافت ابزار جدیدی را برای تستنویسی برنامههای وب به نام «Playwright» ارائه کرده است. با استفاده از این ابزار امکان انجام تست بر روی Chromium ، Firefox و WebKit با استفاده از یک API وجود دارد.
یک مثال ساده:
import { test, expect } from "@playwright/test"; test.describe("navigation", () => { test.beforeEach(async ({ page }) => { // Go to the starting url before each test. await page.goto("https://playwright.dev/"); }); test("main navigation", async ({ page }) => { // Assertions use the expect API. await expect(page).toHaveURL("https://playwright.dev/"); }); });
ASP.NET Web API - قسمت اول
Protocol Channel هم همان مفاهیمی هستند که در ASP.NET MVC وجود دارند و مثلاً قسمتی از اون، تصدیق هویت و تعیین مجوز کاربر برای دسترسی به منابع با استفاده از فیلتر Authorize هست.
لطفاً دنبال تطبیق و تناظر بین مفاهیم پیچیدهی WCF و یافتن معادل در Web API نباشید. Web API به وجود آمده تا ایجاد وب سرویسها در بستر HTTP رو ساده کنه، همین!
رمزنگاری کانکشن استرینگ در ASP.Net
- در وب برای اینکه این کاراکترهای عجیب و غریب مشکل ساز نشوند یکبار دیگر هم اطلاعات رمزنگاری شده را از فیلتر base64 encoding عبور میدهند. به این صورت مشکلی برای نگهداری آنها در فایلها وجود نخواهد داشت.
- نگهداری اطلاعات حساس در حافظه به صورت plain کار اشتباهی است چون دامپ حافظه ویندوز و یا تمام سیستم عاملهای دیگر کار سادهای است. برنامههای زیادی هستند که پروسسهای ویندوز را لیست میکنند و به شما اجازه میدهند حافظه آنها را دامپ کنید (به راحتی چند کلیک). استخراج اطلاعات حساس هم از یک فایل دامپ تر و تازه زیاد مشکل نیست.
- سرعت الگوریتمهای رمزنگاری واقعا بالا است. پیاده سازیهای خیلی خوبی هم دارند. بنابراین زیاد نگران این سربار نباشید. چون در حد یک رشته ساده و امثال آن اصلا سرباری به حساب نمیآیند و بسیار سریع عمل میکنند.
ویرایشگر متن
آشنایی با NHibernate - قسمت هشتم
کلا استفادهی از هر کدام از ORMs موجود بدون پیاده سازی الگوی Repository اشتباه است. به چند دلیل:
- مخفی کردن ساز و کار درونی یک ORM : برای مثال من جدا قصد ندارم این رو حفظ کنم که فلان ORM خاص چطور Insert انجام میدهد. من فقط میخواهم یک متد Insert داشته باشم. یکبار این رو در الگوی Repository پیاده سازی میکنم و بعد فراموش میکنم که این ORM الان EF است یا NH یا هرچی
- امکان تعویض کلی یک ORM : زمانیکه من در کدهای BLL خودم فقط از متد Insert پیاده سازی شده مطابق رهنمونهای الگوی Repository استفاده کردم، دیگر BLL درکی از ORM نخواهد داشت. برای کوچ کردن به یک ORM دیگر فقط کافی است تا Repository را عوض کرد. مابقی برنامه دست نخورده باقی میماند.
- نوشتن Unit test با استفاده از الگوی Repository سادهتر است: این الگو چون بر مبنای یک Interface پیاده سازی میشود، امکان Mocking این Interface در Unit tests سادهتر است.
نشانه های طراحی ضعیف
برای آنکه طراحی قوی و درست را یاد بگیریم، لازم است که نشانههای طراحی ضعیف را بدانیم. این نشانهها عبارتند از:
۱- Rigidity (انعطاف ناپذیری): یک ماژول انعطاف ناپذیر است، اگر یک تغییر در آن، منجر به تغییرات در سایر ماژولها گردد. هر چه میزان تغییرات آبشاری بیشتر باشد، نرم افزار خشکتر و غیر منعطفتر است.
۲- Fragility (شکنندگی): وقتی که تغییر در قسمتی از نرم افزار باعث به بروز اشکال در بخشهای دیگر شود.
۳- Immobility (تحرک ناپذیری): وقتی نتوان قسمت هایی از نرم افزار را در جاهای دیگر استفاده نمود و یا به کار گیری آن هزینه و ریسک بالایی داشته باشد.
۴- Viscosity (لزجی): وقتی حفظ طراحی اصولی پروژه مشکل باشد، میگوییم پروژه لزج شده است. به عنوان مثال وقتی تغییری در پروژه به دو صورت اصولی و غیر اصولی قابل انجام باشد و روش غیر اصولی راحتتر باشد، میگوییم لزج شده است. البته لزجی محیط هم وجود دارد مثلا انجام کار به صورت اصولی نیاز به Build کل پروژه دارد که زیاد طول میکشد.
۵- Needless Complexity (پیچیدگی اضافی): زمانی که امکانات بدون استفاده در نرم افزار قرار گیرند.
۶- Needless Repetition (تکرارهای اضافی): وقتی که کدهایی با منطق یکسان در جاهای مختلف برنامه کپی میشوند، این مشکلات رخ میدهند.
۷- Opacity (ابهام): وقتی که فهمیدن یک ماژول سخت شود، رخ میدهد و کد برنامه مبهم بوده و قابل فهم نباشد.
چرا نرم افزار تمایل به پوسیدگی دارد؟
در روشهای غیر چابک یکی از دلایل اصلی پوسیدگی، عدم تطابق نرم افزار با تغییرات درخواستی است. لازم است که این تغییرات به سرعت انجام شوند و ممکن است که توسعه دهندگان از طراحی ابتدایی اطلاعی نداشته باشند. با این حال ممکن است تغییرایی قابل انجام باشد ولی برخی از آنها طراحی اصلی را نقض میکنند. ما نباید تغییرات نیازمندیها را مقصر بدانیم. باید طراحی ما قابلیت تطبیق با تغییرات را داشته باشد.
یک تیم چابک از تغییرات استقبال میکند. وقت بسیار کمی را روی طراحی اولیه کل کار میگذارد و سعی میکند که طراحی سیستم را تا جایی که ممکن است ساده و تمیز نگه دارد با استفاده از تستهای واحد و یکپارچه از آن محافظت کند. این طراحی را انعطاف پذیر میکند. تیم از قابلیت انعطاف پذیری برای بهبود همیشگی طراحی استفاده میکند. بنابراین در هر تکرار نرم افزاری خواهیم داشت که نیازمندیهای آن تکرار را برآورده میکند.
MongoDB #2
yield و نحوهٔ پیادهسازی آن
public class ArrayEnumerable<T> : IEnumerable<T> { T[] _array; public ArrayEnumerable(T[] array) { _array = array; } public IEnumerator<T> GetEnumerator() { int index = 0; while (index < _array.Length) { yield return _array[index]; index++; } yield break; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } }
همانطور که میبینیم کد قبلی ما به مقدار بسیاری سادهتر و خواناتر شد و برای فهم آن کافی است که مفهوم yield را بدانیم.
yield به معنای برآوردن یا ارائهکردن کلید واژهای است که میتوان آن را اینگونه تصور کرد که با هر با صدا زدهشدن کد را متوقف میکند و نتیجهای را برمیگرداند و با درخواست ما برای ادامهٔ کار (با MoveNext) کار خود را از همان جای متوقف شده ادامه میدهد.
حالا اگر کمی دقیقتر باشیم سوالی که باید برای ما پیش بیاید این است که آیا CLR خود yield را پشیبانی میکند؟
public class ArrayEnumerable<T> : IEnumerable<T>, IEnumerable { // Fields private T[] _array; // Methods public ArrayEnumerable(T[] array) { this._array = array; } public IEnumerator<T> GetEnumerator() { return new <GetEnumerator>d__0(0); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } // Nested Types [CompilerGenerated] private sealed class <GetEnumerator>d__0 : IEnumerator<T>, IEnumerator, IDisposable { // Fields private int <>1__state; private T <>2__current; public ArrayEnumerable<T> <>4__this; public int <index>5__1; // Methods [DebuggerHidden] public <GetEnumerator>d__0(int <>1__state) { this.<>1__state = <>1__state; } private bool MoveNext() { switch (this.<>1__state) { case 0: this.<>1__state = -1; this.<index>5__1 = 0; while (this.<index>5__1 < ArrayEnumerable<T>._array.Length) { this.<>2__current = ArrayEnumerable<T>._array[this.<index>5__1]; this.<>1__state = 1; return true; Label_0050: this.<>1__state = -1; this.<index>5__1++; } break; case 1: goto Label_0050; } return false; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } void IDisposable.Dispose() { } // Properties T IEnumerator<T>.Current { [DebuggerHidden] get { return this.<>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return this.<>2__current; } } } }
در این کد کامپایلر وضعیتهای مختلفی که برای توقف و ادامهٔ کار MoveNext که مهمترین بخش کد هست را با کمک ترکیبی از switch case و goto پیادهسازی کردهاست که با کمی دقت میتوانید متوجه منطق آن شوید :)
ممکن است به نظرتان برسد که این قطعه کد از نظر (حداقل نامگذاری) در سیشارپ صحیح نیست. اینگونه نامگذاریها که از نظر CLR (و زبان IL) درست ولی از نظر زبان سطح بالا نادرست هستند باعث میشوند که از هرگونه برخورد نامی احتمالی با نامهای معتبر تعریف شده توسط کاربر جلوگیری شود.
احتمالاً اگر پیشزمینه نسبت به این مطلب داشته باشید با خود خواهید گفت که «این که واضح بود، اصلاً وظیفهٔ ماشین در سطح پایین نیست که چنین عملی را پشتیبانی کند». واضحبودن این موضوع برای شما شاید به این دلیل باشد که پیادهسازی yield را قبلاً جای دیگری ندیدهاید. برای درک این مطلب در اینجا نحوهٔ پیادهسازی yield را در پایتون بررسی میکنیم.
def array_iterator(array): length = len(array) index = 0 while index < length: yield array[index] index = index + 1
>>> import dis >>> dis.dis(array_iterator) 2 0 LOAD_GLOBAL 0 (len) 3 LOAD_FAST 0 (array) 6 CALL_FUNCTION 1 9 STORE_FAST 1 (length) 3 12 LOAD_CONST 1 (0) 15 STORE_FAST 2 (index) 4 18 SETUP_LOOP 35 (to 56) >> 21 LOAD_FAST 2 (index) 24 LOAD_FAST 1 (length) 27 COMPARE_OP 0 (<) 30 POP_JUMP_IF_FALSE 55 5 33 LOAD_FAST 0 (array) 36 LOAD_FAST 2 (index) 39 BINARY_SUBSCR 40 YIELD_VALUE 41 POP_TOP 6 42 LOAD_FAST 2 (index) 45 LOAD_CONST 2 (1) 48 BINARY_ADD 49 STORE_FAST 2 (index) 52 JUMP_ABSOLUTE 21 >> 55 POP_BLOCK >> 56 LOAD_CONST 0 (None) 59 RETURN_VALUE
public class SimpleStateMachine : IEnumerable<bool> { public IEnumerator<bool> GetEnumerator() { while (true) { yield return true; yield return false; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } }
new SimpleStateMachine().Take(20).ToList().ForEach(x => Console.WriteLine(x));
مایکروسافت اخیرا علاوه بر تکمیل ORM های خود مانند LINQ to SQL و همچنین Entity framework ، لایه دیگری را نیز بر روی ADO.NET جهت کسانی که به هر دلیلی دوست ندارند با ORMs کار کنند و از نوشتن کوئریهای مستقیم SQL لذت میبرند، ارائه داده است که Microsoft.Data library نام دارد و از قابلیتهای جدید زبان سی شارپ مانند واژه کلیدی dynamic استفاده میکند.
در ادامه قصد داریم جهت بررسی تواناییهای این کتابخانه از بانک اطلاعاتی معروف Northwind استفاده کنیم. این بانک اطلاعاتی را از اینجا میتوانید دریافت کنید.
مراحل استفاده از Microsoft.Data library:
الف) این اسمبلی جدید به همراه پروژه WebMatrix ارائه شده است. بنابراین ابتدا باید آنرا دریافت کنید: +
لازم به ذکر است که این کتابخانه اخیرا به WebMatrix.Data.dll تغییر نام یافته است. (اگر وب را جستجو کنید فقط به Microsoft.Data.dll اشاره شده است)
ب) پس از نصب، ارجاعی را از اسمبلی WebMatrix.Data.dll به پروژه خود اضافه نمائید. این اسمبلی در صفحهی Add References ظاهر نمیشود و باید کامپیوتر خود را برای یافتن آن جستجو کنید که عموما در آدرس زیر قرار دارد:
C:\Program Files\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\WebMatrix.Data.dll
ج) اتصال به بانک اطلاعاتی
پیش فرض اصلی این کتابخانه بانک اطلاعاتی SQL Server CE است. بنابراین اگر قصد استفاده از پروایدرهای دیگری را دارید باید به صورت صریح آنرا ذکر نمائید:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="systemData:defaultProvider" value="System.Data.SqlClient" />
</appSettings>
<connectionStrings>
<add name="Northwind"
connectionString="Data Source=(local);Integrated Security = true;Initial Catalog=Northwind"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
این تعاریف در فایل web.config و یا app.config برنامه وب یا ویندوزی شما قرار خواهند گرفت.
د) نحوهی تعریف کوئریها و دریافت اطلاعات
using System;
using WebMatrix.Data;
namespace TestMicrosoftDataLibrary
{
class Program
{
static void Main(string[] args)
{
getProducts();
Console.Read();
Console.WriteLine("Press a key ...");
}
private static void getProducts()
{
using (var db = Database.Open("Northwind"))
{
foreach (var product in db.Query("select * from products where UnitsInStock < @0", 20))
{
Console.WriteLine(product.ProductName + " " + product.UnitsInStock);
}
}
}
}
}
همچنین نکتهی مهم دیگر آن نحوهی تعریف پارامتر در آن است (همان 0@ ذکر شده) که نسبت به ADO.NET کلاسیک به شدت ساده شدهاست (و نوشتن کوئریهای امن و SQL Injection safe را تسهیل میکند).
در اینجا Database.Open کار گشودن name ذکر شده در فایل کانفیگ برنامه را انجام خواهد داد. اگر بخواهید این تعاریف را در کدهای خود قرار دهید (که اصلا توصیه نمیشود)، میتوان از متد Database.OpenConnectionString استفاده نمود.
یا مثالی دیگر: استفاده از LINQ حین تعریف کوئریها:
private static void getCustomerFax()
{
using (var db = Database.Open("Northwind"))
{
var product = db.Query("SELECT * FROM [Customers] WHERE City=@0", "Paris").FirstOrDefault();
if (product != null)
Console.WriteLine(product.Fax);
else
Console.WriteLine("not found.");
}
}
ه) اجرای کوئریها بر روی بانک اطلاعاتی
private static void ExecQuery()
{
using (var db = Database.Open("Northwind"))
{
int affectedRecords = db.Execute("UPDATE [Customers] SET fax = fax + '*' WHERE City = @0", "Paris");
Console.WriteLine("Affected records: {0}", affectedRecords);
}
}
با استفاده از متد Execute آن میتوان کوئریهای دلخواه خود را بر روی بانک اطلاعاتی اجرا کرد. خروجی آن تعداد رکورد تغییر کرده است.
و) نحوهی اجرای یک رویه ذخیره شده و نمایش خروجی آن
private static void ExecSPShowResult()
{
using (var db = Database.Open("Northwind"))
{
var customer = db.Query("exec CustOrderHist @0", "ALFKI").FirstOrDefault();
if (customer != null)
{
Console.WriteLine(customer.ProductName);
}
}
}
ز) اجرای یک تابع و نمایش خروجی آن
private static void useFuncs()
{
using (var db = Database.Open("Northwind"))
{
var query = db.Query("SELECT dbo.FN_GET_CATEGORY_TREE(@0) as Rec1", 3);
foreach(var tree in query)
{
Console.WriteLine(tree.Rec1);
}
}
}
سؤال : آیا WebMatrix.Data.dll بهتر است یا استفاده از ORMs ؟
در اینجا چون از قابلیتهای داینامیک زبان سی شارپ 4 استفاده میشود، کامپایلر درکی از اشیاء خروجی و خواص آنها برای مثال tree.Rec1 (در مثال آخر) ندارد و تنها در زمان اجرا است که مشخص میشود آیا یک چنین ستونی در خروجی کوئری وجود داشته است یا خیر. اما حین استفاده از ORMs این طور نیست و Schema یک بانک اطلاعاتی پیشتر از طریق نگاشتهای جداول به اشیاء دات نتی، به کامپایلر معرفی میشوند و همین امر سبب میشود تا اگر ساختار بانک اطلاعاتی تغییر کرد، پیش از اجرای برنامه و در حین کامپایل بتوان مشکلات را دقیقا مشاهده نمود و سپس برطرف کرد.
ولی در کل استفاده از این کتابخانه نسبت به ADO.NET کلاسیک بسیار سادهتر بوده، میتوان اشیاء و خواص آنها را مطابق نام جداول و فیلدهای بانک اطلاعاتی تعریف کرد و همچنین تعریف پارامترها و برنامه نویسی امن نیز در آن بسیار سادهتر شده است.
برای مطالعه بیشتر:
Introduction to Microsoft.Data.dll