Here’s where you can download our First Responder Kit complete with:
- Our scripts: sp_Blitz, sp_BlitzCache, sp_BlitzIndex, sp_AskBrent, sp_BlitzRS, sp_BlitzTrace
- Our e-books: SQL Server Setup Guide, AlwaysOn Availability Groups Checklist, the DBA Training Plan, How to Develop Your DBA Career
- Our posters: Table Partitioning, Isolation Levels, Bandwidth Reference
- Our whitepapers: Factors of Cloud Success, Sizing Up Solid State Drives
Introduction
Chapter 1 Setting Up
Chapter 2 Domain Model
Chapter 3 Database
Chapter 4 Getting Data from the Database
Chapter 5 Writing Data to the Database
Chapter 6 Spatial Data Types
Chapter 7 Handling Events
Chapter 8 Extending Entity Framework
Chapter 9 Exposing Data to the World
Chapter 10 Tracing and Profiling
Chapter 11 Performance Optimizations
Chapter 12 Common Pitfalls
WhatsApp for Windows Phone is one of the few apps on Windows 10 Mobile today that continues to receive frequent updates from its developer. Unfortunately, the app itself is one based on Silverlight, which is what apps built for Windows Phone 8.1 used back in 2014. This means the app isn't a Universal Windows Platform app (UWP,) and as such doesn't run across all the different Windows 10 platforms and devices available today.
آموزش Unit Testing در #C با xUnit
How to become a Zero to Hero in Unit Testing with C# and xUnit ?
Timeline:
Background 0:00:00
Introduction To Fluent Assertions 0:01:12
Setting Up Fluent Assertions 0:02:39
Basic Assertions with Fluent Assertions 0:09:19
Advanced Assertions with Fluent Assertions 0:20:28
Custom Assertions with Fluent Assertions 0:28:32
Test the Custom Person Assertions 0:47:56
Best Practices For Using Fluent Assertions 0:54:21
Issues fixed in Visual Studio 2019 version 16.0.4
- Attach to iis prompt to attach multiple times.
- WPF applications broken after FW 4.8 update.
- installing typescript 3.3 disables version 2.2 compilation.
- Unable to attach debugger when attaching multiple startup projects on Visual Studio 2019.
- Known issue: Application tab of VB project property pages is broken when isntalling the .NET Framework 4.8 Developer Pack.
- Visual Studio extensions may fail to install attempting to overwrite core NuGet files.
- microsoft.diagnosticshub.dotnetobjectalloc.vsix missing from offline installer.
Security Advisory Notices
CVE-2019-0727 Diagnostics Hub Standard Collector Service Elevation of Privilege Vulnerability
An elevation of privilege vulnerability exists when the Diagnostics Hub Standard Collector Service improperly performs certain file operations. An attacker who successfully exploited this vulnerability could delete files in arbitrary locations. To exploit this vulnerability, an attacker would require unprivileged access to a vulnerable system. The security update addresses the vulnerability by securing locations the Diagnostics Hub Standard Collector performs file operations in.
مقدمهای بر داکر، قسمت سوم
Step 5/7 : RUN npm i ---> [Warning] The requested image's platform (linux/amd64) does not match the detected host platform (windows/amd64) and no specific platform was requested ---> Running in dc37481d5781 added 48 packages, and audited 49 packages in 6s found 0 vulnerabilities npm notice npm notice New minor version of npm available! 8.5.5 -> 8.7.0 npm notice Changelog: <https://github.com/npm/cli/releases/tag/v8.7.0> npm notice Run `npm install -g npm@8.7.0` to update! npm notice The command '/bin/sh -c npm i' returned a non-zero code: 4294967295: failed to shutdown container: container dc37481d5781615dbf01c129f2322f06fcac8851a8a4078a6273438a8427254a encountered an error during hcsshim::System::waitBackground: failure in a Windows system call: The virtual machine or container with the specified identifier is not running. (0xc0370110): subsequent terminate failed container dc37481d5781615dbf01c129f2322f06fcac8851a8a4078a6273438a8427254a encountered an error during hcsshim::System::waitBackground: failure in a Windows system call: The virtual machine or container with the specified identifier is not running. (0xc0370110)
CREATE TABLE Users ( id INT NOT NULL AUTO_INCREMENT, first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, gender ENUM('Male', 'Female') NOT NULL, PRIMARY KEY (id) ); CREATE TABLE BlogPosts ( id INT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, user_id INT NOT NULL, KEY user_id (user_id), CONSTRAINT `blogposts_ibfk_1` FOREIGN KEY (user_id) REFERENCES Users (id), PRIMARY KEY (id) )
SELECT U.id, CONCAT_WS(' ', U.first_name, U.last_name) AS FullName, BP.title FROM Users AS U JOIN BlogPosts AS BP ON U.id = BP.user_id;
تبدیل یک ساختار Relational به JSON
میتوانیم خروجی موردنظر را در قالب JSON نیز کوئری بگیریم:
SELECT JSON_OBJECT('id', U.id, 'user', CONCAT_WS(' ', U.first_name, U.last_name), 'title', BP.title) FROM Users AS U JOIN BlogPosts AS BP ON U.id = BP.user_id;
به عنوان مثال میتوانیم لیست کاربران را به همراه بلاگ پستهایشان، اینگونه کوئری بگیریم:
SELECT JSON_OBJECT('user', CONCAT_WS(' ', U.first_name, U.last_name), 'blog_posts', JSON_ARRAYAGG(JSON_OBJECT('id', BP.id, 'title', BP.title))) AS JSON FROM Users AS U JOIN BlogPosts AS BP ON U.id = BP.user_id GROUP BY U.id;
خروجی کوئری فوق اینچنین خواهد بود:
تبدیل یک ساختار JSON به Relational
همچنین میتوانیم یک ساختار JSON را به صورت Relational تبدیل کنیم. اینکار توسط تابع JSON_TABLE قابل انجام است. کاری که این تابع انجام میدهد، ایجاد یک جدول موقت و کپی کردن دیتای موردنظر درون آن است. فرض کنید ساختار JSON زیر را به اینصورت درون دیتابیس ذخیره کردهایم:
{ "id": "1", "new": false, "sku": "asdf123", "tag": ["fashion", "men", "jacket", "full sleeve"], "name": "Lorem ipsum jacket", "image": [ "/assets/img/product/fashion/1.jpg", "/assets/img/product/fashion/3.jpg", "/assets/img/product/fashion/6.jpg", "/assets/img/product/fashion/8.jpg", "/assets/img/product/fashion/9.jpg" ], "price": 12.45, "rating": 4, "category": ["fashion", "men"], "discount": 10, "offerEnd": "October 5, 2020 12:11:00", "saleCount": 54, "description": { "fullDescription": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?", "shortDescription": "Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur." } }
اکنون میخواهیم چنین خروجیای داشته باشیم:
کوئری موردنیاز برای تهیه خروجی فوق اینچنین خواهد بود:
SELECT newTable.* FROM experiments.productMetadata, JSON_TABLE( data, "$" COLUMNS ( id INT PATH "$.id", new CHAR(5) PATH "$.new", sku CHAR(20) PATH "$.sku", price FLOAT PATH "$.price", rating FLOAT PATH "$.rating", name CHAR(255) PATH "$.name", discount FLOAT PATH "$.discount", offerEnd TEXT PATH "$.offerEnd", saleCount INT PATH "$.saleCount", description TEXT PATH "$.description.shortDescription" ) ) AS newTable;
تابع JSON_TABLE دو ورودی نیاز خواهد داشت؛ ورودی اول ستون JSONی است که میخوایم از آن کوئری بگیریم. ورودی دوم با تعیین path شروع خواهد شد. از آنجائیکه محتوای داخل ستون data به صورت آبجکت ذخیره شدهاست، از $ استفاده کردهایم که به معنای داکیومنت جاری است. سپس توسط کلمه کلیدی COLUMNS ساختار جدول موقتمان را تعریف خواهیم کرد. این ساختار به صورت یک آرگومان به COLUMNS ارسال خواهد شد و شبیه به ساختار CREATE TABLE است؛ با این تفاوت که بعد از تعریف نوع دادهای هر ستون باید مسیر رسیدن به مقدار موردنظر را نیز تعیین کنیم که در واقع همان سینتکس pathی است که در مثالهای قبل نیز بررسی کردیم. به عنوان مثال برای رسیدن به مقدار پراپرتی name، مسیر را به صورت name.$ نوشتهایم. این path از آرایه نیز پشتیبانی میکند؛ مثلاً برای دسترسی به عنصر اول آرایه tag کافی است اینگونه عمل کنیم:
tag CHAR(20) PATH "$.tag[0]"
همچنین تابع JSON_TABLE، از ساختارهای تودرتو نیز پشتیبانی میکند. به عنوان مثال برای داشتن مقادیر tag, category, image در خروجی میتوانیم از کلمه کلیدی NESTED استفاده کنیم:
SELECT newTable.* FROM experiments.productMetadata, JSON_TABLE( data, "$" COLUMNS ( id INT PATH "$.id", new CHAR(5) PATH "$.new", sku CHAR(20) PATH "$.sku", price FLOAT PATH "$.price", rating FLOAT PATH "$.rating", NESTED PATH '$.tag[*]' COLUMNS (tag TEXT PATH '$'), name CHAR(255) PATH "$.name", discount FLOAT PATH "$.discount", offerEnd TEXT PATH "$.offerEnd", saleCount INT PATH "$.saleCount", description TEXT PATH "$.description.shortDescription", NESTED PATH '$.image[*]' COLUMNS (image TEXT PATH '$'), NESTED PATH '$.category[*]' COLUMNS (category TEXT PATH '$') ) ) AS newTable;
درون COLUMNS میتوانیم یک name FOR ORDINALITY نیز تعیین کنیم. این فیلد دقیقاً مشابه AUTO INCREMENT در ساختار CREATE TABLE میباشد. به این معنا که به ازای هر آیتم فیلد nested، یک واحد اضافه خواهد شد. از آن میتوانیم به عنوان rowId برای آیتم آرایه استفاده کنیم:
NESTED PATH '$.category[*]' COLUMNS (categoryRowId FOR ORDINALITY, category TEXT PATH '$')
همچنین از ON EMPTY برای پراپرتیهایی که در ساختار JSON وجود ندارند نیز میتوانیم استفاده کنیم. به عنوان مثال در کوئری زیر گفتهایم در صورت عدم وجود price، یک مقدار پیشفرض باید نمایش داده شود و همچنین در صورت عدم وجود name، یک خطا در خروجی نمایش داده شود:
name CHAR(255) PATH "$.name" ERROR ON EMPTY, price FLOAT PATH "$.price" DEFAULT "0" ON EMPTY,
همچنین میتوانیم مقدار NULL را در صورت عدم وجود name ست کنیم:
name CHAR(255) PATH "$.name" NULL ON EMPTY,
خواندنیهای 12 خرداد
امنیت
توسعه وب
ویندوز 7
Silverlight و WPF
PHP
دات نت فریم ورک
سی شارپ
دلفی
شیرپوینت
متفرقه
واژهی استثناء یا 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()
IEnumerable<Product> GetCachedProducts()
در واقع استثناها حالتهایی هستند که غیرقابل پیشبینی هستند. این حالتها میتوانند یک خطای منطقی از طرف برنامهنویس و یا چیزی خارج کنترل برنامهنویس باشند (مانند خطاهای سیستمعامل، شبکه، دیسک). یعنی در بیشتر مواقع این نوع خطاها را نمیتوان مدیریت کرد.
اگر میخواهید استثناءها را 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); } } }
اگر مطمئن نیستید که تمام استثناءها توسط شما مدیریت شدهاند، میتوانید در حالتهای زیر، دیگر استثناءها را مدیریت کنید:
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); }
مشکل فوق را میتوانیم با صدور استثنای ArgumentNullException رفع کنیم:
public void PrintName(string name) { if (name == null) throw new ArgumentNullException("name"); Console.WriteLine(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); }
حالت دیگر صدور استثناء، زمانی است که متدی خروجی مورد انتظارمان را نتواند تحویل دهد. یک مثال بحثبرانگیز متدی با امضای زیر است:
public User GetUser(int id) { }
با استفاده از بررسی null کدهایی شبیه به این را در همه جا خواهیم داشت:
var user = datasource.GetUser(userId); if (user == null) throw new InvalidOperationException("Failed to find user: " + userId); // actual logic here
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; }
خطاهای متداول حین کار با استثناءها
- صدور مجدد استثناء و از بین بردن stacktrace
کد زیر را در نظر بگیرید:
try { FutileAttemptToResist(); } catch (BorgException err) { _myDearLog.Error("I'm in da cube! Ohh no!", err); throw err; }
- اضافه نکردن اطلاعات استثناء اصلی به استثناء جدید
یکی دیگر از خطاهای رایج اضافه نکردن استثناء اصلی حین صدور استثناء جدید است:
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); }
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)
سازندهی دوم برای تعیین description بوده و همانطور که عنوان شد ارائه دادن context information از اهمیت بالایی برخوردار است. به عنوان مثال فرض کنید استثناء KeyNotFoundException که توسط کلاس Dictionary صادر شده است را دریافت کردهاید. این استثناء زمانی صادر خواهد شد که بخواهید به عنصری که درون دیکشنری پیدا نشده است دسترسی داشته باشید. در این حالت پیام زیر را دریافت خواهید کرد:
“The given key was not present in the dictionary.”
“The key ‘abrakadabra’ was not present in the dictionary.”
سازندهی سوم شبیه به سازندهی قبلی عمل میکند با این تفاوت که توسط پارامتر دوم میتوانیم یک استثناء دیگر را 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; } }
public override string Message { get { return base.Message + "\r\nStatus code: " + StatusCode; } }
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); } }
در حین صدور استثناءها همیشه باید در نظر داشته باشیم که چه نوع context information را میتوان ارائه داد، این مورد در یافتن راهحل خیلی کمک خواهد کرد.
طراحی پیامهای مناسب
پیامهای exception مختص به توسعهدهندگان است نه کاربران نهایی.
نوشتن این نوع پیامها برای برنامهنویس کار خستهکنندهایی است. برای مثال دو مورد زیر را در نظر داشته باشید:
throw new Exception("Unknown FaileType"); throw new Exception("Unecpected workingDirectory");
توسعهدهندگانی که 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"."
همیشه برای نوشتن پیامهای مناسب سعی کنید از لحاظ نوشتاری متن شما مشکلی نداشته باشد، اطلاعات کافی را درون پیام اضافه کنید و تا حد امکان نحوهی رفع مشکل را توضیح دهید