نظرات مطالب
Blazor 5x - قسمت نهم - مبانی Blazor - بخش 6 - ساده سازی تعاریف ویژگی‌های المان‌ها و انتقال پارامترها به چندین زیر سطح
یک نکته‌ی تکمیلی: بلیزر به ازای هرکامپوننتی که دریافت کننده‌ی مقدار آبشاری است، یک نوتیفیکیشن اطلاع رسانی تنظیم میکند. هنگامیکه یک مقدار آبشاری تغییر می‌کند، مقدار جدید به درخت کامپوننت فرستاده می‌شود و تمام اجزایی که از آن استفاده می‌کنند به‌روزرسانی می‌شوند. بنابراین، Blazor باید به طور مداوم در حال چک کردن این مقدار باشد. این کار در یک برنامه‌ی بزرگ می‌تواند کارآیی را کاهش دهد . اگر CascadeVaue پس از اولین مقداردهی، مقادیرش دیگر هرگز تغییر نکند، می‌توانیم این بررسی مداوم را متوقف کنیم؛ بوسیله‌ی پارامتر IsFixed که در  CascadingValue وجود دارد. این پارامتر به طور پیش فرض روی false تنظیم شده است؛ اما اگر روی true تنظیم شود، Blazor بررسی مداوم را انجام نمی‌دهد. یکی از مواردی که IsFixed کاربرد دارد در ارسال خواص مشترک کامپوننتها از سمت  MainLayout  به سایر کامپوننتها میباشد.
<CascadingValue Value="this" IsFixed="true">
  main
</CascadingValue>
نظرات مطالب
Blazor 5x - قسمت دوم - بررسی ساختار اولیه‌ی پروژه‌های Blazor
یک نکته‌ی تکمیلی: خطاهای بارگذاری پروژه‌های Blazor

ممکن است برای بار اولی که یک پروژه‌ی Blazor را بر اساس قالب پیش‌فرض آن ایجاد کردید، برای مثال در VSCode با ذکر خطای عدم بارگذاری، تشخیص داده نشود.
Failed to load project file BlazorServer.App.csproj.
C:\Program Files\dotnet\sdk\5.0.103\Sdks\Microsoft.NET.Sdk.Razor\build\netstandard2.0\Microsoft.NET.Sdk.Razor.Component.targets(107,5): 
Error: The specified task executable location "C:\Program Files\dotnet\dotnet.exe /M" is invalid.
در این حالت VSCode را بسته و یکبار پروژه را با دستور dotnet run، بیلد و اجرا کنید. سپس VSCode را باز کنید. در این حالت پروژه بدون مشکل بارگذاری می‌شود. یک چنین حالتی در Visual Studio هم گزارش شده‌است و گاهی از اوقات کامپوننت‌های جدیدی را که تعریف می‌شوند، تشخیص نمی‌دهد. این مشکل نیز با Build پروژه و بارگذاری مجدد آن حل می‌شود.
اشتراک‌ها
دوره کار با Angular 16 توسط Web API و Entity Framework Core

Angular 16 CRUD with .NET 7 Web API using Entity Framework Core - Full Course

📑 Contents:
00:00:00 Video Introduction
00:00:40 Angular and ASP.NET Core Udemy Course Demo
00:03:07 Prerequisites
00:03:37 Setting Up Development Environment
00:15:37 Create ASP.NET Core Web API
00:20:07 Understanding Files and Folder Structure
00:25:37 Understanding REST and HTTP Verbs
00:30:10 Create .NET 6 Web API
00:32:41 Our Project and Domain Models
00:41:16 Installing Nuget Packages For Entity Framework Core
00:43:06 DbContext
00:59:26 Running EF Core Migrations
01:03:26 Create Controllers and Actions
01:23:46 Repository Pattern
01:36:46 Create New Angular Application using Angular CLI
01:50:09 Angular Components
02:13:29 CRUD in Angular and ASP.NET Core Web APIs
02:17:21 Angular Forms
02:26:59 Angular Services
02:38:09 CORS
02:42:09 Unsubscribing
 

دوره کار با Angular 16 توسط Web API و Entity Framework Core
اشتراک‌ها
سری 10 قسمتی توسعه‌ی برنامه‌های React با TypeScript از JetBrains

ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way, we'll show test-driven development and emphasize tips and tricks in the IDE.

Other videos from this series:
1. Project Setup: https://youtu.be/wm8WdAB64gw
2. Project Cleanup: https://youtu.be/b0KrB31hN5k
3. Testing: https://youtu.be/Y_TGIsFnvo4
4. Debugging Node.js: https://youtu.be/r1kwXZnO8gw
5. Debugging in Chrome: https://youtu.be/dvmZi_DWu9I
6. TSX and ES6: https://youtu.be/JXrZDUzkc2Q
7. Class Props: https://youtu.be/HYmoeUF9ZH0
8. Class State: https://youtu.be/21-VMTmiV8E
9. Rich Events and Testing: https://youtu.be/OO7OmA5UlQM
10. Presentation Components: https://youtu.be/SnCGW6JUo4E 

سری 10 قسمتی توسعه‌ی برنامه‌های React با TypeScript از JetBrains
مطالب
نکات کار با استثناءها در دات نت
استثناء چیست؟
واژه‌ی استثناء یا exception کوتاه شده‌ی عبارت exceptional event است. در واقع exception یک نوع رویداد است که در طول اجرای برنامه رخ می‌دهد و در نتیجه، جریان عادی برنامه را مختل می‌کند. زمانیکه خطایی درون یک متد رخ دهد، یک شیء (exception object) حاوی اطلاعاتی درباره‌ی خطا ایجاد خواهد شد. به فرآیند ایجاد یک exception object و تحویل دادن آن به سیستم runtime، اصطلاحاً throwing an exception یا صدور استثناء گفته می‌شود که در ادامه به آن خواهیم پرداخت.
بعد از اینکه یک متد استثناءایی را صادر می‌کند، سیستم runtime سعی در یافتن روشی برای مدیریت آن خواهد کرد.
خوب اکنون که با مفهوم استثناء آشنا شدید اجازه دهید دو سناریو را با هم بررسی کنیم.
- سناریوی اول:
فرض کنید یک فایل XML از پیش تعریف شده (برای مثال یک لیست از محصولات) قرار است در کنار برنامه‌ی شما باشد و باید این لیست را درون برنامه‌ی خود نمایش دهید. در این حالت برای خواندن این فایل انتظار دارید که فایل وجود داشته باشد. اگر این فایل وجود نداشته باشد برنامه‌ی شما با اشکال روبرو خواهد شد.
- سناریوی دوم:
فرض کنید یک فایل XML از آخرین محصولات مشاهده شده توسط کاربران را به صورت cache در برنامه‌تان دارید. در این حالت در اولین بار اجرای برنامه توسط کاربر انتظار داریم که این فایل موجود نباشد و اگر فایل وجود نداشته باشد به سادگی می‌توانیم فایل مربوط را ایجاده کرده و محصولاتی را که توسط کاربر مشاهده شده، درون این فایل اضافه کنیم.
در واقع استثناء‌ها بستگی به حالت‌های مختلفی دارد. در مثال اول وجود فایل حیاتی است ولی در حالت دوم بدون وجود فایل نیز برنامه می‌تواند به کار خود ادامه داده و فایل مورد نظر را از نو ایجاد کند.
 استثناها مربوط به زمانی هستند که این احتمال وجود داشته باشد که برنامه طبق انتظار پیش نرود.
برای حالت اول کد زیر را داریم:
public IEnumerable<Product> GetProducts()
{
    using (var stream = File.Read(Path.Combine(Environment.CurrentDirectory, "products.xml")))
    {
        var serializer = new XmlSerializer();
        return (IEnumerable<Product>)serializer.Deserialize(stream);
    }
}
همانطور که عنوان شد در حالت اول انتظار داریم که فایلی بر روی دیسک موجود باشد. در نتیجه نیازی نیست هیچ استثناءایی را مدیریت کنیم (زیرا در واقع اگر فایل موجود نباشد هیچ روشی برای ایجاد آن نداریم).
در مثال دوم می‌دانیم که ممکن است فایل از قبل موجود نباشد. بنابراین می‌توانیم موجود بودن فایل را با یک شرط بررسی کنیم:
public IEnumerable<Product> GetCachedProducts()
{
    var fullPath = Path.Combine(Environment.CurrentDirectory, "ProductCache.xml");
    if (!File.Exists(fullPath))
        return new Product[0];
         
    using (var stream = File.Read(fullPath))
    {
        var serializer = new XmlSerializer();
        return (IEnumerable<Product>)serializer.Deserialize(stream);
    }
}

چه زمانی باید استثناءها را مدیریت کنیم؟
زمانیکه بتوان متدهایی که خروجی مورد انتظار را بر می‌گردانند ایجاد کرد.
اجازه دهید دوباره از مثال‌های فوق استفاده کنیم:
IEnumerable<Product> GetProducts()
همانطور که از نام آن پیداست این متد باید همیشه لیستی از محصولات را برگرداند. اگر می‌توانید اینکار را با استفاده از catch کردن یک استثنا انجام دهید در غیر اینصورت نباید درون متد اینکار را انجام داد.
IEnumerable<Product> GetCachedProducts()
در متد فوق می‌توانستیم از FileNotFoundException برای فایل موردنظر استفاده کنیم؛ اما مطمئن بودیم که فایل در ابتدا وجود ندارد.
در واقع استثنا‌ها حالت‌هایی هستند که غیرقابل پیش‌بینی هستند. این حالت‌ها می‌توانند یک خطای منطقی از طرف برنامه‌نویس و یا چیزی خارج کنترل برنامه‌نویس باشند (مانند خطاهای سیستم‌عامل، شبکه، دیسک). یعنی در بیشتر مواقع این نوع خطاها را نمی‌توان مدیریت کرد.

اگر می‌خواهید استثناء‌ها را catch کرده و آنها را لاگ کنید در بالاترین لایه اینکار را انجام دهید.


چه استثناءهایی باید مدیریت شوند و کدام‌ها خیر؟ 
مدیریت صحیح استثناء‌ها می‌تواند خیلی مفید باشد. همانطور که عنوان شد یک استثناء زمانی رخ می‌دهد که یک حالت استثناء در برنامه اتفاق بیفتد. این مورد را بخاطر داشته باشید، زیرا به شما یادآوری می‌کند که در همه جا نیازی به استفاده از try/catch نیست. در اینجا ذکر این نکته خیلی مهم است:
تنها استثناء‌هایی را catch کنید که بتوانید برای آن راه‌حلی ارائه دهید.
به عنوان مثال اگر در لایه‌ی دسترسی به داده، خطایی رخ دهد و استثناءی SqlException صادر شود، می‌توانیم آن را catch کرده و درون یک استثناء عمومی‌تر قرار دهیم:
public class UserRepository : IUserRepository
{
    public IList<User> Search(string value)
    {
        try
        {
              return CreateConnectionAndACommandAndReturnAList("WHERE value=@value", Parameter.New("value", value));
        }
        catch (SqlException err)
        {
             var msg = String.Format("Ohh no!  Failed to search after users with '{0}' as search string", value);
             throw new DataSourceException(msg, err);
        }
    }
}
همانطور که در کد فوق مشاهده می‌کنید به محض صدور استثنای SqlException آن را درون قسمت catch به صورت یک استثنای عمومی‌تر همراه با افزودن یک سری اطلاعات جدید صادر می‌کنیم. اما همانطور که عنوان شد کار لاگ کردن استثناءها را بهتر است در لایه‌های بالاتر انجام دهیم.
اگر مطمئن نیستید که تمام استثناء‌ها توسط شما مدیریت شده‌اند، می‌توانید در حالت‌های زیر، دیگر استثناءها را مدیریت کنید:
ASP.NET: می‌توانید Aplication_Error را پیاده‌سازی کنید. در اینجا فرصت خواهید داشت تا تمامی خطاهای مدیریت نشده را هندل کنید.
WinForms: استفاده از رویدادهای Application.ThreadException و AppDomain.CurrentDomain.UnhandledException 
WCF: پیاده‌سازی اینترفیس IErrorHandler 
ASMX: ایجاد یک Soap Extension سفارشی
ASP.NET WebAPI


چه زمان‌هایی باید یک استثناء صادر شود؟ 
صادر کردن یک استثناء به تنهایی کار ساده‌ایی است. تنها کافی است throw را همراه شیء exception (exception object) فراخوانی کنیم. اما سوال اینجاست که چه زمانی باید یک استثناء را صادر کنیم؟ چه داده‌هایی را باید به استثناء اضافه کنیم؟ در ادامه به این سوالات خواهیم پرداخت.
همانطور که عنوان گردید استثناءها زمانی باید صادر شوند که یک استثناء اتفاق بیفتد.

اعتبارسنجی آرگومان‌ها
ساده‌ترین مثال، آرگومان‌های مورد انتظار یک متد است:
public void PrintName(string name)
{
     Console.WriteLine(name);
}
در حالت فوق انتظار داریم مقداری برای پارامتر name تعیین شود. متد فوق با آرگومان null نیز به خوبی کار خواهد کرد؛ یعنی مقدار خروجی یک خط خالی خواهد بود. از لحاظ کدنویسی متد فوق به خوبی کار خود را انجام می‌دهد اما خروجی مورد انتظار کاربر نمایش داده نمی‌شود. در این حالت نمی‌توانیم تشخیص دهیم مشکل از کجا ناشی می‌شود.
مشکل فوق را می‌توانیم با صدور استثنای ArgumentNullException رفع کنیم:
public void PrintName(string name)
{
    if (name == null) throw new ArgumentNullException("name");
     
     Console.WriteLine(name);
}
خوب، name باید دارای طول ثابت و همچنین ممکن است حاوی عدد و حروف باشد:
public void PrintName(string name)
{
    if (name == null) throw new ArgumentNullException("name");
    if (name.Length < 5 || name.Length > 10) throw new ArgumentOutOfRangeException("name", name, "Name must be between 5 or 10 characters long");
    if (name.Any(x => !char.IsAlphaNumeric(x)) throw new ArgumentOutOfRangeException("name", name, "May only contain alpha numerics");
     
     Console.WriteLine(name);
}
برای حالت فوق و همچنین جلوگیری از تکرار کدهای داخل متد PrintName می‌توانید یک متد Validator برای کلاسی با نام Person ایجاد کنید.
حالت دیگر صدور استثناء، زمانی است که متدی خروجی مورد انتظارمان را نتواند تحویل دهد. یک مثال بحث‌برانگیز متدی با امضای زیر است:
public User GetUser(int id)
{
}
کاملاً مشخص است که متدی همانند متد فوق زمانیکه کاربری را پیدا نکند، مقدار null را برمی‌گرداند. اما این روش درستی است؟ خیر؛ زیرا همانطور که از نام این متد پیداست باید یک کاربر به عنوان خروجی برگردانده شود.
با استفاده از بررسی null کدهایی شبیه به این را در همه جا خواهیم داشت:
var user = datasource.GetUser(userId);
if (user == null)
    throw new InvalidOperationException("Failed to find user: " + userId);
// actual logic here
به این چنین کدهایی معمولاً The null cancer گفته می‌شود (سرطان نال!) زیرا اجازه داده‌ایم متد، خروجی null را بازگشت دهد. به جای کد فوق می‌توانیم از این روش استفاده کنیم:
public User GetUser(int id)
{
    if (id <= 0) throw new ArgumentOutOfRangeException("id", id, "Valid ids are from 1 and above. Do you have a parsing error somewhere?");
    
    var user = db.Execute<User>("WHERE Id = ?", id);
    if (user == null)
        throw new EntityNotFoundException("Failed to find user with id " + id);
        
    return user;
}
نکته‌ایی که باید به آن توجه کنید این است که در هنگام صدور یک استثناء اطلاعات کافی را نیز به آن پاس دهید. به عنوان مثال در EntityNotFoundException مثال فوق پاس دادن "Failed to find user with id " + id کار دیباگ را برای مصرف کننده، راحتر خواهد کرد.


خطاهای متداول حین کار با استثناءها  


  • صدور مجدد استثناء و از بین بردن stacktrace

کد زیر را در نظر بگیرید:

try
{
    FutileAttemptToResist();
}
catch (BorgException err)
{
     _myDearLog.Error("I'm in da cube! Ohh no!", err);
    throw err;
}
مشکل کد فوق قسمت throw err است. این خط کد، محتویات stacktrace را از بین برده و استثناء را مجدداً برای شما ایجاد خواهد کرد. در این حالت هرگز نمی‌توانیم تشخیص دهیم که منبع خطا از کجا آمده است. در این حالت پیشنهاد می‌شود که تنها از throw استفاده شود. در این حالت استثناء اصلی مجدداً صادر گردیده و مانع حذف شدن محتویات stacktrace خواهد شد(+).
  • اضافه نکردن اطلاعات استثناء اصلی به استثناء جدید

یکی دیگر از خطاهای رایج اضافه نکردن استثناء اصلی حین صدور استثناء جدید است:

try
{
    GreaseTinMan();
}
catch (InvalidOperationException err)
{
    throw new TooScaredLion("The Lion was not in the m00d", err); //<---- استثناء اصلی بهتر است به استثناء جدید پاس داده شود
}
  • ارائه ندادن context information

در هنگام صدور یک استثناء بهتر است اطلاعات دقیقی را به آن ارسال کنیم تا دیباگ کردن آن به راحتی انجام شود. به عنوان مثال کد زیر را در نظر داشته باشید:

try
{
   socket.Connect("somethingawful.com", 80);
}
catch (SocketException err)
{
    throw new InvalidOperationException("Socket failed", err);  
}
هنگامی که کد فوق با خطا مواجه شود نمی‌توان تنها با متن Socket failed تشخیص داد که مشکل از چه چیزی است. بنابراین پیشنهاد می‌شود اطلاعات کامل و در صورت امکان به صورت دقیق را به استثناء ارسال کنید. به عنوان مثال در کد زیر سعی شده است تا حد امکان context information کاملی برای استثناء ارائه شود:
void IncreaseStatusForUser(int userId, int newStatus)
{
    try
    {
         var user  = _repository.Get(userId);
         if (user == null)
             throw new UpdateException(string.Format("Failed to find user #{0} when trying to increase status to {1}", userId, newStatus));
    
         user.Status = newStatus;
         _repository.Save(user);
    }
   catch (DataSourceException err)
   {
       var errMsg = string.Format("Failed to find modify user #{0} when trying to increase status to {1}", userId, newStatus);
        throw new UpdateException(errMsg, err);
   }

نحوه‌ی طراحی استثناءها 
برای ایجاد یک استثناء سفارشی می‌توانید از کلاس Exception ارث‌بری کنید و چهار سازنده‌ی آن را اضافه کنید:
public NewException()
public NewException(string description )
public NewException(string description, Exception inner)
protected or private NewException(SerializationInfo info, StreamingContext context)
سازنده اول به عنوان default constructor شناخته می‌شود. اما پیشنهاد می‌شود که از آن استفاده نکنید، زیرا یک استثناء بدون context information از ارزش کمی برخوردار خواهد بود.
سازنده‌ی دوم برای تعیین description بوده و همانطور که عنوان شد ارائه دادن context information از اهمیت بالایی برخوردار است. به عنوان مثال فرض کنید استثناء KeyNotFoundException که توسط کلاس Dictionary صادر شده است را دریافت کرده‌اید. این استثناء زمانی صادر خواهد شد که بخواهید به عنصری که درون دیکشنری پیدا نشده است دسترسی داشته باشید. در این حالت پیام زیر را دریافت خواهید کرد:
“The given key was not present in the dictionary.”
حالا فرض کنید اگر پیام به صورت زیر باشد چقدر باعث خوانایی و عیب‌یابی ساده‌تر خطا خواهد شد:
“The key ‘abrakadabra’ was not present in the dictionary.”
در نتیجه تا حد امکان سعی کنید که context information شما کاملتر باشد.
سازنده‌ی سوم شبیه به سازنده‌ی قبلی عمل می‌کند با این تفاوت که توسط پارامتر دوم می‌توانیم یک استثناء دیگر را catch کرده یک استثناء جدید صادر کنیم.
سازنده‌ی سوم زمانی مورد استفاده قرار می‌گیرد که بخواهید از Serialization پشتیبانی کنید (به عنوان مثال ذخیره‌ی استثناءها درون فایل و...)

خوب، برای یک استثناء سفارشی حداقل باید کدهای زیر را داشته باشیم:
public class SampleException : Exception
{
    public SampleException(string description)
        : base(description)
    {
        if (description == null) throw new ArgumentNullException("description");
    }
 
    public SampleException(string description, Exception inner)
        : base(description, inner)
    {
        if (description == null) throw new ArgumentNullException("description");
        if (inner == null) throw new ArgumentNullException("inner");
    }
 
    public SampleException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }
}

اجباری کردن ارائه‌ی Context information:
برای اجباری کردن context information کافی است یک فیلد اجباری درون سازنده تعریف کنیم. برای مثال اگر بخواهیم کاربر HTTP status code را برای استثناء ارائه دهد باید سازنده‌ها را اینگونه تعریف کنیم:
public class HttpException : Exception
{
    System.Net.HttpStatusCode _statusCode;
     
    public HttpException(System.Net.HttpStatusCode statusCode, string description)
        : base(description)
    {
        if (description == null) throw new ArgumentNullException("description");
        _statusCode = statusCode;
    }
 
    public HttpException(System.Net.HttpStatusCode statusCode, string description, Exception inner)
        : base(description, inner)
    {
        if (description == null) throw new ArgumentNullException("description");
        if (inner == null) throw new ArgumentNullException("inner");
        _statusCode = statusCode;
    }
 
    public HttpException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }
     
    public System.Net.HttpStatusCode StatusCode { get; private set; }
 
}
همچنین بهتر است پراپرتی Message را برای نمایش پیام مناسب بازنویسی کنید:
public override string Message
{
        get { return base.Message + "\r\nStatus code: " + StatusCode; }
}
مورد دیگری که باید در کد فوق مد نظر داشت این است که status code قابلیت سریالایز شدن را ندارد. بنابراین باید متد GetObjectData را برای سریالایز کردن بازنویسی کنیم:
public class HttpException : Exception
{
    // [...]
 
    public HttpException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        // this is new
        StatusCode = (HttpStatusCode) info.GetInt32("HttpStatusCode");
    }
 
    public HttpStatusCode StatusCode { get; private set; }
 
    public override string Message
    {
        get { return base.Message + "\r\nStatus code: " + StatusCode; }
    }
 
    // this is new
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        info.AddValue("HttpStatusCode", (int) StatusCode);
    }
}
در اینحالت فیلدهای اضافی در طول فرآیند Serialization به خوبی سریالایز خواهند شد.

در حین صدور استثناءها همیشه باید در نظر داشته باشیم که چه نوع context information را می‌توان ارائه داد، این مورد در یافتن راه‌حل خیلی کمک خواهد کرد.


طراحی پیام‌های مناسب 
پیام‌های exception مختص به توسعه‌دهندگان است نه کاربران نهایی.
نوشتن این نوع پیام‌ها برای برنامه‌نویس کار خسته‌کننده‌ایی است. برای مثال دو مورد زیر را در نظر داشته باشید:
throw new Exception("Unknown FaileType");
throw new Exception("Unecpected workingDirectory");
این نوع پیام‌ها حتی اگر از لحاظ نوشتاری مشکلی نداشته باشند یافتن راه‌حل را خیلی سخت خواهند کرد. اگر در زمان برنامه‌نویسی با این نوع خطاها روبرو شوید ممکن است با استفاده از debugger ورودی نامعتبر را پیدا کنید. اما در یک برنامه و خارج از محیط برنامه‌نویسی، یافتن علت بروز خطا خیلی سخت خواهد بود.
توسعه‌دهندگانی که exception message را در اولویت قرار می‌دهند، معتقد هستند که از لحاظ تجربه‌ی کاربری پیام‌ها تا حد امکان باید فاقد اطلاعات فنی باشد. همچنین همانطور که پیش‌تر عنوان گردید این نوع پیام‌ها همیشه باید در بالاترین سطح نمایش داده شوند نه در لایه‌های زیرین. همچنین پیام‌هایی مانند Unknown FaileType نه برای کاربر نهایی، بلکه برای برنامه‌نویس نیز ارزش چندانی ندارد زیرا فاقد اطلاعات کافی برای یافتن مشکل است.
در طراحی پیام‌ها باید موارد زیر را در نظر داشته باشیم:
- امنیت:
یکی از مواردی که از اهمیت بالایی برخوردار است مسئله امنیت است از این جهت که پیام‌ها باید فاقد مقادیر runtime باشند. زیرا ممکن است اطلاعاتی را در خصوص نحوه‌ی عملکرد سیستم آشکار سازند.
- زبان:
همانطور که عنوان گردید پیام‌های استثناء برای کاربران نهایی نیستند، زیرا کاربران نهایی ممکن است اشخاص فنی نباشند، یا ممکن است زبان آنها انگلیسی نباشد. اگر مخاطبین شما آلمانی باشند چطور؟ آیا تمامی پیام‌ها را با زبان آلمانی خواهید نوشت؟ اگر هم اینکار را انجام دهید تکلیف استثناء‌هایی که توسط Base Class Library و دیگر کتابخانه‌های thirt-party صادر می‌شوند چیست؟ اینها انگلیسی هستند.

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

خب اگر پیام‌ها برای کاربران نهایی نیستند، پس برای کسانی مورد استفاده قرار خواهند گرفت؟ در واقع این نوع پیام می‌تواند به عنوان یک documentation برای سیستم شما باشند.
فرض کنید در حال استفاده از یک کتابخانه جدید هستید به نظر شما کدام یک از پیام‌های زیر مناسب هستند:
"Unecpected workingDirectory"
یا:
"You tried to provide a working directory string that doesn't represent a working directory. It's not your fault, because it wasn't possible to design the FileStore class in such a way that this is a statically typed pre-condition, but please supply a valid path to an existing directory.

"The invalid value was: "fllobdedy"."
یافتن مشکل در پیام اول خیلی سخت خواهد بود زیرا فاقد اطلاعات کافی برای یافتن مشکل است. اما پیام دوم مشکل را به صورت کامل توضیح داده است. در حالت اول شما قطعاً نیاز خواهید داشت تا از دیباگر برای یافتن مشکل استفاده کنید. اما در حالت دوم پیام به خوبی شما را برای یافتن راه‌حل راهنمایی می‌کند.
همیشه برای نوشتن پیام‌های مناسب سعی کنید از لحاظ نوشتاری متن شما مشکلی نداشته باشد، اطلاعات کافی را درون پیام اضافه کنید و تا حد امکان نحوه‌ی رفع مشکل را توضیح دهید
نظرات مطالب
امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت دهم- ذخیره سازی اطلاعات کاربران IDP در بانک اطلاعاتی
من میخواهم IDP خودمون رو با کمک is4 مستقر کنم. مشکل اینه که سامانه‌ها و نرم افزارهایی هستند که خودمون ننوشتیم و نمینویسیم، بلکه توسط پیمانکاران مختلف (با معماری‌های مختلف) طراحی شدن و هر کدوم بصورت جزیره ای (احراز هویت مستقل دارن ولی مثلا کد ملی بین همه مشترکه) دارن کار میکنن با یوزرهای خودشون. میخواهیم در نهایت بهشون یک فایل راهنما بدیم که طبق اون دستورالعمل (و بر اساس client id , client secret خودشون)، هرکدوم از اونها به idp ما، متصل بشن جهت احراز هویت و گرفتن اطلاعات اصلی کاربران (مباحث کانورت اطلاعات و حواشی اینچنین رو کار ندارم و فرض میکنیم مشکلی نیست)
1: با توجه به اینکه IS4 بر اساس openid connect + oauth2 پیاده سازی شده و مورد تایید هم می‌باشد و بر اساس ارسال درخواست‌ها روی front channel و back channel هست، آیا محدودیت هایی برای تنوع بسترهای مختلف بعنوان کلاینت هست یا خیر؟
مثلا برای
php, oracle adf, asp.net web forms, asp.net mvc, ...

2: چه مقدار تغییرات خواهند داشت؟ فقط روال ورود و خروج رو باید تغییر بدن؟ با توجه به اینکه مثلا روال user, role, user-role کلاسیک خودشون رو دارن جهت authorization لوکال منابع داخلیشون
3: ما فقط میخواهیم از is4 بعنوان مخزن مشترک " اطلاعات اصلی" کاربران و درگاه ورود/خروج مشترک استفاده کنیم. یعنی :
  • در ابتدا کاربر میاد به idp و رجیستر میکنه تا بتونه از سامانه‌های مختلف متصل به idp استفاده کنه
  • کاربر جهت لاگین در کلاینت (سامانه)، هدایت میشه به idp (این کار توسط همون کلاینت انجام میشه)
  • بعد از احراز هویت در idp، کاربر ریدایرکت میشه به همون برنامه (کلاینت)
  • حالا وقتی برمیگرده به کلاینت، اون برنامه، "اطلاعات اصلی" کاربر (که از idp برگشته) رو داره
  • کاربر رو در دیتابیس لوکال خودش جستجو میکنه (مثلا بر اساس subject_id)؛ یا پیداش میکنه و یا اگه کاربر در دیتابیس لوکال اون سامانه وجود نداشته باشه، بعنوان کاربر جدید درجش میکنه
  • با کاربر مرحله قبل، در سامانه لوکال لاگین میکنه
  • دیگه برنامه لوکال (کلاینت) با idp کاری نداره. چون سیاست داخلی خودش رو داره جهت هندل کردن session‌ها و دسترسی‌ها بر اساس role‌های لوکال در اون برنامه. تا زمانی که بخواد کاربر logout کنه.
  • برای logout، کاربر در کلاینت لاگ اوت و سپس در سمت idp لاگ اوت میشه و مجددا به homepage سامانه لوکال ریدایرکت میشه
آیا سناریو بالا درسته؟
4: با توجه به توضیحات مطروحه، برای این تنوع کلاینت‌ها که بالاتر عنوان کردم، باید از hybrid flow استفاده کنیم یا authorization code + PKCE؟ یا باز برای هر کلاینت جداگانه بررسی و تصمیم گیری کنیم بسته به نوع مکانیزمش
اشتراک‌ها
4.Visual Studio 2017 15.8 منتشر شد
مطالب
آشنایی با NHibernate - قسمت نهم

استفاده از Log4Net جهت ثبت خروجی‌های SQL حاصل از NHibernate

هنگام استفاده از NHibernate، پس از افزودن ارجاعات لازم به اسمبلی‌های مورد نیاز آن به برنامه، یکی از اسمبلی‌هایی که به پوشه build برنامه به صورت خودکار کپی می‌شود، فایل log4net.dll است (حتی اگر ارجاعی را به آن اضافه نکرده باشیم) که جهت ثبت وقایع مرتبط با NHibernate مورد استفاده قرار می‌گیرد. خوب اگر مجبوریم که این وابستگی کتابخانه NHibernate را نیز در پروژه‌های خود داشته باشیم، چرا از آن استفاده نکنیم؟!
شرح مفصل استفاده از این کتابخانه سورس باز را در سایت اصلی آن می‌توان مشاهده کرد:


برای اینکه از این کتابخانه در برنامه خود جهت ثبت عبارات SQL تولیدی توسط NHibernate استفاده کنیم، باید مراحل زیر طی شوند:
الف) ارجاعی را به اسمبلی log4net.dll اضافه نمائید (کنار اسمبلی NHibernate در پوشه build برنامه باید موجود باشد)
ب) فایل app.config برنامه را (برنامه ویندوزی) به صورت زیر ویرایش کرده و چند سطر مربوطه را اضافه نمائید (در مورد برنامه‌های وب هم به همین شکل است. configSections فایل web.config تنظیم شده و سپس تنظیمات log4net را قبل از بسته شدن تگ configuration اضافه نمائید ) :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>

<connectionStrings>
<!--NHSessionManager-->
<add name="DbConnectionString"
connectionString="Data Source=(local);Initial Catalog=HelloNHibernate;Integrated Security = true"/>
</connectionStrings>

<log4net>
<appender name="rollingFile"
type="log4net.Appender.RollingFileAppender,log4net" >
<param name="File" value="NHibernate_Log.txt" />
<param name="AppendToFile" value="true" />
<param name="DatePattern" value="yyyy.MM.dd" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="500KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout,log4net">
<conversionPattern value="%d %p %m%n" />
</layout>
</appender>
<logger name="NHibernate.SQL">
<level value="ALL" />
<appender-ref ref="rollingFile" />
</logger>
</log4net>

</configuration>
ج) سپس باید فراخوانی زیر نیز در ابتدای کار برنامه صورت گیرد:

log4net.Config.XmlConfigurator.Configure();
در یک برنامه ASP.Net این فراخوانی باید در Application_Start فایل Global.asax.cs صورت گیرد.
یا در یک برنامه از نوع WinForms تنها کافی است سطر زیر را به فایل AssemblyInfo.cs برنامه اضافه کرد:

// Configure log4net using the .config file
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
یا این سطر را به فایل Global.asax.cs یک برنامه ASP.Net نیز می‌توان اضافه کرد. Watch=true آن، با کمک FileSystemWatcher تغییرات فایل کانفیگ را تحت نظر داشته و هر بار که تغییر کند بلافاصله، تغییرات جدید را اعمال خواهد کرد.

د) هنگام استفاده از کتابخانه Fluent NHibernate حتما باید متد ShowSql در جایی که دیتابیس برنامه را تنظیم می‌کنیم (Fluently.Configure().Database) ذکر گردد (که نمونه آن‌را در مثال‌های قسمت‌های قبل ملاحظه‌ کرده‌اید).

توضیحاتی در مورد تنظیمات فوق:
configSections حتما باید در ابتدای فایل app.config‌ ذکر شود و گرنه برنامه کار نخواهد کرد.
سپس کانکشن استرینگ مورد استفاده در قسمت کانفیگ برنامه ذکر شده است.
در ادامه تنظیمات استاندارد مربوط به log4net را مشاهده می‌کنید.
در تنظیمات این کتابخانه، appender مشخص کننده محل ثبت وقایع است. زمانیکه که از RollingFileAppender استفاده کنیم، اطلاعات را در یک سری فایل ذخیره خواهد کرد (امکان ثبت وقایع در EventLog ویندوز، ارسال از طریق ایمیل و غیره نیز میسر است که جهت توضیحات بیشتر می‌توان به مستندات آن رجوع نمود).
سپس نام فایلی که اطلاعات وقایع در آن ثبت خواهند شد ذکر شده است (برای مثال NHibernate_Log.txt)، در ادامه مشخص گردیده که اطلاعات باید هر بار به این فایل Append و اضافه شوند. سطرهای بعدی مشخص می‌کنند که هر زمانیکه این لاگ فایل به 10 مگابایت رسید، یک فایل جدید تولید کن و هر بار 10 فایل آخر را نگه دار و مابقی فایل‌های قدیمی را حذف کن.
در قسمت PatternLayout مشخصات می‌کنیم که خروجی ثبت شده با چه فرمتی باشد. برای مثال یک سطر خروجی مطابق با تنظیمات فوق به شکل زیر خواهد بود:

2009-10-18 20:03:54,187 DEBUG INSERT INTO [Student] (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'Vahid'
در قسمت Logger یک نام دلخواه ذکر شده و میزان اطلاعاتی که باید درج شود، از طریق مقدار level مورد نظر، قابل تنظیم است که می‌تواند یکی از مقادیر ALL ،DEBUG ،INFO ،WARN ،ERROR ،FATAL و یا OFF باشد. اینجا level در نظر گرفته شده ALL است که تمامی اطلاعات مرتبط با اعمال پشت صحنه NHibernate را لاگ خواهد کرد.
توسط appender-ref آن appender ایی را که در ابتدای کار تعریف و تنظیم کردیم، مشخص خواهیم کرد.

اگر هم با برنامه نویسی بخواهیم اطلاعاتی را به این لاگ فایل اضافه کنیم تنها کافی است بنویسیم:

log4net.LogManager.GetLogger("NHibernate.SQL").Info("test1");

اطلاعات بیشتر

ادامه دارد ...

اشتراک‌ها
ساختن یک برنامه خیلی بزرگ جاوا اسکریپت در TypeScript
How can you survive a project that uses JavaScript massively on both the client and the server using node.js? The project is about on-line development components, such as the TypeScript playground (http://www.typescriptlang.org/Playground/) or Visual Studio Online “Monaco”, which enables users to edit Azure Web Sites online. We had an existing large JavaScript code base and we wanted to give TypeScript a try. Today the project is one of the largest TypeScript code bases inside Microsoft with more than 200k lines of TypeScript in production. This session gives a quick introduction into TypeScript and then takes a deep look at how TypeScript and other technologies were used to successfully scale up a large JavaScript project that ships in some of Microsoft’s biggest products and services
ساختن یک برنامه خیلی بزرگ جاوا اسکریپت در TypeScript
اشتراک‌ها
کتاب PHP Succinctly

Known for its straightforward simplicity, PHP is an open source, general-purpose scripting language oriented for web development. In PHP Succinctly, author José Roberto Olivas Mendoza guides newcomers through PHP’s basics, which includes deployment, programming themes such as variables, decision making, arrays, functions, and databases, and the creation of a functional webpage that will connect to a database. By the end, you’ll be ready to join the vast community of PHP users around the world.

Table of Contents
  1. Introduction to PHP
  2. Deploying PHP
  3. PHP Basics
  4. Functions and File Inclusion
  5. Files and Databases
  6. A Contact List Website
  7. General Summary
  8. General Conclusions 
کتاب PHP Succinctly