اشتراکها
نظرات مطالب
ASP.NET MVC #4
پس در واقع فقط با داشتن یک route پیش فرض، سایر اکشن متدها با url درخواستی لینک میشوند؟
اشتراکها
جنریک چگونه به دات نت اضافه شد ؟
Before we dive into the technical details, let’s start with a quick history lesson, courtesy of Don Syme who worked on adding generics to .NET and then went on to design and implement F#, which is a pretty impressive set of achievements!!
Background and History
- 1999 Initial research, design and planning
- 1999 First ‘white paper’ published
- 2001 C# Language Design Specification created
- 2001 Research paper published
- 2004 Work completed and all bugs fixed
اگر پیشتر با SQL Server کار کرده باشید، حالت پیشفرض حساس بودن جستجوی SQLite به بزرگی و کوچکی حروف را انتظار نخواهید داشت؛ تا زمانیکه هنگام لاگین، اکانت Admin بتواند وارد سیستم شود و اکانت admin خیر. در این مطلب نحوهی انجام تنظیمات مرتبط با جستجوی غیرحساس به بزرگی و کوچکی حروف را در SQLite و EF-Core، بررسی خواهیم کرد.
Collations و حساسیت به بزرگی و کوچکی حروف
پردازش متون در بانکهای اطلاعاتی پیچیدهاست و عموما فراتر است از انتظارات سادهی اولیه، خصوصا اینکه بانکهای اطلاعاتی متفاوت، روشهای متفاوتی را هم در این زمینه بکار میگیرند. برای مثال بانکهای اطلاعاتی مانند SQLite و PostgreSQL به صورت پیشفرض به بزرگی و کوچکی حروف حساس هستند، اما بانکهایی مانند SQL Server و MySQL خیر. همچنین این حساسیت، بر روی کارآیی جستجو نیز بسیار تاثیر گذار است. برای مثال میتوان از متدهایی مانند string.ToLower برای انجام جستجوهای غیرحساس به بزرگی و کوچکی حروف استفاده کرد، اما بکارگیری آنها بلافاصله استفادهی از ایندکسها را غیرفعال میکنند و سبب انجام جستجوهایی بسیار کند خواهند شد.
برای مواجه شدن با یک چنین حالتهایی بدون افت کارآیی برنامه، مفهوم پایهای به نام collation در بانکهای اطلاعاتی ارائه شدهاست که مشخص میکند مقادیر رشتهای چگونه باید مرتب شده یا مقایسه شوند. برای مثال یک collation غیرحساس به بزرگی و کوچکی حروف، در حین مقایسهی رشتهها، به بزرگی و کوچکی حروف بکار گرفته شدهی در عبارت اهمیتی نمیدهد. همچنین باید دقت داشت که یک چنین مقایسهای بسته به فرهنگ بکار گرفته شده، میتوان متفاوت باشد؛ برای مثال در زبان ترکی، i و I حروف متفاوتی هستند و نباید در حین مقایسهی غیرحساس به بزرگی و کوچکی حروف، یکی در نظر گرفته شوند. به همین جهت تعداد قابل ملاحظهای case-insensitive collations از پیش تعریف شده، بسته به فرهنگهای مختلف وجود دارند؛ نمونهی دیگر آن فرهنگ آلمانی است که در آن عموما ä و ae را یکسان درنظر میگیرند. به علاوه collation بر روی نحوهی مرتب سازی حروف نیز تاثیر دارد؛ برای مثال در فرهنگ آلمانی، ä پس از a قرار میگیرد، اما در فرهنگ سوئدی در انتهای حروف الفباء واقع شدهاست.
تمام پردازشهای متنی در بانکهای اطلاعاتی (چه به صورت صریح و یا ضمنی) از collations استفاده میکنند و نام آنها از هر بانک اطلاعاتی به بانک اطلاعاتی دیگری متفاوت است. عموما میتوان این collations را در سطح کل بانک اطلاعاتی و یا در سطح یک ستون مشخص از آن و یا حتی در سطح یک کوئری مشخص، تعیین کرد.
روش تعیین collation در سطح بانک اطلاعاتی
در اغلب بانکهای اطلاعاتی، یک collation پیشفرض، در سطح کل آنها تعریف شدهاست و بر روی تمام پردازشهای متنی و تمام ستونهای جداول تاثیرگذار است. برای مثال حالت پیشفرض collation در SQL Server (اگر هیچ تنظیم پیشفرض دیگری در حین تعریف بانک اطلاعاتی وجود نداشته باشد) مقدار SQL_Latin1_General_CP1_CI_AS است. این مقدار یک collation غیرحساس به بزرگی و کوچکی حروف است. مقدار CI آن به معنای case-insensitive و AS آن مخفف accent-sensitive (حساس به لهجه) است.
از زمان EF-Core 5x، امکان کار با collations و تعیین آنها نیز میسر شدهاست. برای مثال برای تعیین یک چنین collation ای در سطح بانک اطلاعاتی میتوان به صورت زیر در متد OnModelCreating عمل کرد:
البته بهتر است یک چنین تنظیماتی را از ابتدای کار و پیش از تعریف و ایجاد بانک اطلاعاتی درنظر داشت؛ چون تغییر collation پس از ایجاد بانک اطلاعاتی، تداخلات زیادی را ایجاد میکند. برای مثال SQL Server حتی اجازهی join دو جدول با collation متفاوت را نمیدهد؛ هرچند راهحلهایی برای آن وجود دارد اما بهتر است این مقدار یکبار و آن هم در ابتدای کار تعیین شود.
روش تعیین collation در سطح جداول بانک اطلاعاتی
Collations را همچنین میتوان در سطح جداول نیز مشخص کرد تا بتوان در صورت نیاز، collation پیشفرض بانک اطلاعاتی را بازنویسی نمود. برای مثال شاید نیاز داشته باشید جداولی case-insensitive و تعدادی دیگر case-sensitive باشند.
در EF-Core 5x به بعد، روش انجام اینکار به صورت زیر است:
در اینجا collation ستون Name جدول Customer، به صورت صریحی مشخص شدهاست.
روش تعیین پویای collation در سطح کوئریهای بانک اطلاعاتی
یک جدول میتواند collation پیشفرضی داشته باشد، اما در حین کوئری گرفتن، collation آنرا به صورت موقت و پویا تغییر داد. برای مثال بجای استفاده از متد ToLower که سبب میشود از ایندکسها استفاده نشود، میتوان از collation خاصی در حین کوئری گرفتن استفاده کرد:
البته باید دقت داشت که تعیین collation در این حالت نیز سبب میشود تا از ایندکسها استفاده نشود. از این جهت که ایندکسها به صورت پیشفرض بر اساس collation یک ستون یا جدول تهیه میشوند. هرچند بانک اطلاعاتیهایی مانند PostgreSQL, Sqlite امکان تعیین collation را در حین تهیهی ایندکسها نیز میسر میکنند. برای مثال میتوان ایندکسهای حساس و غیر حساس به بزرگی و کوچکی حروف را در این بانکهای اطلاعاتی، به صورت جداگانهای تعریف کرد تا در صورت نیاز، از آنها استفاده شود.
تعیین collation غیرحساس به بزرگی و کوچکی حروف در SQLite، توسط EF-Core
با توجه به توضیحات فوق، متد زیر، collation ویژهی nocase را که در SQLite به معنای collation غیرحساس به بزرگی و کوچکی حروف است، به کل بانک اطلاعاتی و همچنین تمام ستونهای رشتهای آن به صورت خودکار اعمال میکند:
سپس روش استفادهی از آن به صورت زیر خواهد بود:
Collations و حساسیت به بزرگی و کوچکی حروف
پردازش متون در بانکهای اطلاعاتی پیچیدهاست و عموما فراتر است از انتظارات سادهی اولیه، خصوصا اینکه بانکهای اطلاعاتی متفاوت، روشهای متفاوتی را هم در این زمینه بکار میگیرند. برای مثال بانکهای اطلاعاتی مانند SQLite و PostgreSQL به صورت پیشفرض به بزرگی و کوچکی حروف حساس هستند، اما بانکهایی مانند SQL Server و MySQL خیر. همچنین این حساسیت، بر روی کارآیی جستجو نیز بسیار تاثیر گذار است. برای مثال میتوان از متدهایی مانند string.ToLower برای انجام جستجوهای غیرحساس به بزرگی و کوچکی حروف استفاده کرد، اما بکارگیری آنها بلافاصله استفادهی از ایندکسها را غیرفعال میکنند و سبب انجام جستجوهایی بسیار کند خواهند شد.
برای مواجه شدن با یک چنین حالتهایی بدون افت کارآیی برنامه، مفهوم پایهای به نام collation در بانکهای اطلاعاتی ارائه شدهاست که مشخص میکند مقادیر رشتهای چگونه باید مرتب شده یا مقایسه شوند. برای مثال یک collation غیرحساس به بزرگی و کوچکی حروف، در حین مقایسهی رشتهها، به بزرگی و کوچکی حروف بکار گرفته شدهی در عبارت اهمیتی نمیدهد. همچنین باید دقت داشت که یک چنین مقایسهای بسته به فرهنگ بکار گرفته شده، میتوان متفاوت باشد؛ برای مثال در زبان ترکی، i و I حروف متفاوتی هستند و نباید در حین مقایسهی غیرحساس به بزرگی و کوچکی حروف، یکی در نظر گرفته شوند. به همین جهت تعداد قابل ملاحظهای case-insensitive collations از پیش تعریف شده، بسته به فرهنگهای مختلف وجود دارند؛ نمونهی دیگر آن فرهنگ آلمانی است که در آن عموما ä و ae را یکسان درنظر میگیرند. به علاوه collation بر روی نحوهی مرتب سازی حروف نیز تاثیر دارد؛ برای مثال در فرهنگ آلمانی، ä پس از a قرار میگیرد، اما در فرهنگ سوئدی در انتهای حروف الفباء واقع شدهاست.
تمام پردازشهای متنی در بانکهای اطلاعاتی (چه به صورت صریح و یا ضمنی) از collations استفاده میکنند و نام آنها از هر بانک اطلاعاتی به بانک اطلاعاتی دیگری متفاوت است. عموما میتوان این collations را در سطح کل بانک اطلاعاتی و یا در سطح یک ستون مشخص از آن و یا حتی در سطح یک کوئری مشخص، تعیین کرد.
روش تعیین collation در سطح بانک اطلاعاتی
در اغلب بانکهای اطلاعاتی، یک collation پیشفرض، در سطح کل آنها تعریف شدهاست و بر روی تمام پردازشهای متنی و تمام ستونهای جداول تاثیرگذار است. برای مثال حالت پیشفرض collation در SQL Server (اگر هیچ تنظیم پیشفرض دیگری در حین تعریف بانک اطلاعاتی وجود نداشته باشد) مقدار SQL_Latin1_General_CP1_CI_AS است. این مقدار یک collation غیرحساس به بزرگی و کوچکی حروف است. مقدار CI آن به معنای case-insensitive و AS آن مخفف accent-sensitive (حساس به لهجه) است.
از زمان EF-Core 5x، امکان کار با collations و تعیین آنها نیز میسر شدهاست. برای مثال برای تعیین یک چنین collation ای در سطح بانک اطلاعاتی میتوان به صورت زیر در متد OnModelCreating عمل کرد:
modelBuilder.UseCollation("SQL_Latin1_General_CP1_CS_AS");
روش تعیین collation در سطح جداول بانک اطلاعاتی
Collations را همچنین میتوان در سطح جداول نیز مشخص کرد تا بتوان در صورت نیاز، collation پیشفرض بانک اطلاعاتی را بازنویسی نمود. برای مثال شاید نیاز داشته باشید جداولی case-insensitive و تعدادی دیگر case-sensitive باشند.
در EF-Core 5x به بعد، روش انجام اینکار به صورت زیر است:
modelBuilder.Entity<Customer>().Property(c => c.Name) .UseCollation("SQL_Latin1_General_CP1_CI_AS");
روش تعیین پویای collation در سطح کوئریهای بانک اطلاعاتی
یک جدول میتواند collation پیشفرضی داشته باشد، اما در حین کوئری گرفتن، collation آنرا به صورت موقت و پویا تغییر داد. برای مثال بجای استفاده از متد ToLower که سبب میشود از ایندکسها استفاده نشود، میتوان از collation خاصی در حین کوئری گرفتن استفاده کرد:
var customers = context.Customers .Where(c => EF.Functions.Collate(c.Name, "SQL_Latin1_General_CP1_CS_AS") == "John").ToList();
یک نکته: هر چند کوئریهای سمت دات نت به صورت پیشفرض حساس به بزرگی و کوچکی حروف هستند (مانند s1 == s2)، اما EF-Core هیچ تلاشی را برای انجام یک کوئری case-sensitive در سمت بانک اطلاعاتی انجام نخواهد داد و == سی شارپ به صورت مستقیمی به تساوی SQL ترجمه میشود که بسته به collation جاری، میتواند یا حتی نمیتواند حساس به بزرگی و کوچکی حروف باشد. بنابراین حالت پیشفرض کوئریهای EF-Core استفاده از collation پیشفرض ستونها است. هرچند متدهایی مانند string.Equals امکان مقایسهی غیرحساس به بزرگی و کوچکی حروف را در دات نت میسر میکنند (چون به همراه پارامتر StringComparison هستند)، اما EF-Core سعی در ترجمهی آنها به SQL نخواهد کرد و تعیین صریح collation توسط متد EF.Functions.Collate به شما واگذار شدهاست.
تعیین collation غیرحساس به بزرگی و کوچکی حروف در SQLite، توسط EF-Core
با توجه به توضیحات فوق، متد زیر، collation ویژهی nocase را که در SQLite به معنای collation غیرحساس به بزرگی و کوچکی حروف است، به کل بانک اطلاعاتی و همچنین تمام ستونهای رشتهای آن به صورت خودکار اعمال میکند:
public static void SetCaseInsensitiveSearchesForSQLite(this ModelBuilder modelBuilder) { if (modelBuilder == null) { throw new ArgumentNullException(nameof(modelBuilder)); } modelBuilder.UseCollation("NOCASE"); foreach (var property in modelBuilder.Model.GetEntityTypes() .SelectMany(t => t.GetProperties()) .Where(p => p.ClrType == typeof(string))) { property.SetCollation("NOCASE"); } }
protected override void OnModelCreating(ModelBuilder modelBuilder) { if (modelBuilder == null) { throw new ArgumentNullException(nameof(modelBuilder)); } modelBuilder.SetCaseInsensitiveSearchesForSQLite(); }
Pingback یکی از روشهای اطلاع رسانی به سایتهای دیگر در مورد لینک دادن به آنها در سایت خود است. برای مثال من لینکی از یکی از مطالب شما را در متن جاری خودم قرار میدهم. سپس به وسیلهی ارسال یک ping، در مورد انجام اینکار به شما اطلاع رسانی میکنم. حاصل آن عموما قسمت معروف ping-backs سایتها است. این مورد نیز یکی از روشهای مؤثر SEO در گرفتن backlink است و تبلیغ محتوا.
کار کردن با پروتکل Ping-back آنچنان ساده نیست؛ از این جهت که تبادل ارتباطات آن با پروتکل XML-RPC انجام میشود. XML-RPC نیز توسط PHP کارها بیشتر مورد استفاده قرار میگیرد؛ بجای استفاده از پروتکلهای استاندارد وب سرویسها مانند Soap و امثال آن. پیاده سازیهای ابتدایی Pingback نیز مرتبط است به Wordpress معروف که با PHP تهیه شدهاست. در ادامه نگاهی خواهیم داشت به جزئیات پیاده سازی ارسال ping-back توسط برنامههای ASP.NET.
یافتن آدرس وب سرویس سایت پذیرای Pingback
اولین قدم در پیاده سازی Pingback، یافتن آدرسی است که باید اطلاعات مورد نظر را به آن ارسال کرد. این آدرس عموما به دو طریق ارائه میشود:
الف) در هدری به نام x-pingback و یا pingback
ب) در قسمتی از کدهای HTML صفحه به شکل
برای مثال اگر به وبلاگهای MSDN دقت کنید، هدر x-pingback را میتوانید در خروجی وب سرور آنها مشاهده کنید:
همانطور که ملاحظه میکنید، نیاز است Response header را آنالیز کنیم.
نحوهی استخراج آدرس سرویس Pingback یک سایت را در کدهای فوق ملاحظه میکنید.
targetUri، آدرسی است از یک سایت دیگر که در سایت ما درج شدهاست. زمانیکه این صفحه را درخواست میکنیم، response.Headers.AllKeys حاصل میتواند حاوی کلید x-pingback باشد یا خیر. اگر بلی، همینجا کار پایان مییابد. فقط باید مطمئن شد که این آدرس مطلق است و نه نسبی. به همین جهت در متد getValidAbsoluteUri، بررسی بر روی UriKind.Absolute انجام شدهاست.
اگر هدر فاقد کلید x-pingback باشد، قسمت ب را باید بررسی کرد. یعنی نیاز است محتوای Html صفحه را برای یافتن link rel=pingback بررسی کنیم. همچنین باید دقت داشت که پیش از اینکار نیاز است حتما بررسی isResponseHtml صورت گیرد. برای مثال در سایت شما لینکی به یک فایل 2 گیگابایتی SQL Server درج شدهاست. در این حالت نباید ابتدا 2 گیگابایت فایل دریافت شود و سپس بررسی کنیم که آیا محتوای آن حاوی link rel=pingback است یا خیر. اگر محتوای ارسالی از نوع text/html بود، آنگاه کار دریافت محتوای لینک انجام خواهد شد.
ارسال Ping به آدرس سرویس Pingback
اکنون که آدرس سرویس pingback یک سایت را یافتهایم، کافی است ping ایی را به آن ارسال کنیم:
اینبار HttpWebRequest تشکیل شده از نوع post است و نه get. همچنین مقداری را که باید ارسال کنیم نیاز است مطابق پروتکل XML-RPC باشد. برای کار با XML-RPC در دات نت یا میتوان از کتابخانهی Cook Computing's XML-RPC.Net استفاده کرد و یا مطابق کدهای فوق، دستورات آنرا توسط یک XmlTextWriter کنار هم قرار داد و نهایتا در درخواست Post ارسالی درج کرد.
در اینجا sourceUri آدرس صفحهای در سایت ما است که targetUri ایی (آدرسی از سایت دیگر) در آن درج شدهاست. در یک pinback، صرفا این دو آدرس به سرویس دریافت کنندهی pingback ارسال میشوند.
سپس سایت دریافت کنندهی ping، ابتدا sourceUri را دریافت میکند تا عنوان آنرا استخراج کند و همچنین بررسی میکند که آیا targetUri، در آن درج شدهاست یا خیر (آیا spam است یا خیر)؟
تا اینجا اگر این مراحل را کنار هم قرار دهیم به کلاس Pingback ذیل خواهیم رسید:
Pingback.cs
نحوهی استفاده از کلاس Pingback تهیه شده
کار ارسال Pingback عموما به این نحو است: هر زمانیکه مطلبی یا یکی از نظرات آن، ثبت یا ویرایش میشوند، نیاز است Pingbackهای آن ارسال شوند. بنابراین تنها کاری که باید انجام شود، استخراج لینکهای خارجی یک صفحه و سپس فراخوانی متد Send کلاس فوق است.
یافتن لینکهای یک محتوا را نیز میتوان مانند متد extractPingbackServiceUriFormPage فوق، توسط یک Regex انجام داد و یا حتی با استفاده از کتابخانهی معروف HTML Agility Pack:
کار کردن با پروتکل Ping-back آنچنان ساده نیست؛ از این جهت که تبادل ارتباطات آن با پروتکل XML-RPC انجام میشود. XML-RPC نیز توسط PHP کارها بیشتر مورد استفاده قرار میگیرد؛ بجای استفاده از پروتکلهای استاندارد وب سرویسها مانند Soap و امثال آن. پیاده سازیهای ابتدایی Pingback نیز مرتبط است به Wordpress معروف که با PHP تهیه شدهاست. در ادامه نگاهی خواهیم داشت به جزئیات پیاده سازی ارسال ping-back توسط برنامههای ASP.NET.
یافتن آدرس وب سرویس سایت پذیرای Pingback
اولین قدم در پیاده سازی Pingback، یافتن آدرسی است که باید اطلاعات مورد نظر را به آن ارسال کرد. این آدرس عموما به دو طریق ارائه میشود:
الف) در هدری به نام x-pingback و یا pingback
ب) در قسمتی از کدهای HTML صفحه به شکل
<link rel="pingback" href="pingback server">
همانطور که ملاحظه میکنید، نیاز است Response header را آنالیز کنیم.
private Uri findPingbackServiceUri() { var request = (HttpWebRequest)WebRequest.Create(_targetUri); request.UserAgent = UserAgent; request.Timeout = Timeout; request.ReadWriteTimeout = Timeout; request.Method = WebRequestMethods.Http.Get; request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; using (var response = request.GetResponse() as HttpWebResponse) { if (response == null) return null; var url = extractPingbackServiceUriFormHeaders(response); if (url != null) return url; if (!isResponseHtml(response)) return null; using (var reader = new StreamReader(response.GetResponseStream())) { return extractPingbackServiceUriFormPage(reader.ReadToEnd()); } } } private static Uri extractPingbackServiceUriFormHeaders(WebResponse response) { var pingUrl = response.Headers.AllKeys.FirstOrDefault(header => header.Equals("x-pingback", StringComparison.OrdinalIgnoreCase) || header.Equals("pingback", StringComparison.OrdinalIgnoreCase)); return getValidAbsoluteUri(pingUrl); } private static Uri extractPingbackServiceUriFormPage(string content) { if (string.IsNullOrWhiteSpace(content)) return null; var regex = new Regex(@"(?s)<link\srel=""pingback""\shref=""(.+?)""", RegexOptions.IgnoreCase); var match = regex.Match(content); return (!match.Success || match.Groups.Count < 2) ? null : getValidAbsoluteUri(match.Groups[1].Value); } private static Uri getValidAbsoluteUri(string url) { Uri absoluteUri; return string.IsNullOrWhiteSpace(url) || !Uri.TryCreate(url, UriKind.Absolute, out absoluteUri) ? null : absoluteUri; } private static bool isResponseHtml(WebResponse response) { var contentTypeKey = response.Headers.AllKeys.FirstOrDefault(header => header.Equals("content-type", StringComparison.OrdinalIgnoreCase)); return !string.IsNullOrWhiteSpace(contentTypeKey) && response.Headers[contentTypeKey].StartsWith("text/html", StringComparison.OrdinalIgnoreCase); }
targetUri، آدرسی است از یک سایت دیگر که در سایت ما درج شدهاست. زمانیکه این صفحه را درخواست میکنیم، response.Headers.AllKeys حاصل میتواند حاوی کلید x-pingback باشد یا خیر. اگر بلی، همینجا کار پایان مییابد. فقط باید مطمئن شد که این آدرس مطلق است و نه نسبی. به همین جهت در متد getValidAbsoluteUri، بررسی بر روی UriKind.Absolute انجام شدهاست.
اگر هدر فاقد کلید x-pingback باشد، قسمت ب را باید بررسی کرد. یعنی نیاز است محتوای Html صفحه را برای یافتن link rel=pingback بررسی کنیم. همچنین باید دقت داشت که پیش از اینکار نیاز است حتما بررسی isResponseHtml صورت گیرد. برای مثال در سایت شما لینکی به یک فایل 2 گیگابایتی SQL Server درج شدهاست. در این حالت نباید ابتدا 2 گیگابایت فایل دریافت شود و سپس بررسی کنیم که آیا محتوای آن حاوی link rel=pingback است یا خیر. اگر محتوای ارسالی از نوع text/html بود، آنگاه کار دریافت محتوای لینک انجام خواهد شد.
ارسال Ping به آدرس سرویس Pingback
اکنون که آدرس سرویس pingback یک سایت را یافتهایم، کافی است ping ایی را به آن ارسال کنیم:
public void Send() { var pingUrl = findPingbackServiceUri(); if (pingUrl == null) throw new NotSupportedException(string.Format("{0} doesn't support pingback.", _targetUri.Host)); sendPing(pingUrl); } private void sendPing(Uri pingUrl) { var request = (HttpWebRequest)WebRequest.Create(pingUrl); request.UserAgent = UserAgent; request.Timeout = Timeout; request.ReadWriteTimeout = Timeout; request.Method = WebRequestMethods.Http.Post; request.ContentType = "text/xml"; request.ProtocolVersion = HttpVersion.Version11; makeXmlRpcRequest(request); using (var response = (HttpWebResponse)request.GetResponse()) { response.Close(); } } private void makeXmlRpcRequest(WebRequest request) { var stream = request.GetRequestStream(); using (var writer = new XmlTextWriter(stream, Encoding.ASCII)) { writer.WriteStartDocument(true); writer.WriteStartElement("methodCall"); writer.WriteElementString("methodName", "pingback.ping"); writer.WriteStartElement("params"); writer.WriteStartElement("param"); writer.WriteStartElement("value"); writer.WriteElementString("string", Uri.EscapeUriString(_sourceUri.ToString())); writer.WriteEndElement(); writer.WriteEndElement(); writer.WriteStartElement("param"); writer.WriteStartElement("value"); writer.WriteElementString("string", Uri.EscapeUriString(_targetUri.ToString())); writer.WriteEndElement(); writer.WriteEndElement(); writer.WriteEndElement(); writer.WriteEndElement(); } }
در اینجا sourceUri آدرس صفحهای در سایت ما است که targetUri ایی (آدرسی از سایت دیگر) در آن درج شدهاست. در یک pinback، صرفا این دو آدرس به سرویس دریافت کنندهی pingback ارسال میشوند.
سپس سایت دریافت کنندهی ping، ابتدا sourceUri را دریافت میکند تا عنوان آنرا استخراج کند و همچنین بررسی میکند که آیا targetUri، در آن درج شدهاست یا خیر (آیا spam است یا خیر)؟
تا اینجا اگر این مراحل را کنار هم قرار دهیم به کلاس Pingback ذیل خواهیم رسید:
Pingback.cs
نحوهی استفاده از کلاس Pingback تهیه شده
کار ارسال Pingback عموما به این نحو است: هر زمانیکه مطلبی یا یکی از نظرات آن، ثبت یا ویرایش میشوند، نیاز است Pingbackهای آن ارسال شوند. بنابراین تنها کاری که باید انجام شود، استخراج لینکهای خارجی یک صفحه و سپس فراخوانی متد Send کلاس فوق است.
یافتن لینکهای یک محتوا را نیز میتوان مانند متد extractPingbackServiceUriFormPage فوق، توسط یک Regex انجام داد و یا حتی با استفاده از کتابخانهی معروف HTML Agility Pack:
var doc = new HtmlWeb().Load(url); var linkTags = doc.DocumentNode.Descendants("link"); var linkedPages = doc.DocumentNode.Descendants("a") .Select(a => a.GetAttributeValue("href", null)) .Where(u => !String.IsNullOrEmpty(u));
نظرات مطالب
تفاوت بین Interface و کلاس Abstract در چیست؟
به روز رسانی نکات مطرح شدهی در این مطلب، بر اساس امکانات C# 8.0 به بعد
1- یک کلاس معمولی تنها میتواند از یک کلاس Abstract ارث بری کند ولی همان کلاس میتواند از چندین Interface ارث ببرد.وضعیت فعلی: هنوز هم برقرار است. اما چون اینبار اینترفیسها هم میتوانند به همراه کد باشند، میتوان به multiple inheritance از طریق اینترفیسها نیز رسید. هنگامیکه پیاده سازیهای پیشفرض اینترفیسها فراخوانی میشوند، فراخوان باید نوع اینترفیس متناظر را هم دقیقا مشخص کند. این ویژگی، مشکل معروفی را به نام «diamond problem»، که در ارث بری چندگانهی از کلاسهای پایه در زبانهای دیگر وجود دارد، حل میکند: زمانیکه دو یا چند کلاس پایه، متد هم نامی را داشته باشند.
2- یک Interface فقط میتواند اعلان متدها و خصوصیتها را داشته باشد؛ اما یک کلاس Abstract علاوه بر آنها میتوانید متدها و خصوصیتهایی با کدهای کامل داشته باشد.وضعیت فعلی: دیگر اینطور نیست. اینترفیسها در C# 8.0 میتوانند به همراه «default implementation» هم باشند.
3- عناصر موجود در کلاس Abstract میتوانند مانند یک کلاس معمولی دارای سطح دسترسی باشند؛ ولی Interfaceها فاقد این امکان هستند.وضعیت فعلی: دیگر اینطور نیست. از زمان C# 8.0 به بعد، اعضای یک اینترفیس هم میتوانند دارای access modifiers باشند. حالت پیشفرض، public است. اما اکنون اینترفیسها میتوانند دارای اعضای private هم باشند که فقط داخل همان اینترفیس قابل دسترسی هستند. این مورد برای خواناتر کردن پیاده سازیهای پیشفرض متدها در اینترفیسها میتواند مفید باشد. در اینجا امکان تعریف اعضای protected نیز میسر است.
4- وقتی شما متدی را به کلاس Abstract اضافه میکنید، به طور خودکار به همه زیر کلاسها اعمال میشود؛ اما در Interface اگر متدی اضافه کنید باید در تمام زیر کلاسها آن را اعمال کنید .
وضعیت فعلی: دیگر اینطور نیست؛ به علت امکان تعریف «default implementation» در اینترفیسها.
5- کلاسهای Abstract مانند کلاسهای معمولی میتوانند دارای فیلد و عناصر دیگری (مثل ثابتها) باشند؛ در حالیکه یک Interface فاقد این امکان میباشد. همچنین کلاس abstract میتواند شامل سازنده باشد، اما اینترفیس نمیتواند.وضعیت فعلی: هنوز هم تا حدودی برقرار است. با تغییرات زبان #C، اکنون اینترفیسها میتوانند دارای فیلدها، سازندهها و تخریبگرهای استاتیک هم باشند. یک فیلد استاتیک، فیلدی است که مقدار آن متعلق به یک وهلهی خاص نیست (هنوز هم نمیتوان instance filed در اینجا داشت) و به اشتراک گذاشته میشود (دقیقا مانند فیلدهای استاتیک کلاسها عمل میکند).
نظرات مطالب
C# 8.0 - Nullable Reference Types
بهبودهای نوعهای ارجاعی نالنپذیر در NET Core 3.0 Preview 7.
پس از نصب SDK جدید، نحوهی فعالسازی این قابلیت در فایل csproj، به صورت زیر درآمدهاست:
این موارد در Preview 7 جدید هستند:
1) امکان اضافه کردن قید جدید notnull در حین تعریف نوعهای جنریک
2) اضافه شدن یک سری ویژگی توکار جدید برای «پیش» بررسی کار با نوعهای ارجاعی نال نپذیر
ویژگی AllowNull به فراخوانها امکان ارسال نال را حتی اگر مجاز نباشد، میدهد. این ویژگی جدید در فضای نام System.Diagnostics.CodeAnalysis تعریف شدهاست. برعکس آن ویژگی DisallowNull، سبب خواهد شد تا فراخوانها حتی در صورت مجاز بودن نیز نتوانند نال ارسال کنند.
در مثال فوق ویژگی AllowNull سبب میشود تا در قسمت setter امکان دریافت نال نیز میسر شود؛ برای مثال جهت سازگاری با نگارشهای قبلی برنامه.
دو ویژگی یاد شده را میتوان بر روی پارامترهای متدها، پارامترهایی از نوع in و ref، فیلدها، خواص و ایندکسرها اعمال کرد.
3) اضافه شدن یک سری ویژگی توکار جدید برای «پس» بررسی کار با نوعهای ارجاعی نال نپذیر
دو ویژگی جدید MaybeNull و NotNull کار پس بررسی نال پذیری را انجام میدهند:
در این مثال، متد Find با تعریف ویژگی return: MaybeNull، ممکن است نال برگرداند. برای مثال اگر چیزی یافت نشد، default را بر گرداند.
در متد Resize، پارامتر array، میتواند نال دریافت کند، چون نالپذیر تعریف شدهاست؛ اما چون به ویژگی NotNull مزین است، حاصل تغییرات بر روی آن (خروجی از متد، از طریق پارامتری از نوع ref) نمیتواند نال باشد.
دو ویژگی یاد شده را میتوان بر روی خروجی متدها، پارامترهایی از نوع out و ref، فیلدها، خواص و ایندکسرها اعمال کرد.
4) اضافه شدن یک سری ویژگی توکار جدید برای «پس» بررسی «شرطی» کار با نوعهای ارجاعی نال نپذیر
در مثالهای زیر کاربردهای دو ویژگی شرطی جدید NotNullWhen و MaybeNullWhen را مشاهده میکنید:
در اینجا با بکارگیری ویژگی [NotNullWhen(false)] به فراخوان اعلام میکنیم که اگر IsNullOrEmpty مقدار false را برگرداند، مقدار value ارسال شدهی به آن، نال نیست.
در اینجا با بکارگیری ویژگی [NotNullWhen(true)] به فراخوان اعلام میکنیم که اگر TryParse مقدار true را برگرداند، مقدار version خروجی آن، نال نیست.
در اینجا با بکارگیری ویژگی [MaybeNullWhen(false)] به فراخوان اعلام میکنیم که اگر TryDequeue مقدار false را برگرداند، مقدار result خروجی آن، میتواند نال هم باشد.
5) اضافه شدن یک سری ویژگی توکار جدید برای شرط گذاشتن بین ورودی و خروجی، در حین کار با نوعهای ارجاعی نال نپذیر
در متد زیر، هم خروجی و هم ورودی آن میتوانند نال باشند. اما میخواهیم اگر path نال نباشد، اطمینان حاصل کنیم که استفاده کننده میداند، خروجی این متد، حتما نال نخواهد بود:
برای انجام یک چنین اطلاع رسانیهایی میتوان از ویژگی جدید NotNullIfNotNull استفاده کرد. از آن میتوان برای مزین سازی خروجی متدها و یا پارامترهایی از نوع ref استفاده کرد.
6) اضافه شدن یک سری ویژگی توکار جدید برای بررسی سیلان برنامه، در حین کار با نوعهای ارجاعی نال نپذیر
در اینجا نحوهی استفاده از دو ویژگی جدید DoesNotReturn و DoesNotReturnIf را مشاهده میکنید:
اگر متد ThrowArgumentNullException در جائی فراخوانی شود، سبب بروز یک استثناء میشود. استفاده از DoesNotReturn سبب میشود تا به کامپایلر اعلام کند، پس از این نقطه، دیگر نیازی به بررسی نال بودن اشیاء نیست؛ چون آن قطعه از کد، غیرقابل اجرا و رسیدن میشود. این ویژگی را تنها بر روی متدها میتوان قرار داد.
اگر متد MyAssert فراخوانی شود و ورودی آن false باشد، یک استثناء را صادر میکند. با بکارگیری ویژگی [DoesNotReturnIf(false)] این موضوع را به کامپایلر اعلام کرده و از آن درخواست میکنیم تا کار بررسی نال بودن اشیاء را از آن سطر به بعد، انجام ندهد. این ویژگی را تنها بر روی پارامترها میتوان اعمال کرد.
پس از نصب SDK جدید، نحوهی فعالسازی این قابلیت در فایل csproj، به صورت زیر درآمدهاست:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> <LangVersion>8.0</LangVersion> <Nullable>enable</Nullable> </PropertyGroup> </Project>
این موارد در Preview 7 جدید هستند:
1) امکان اضافه کردن قید جدید notnull در حین تعریف نوعهای جنریک
interface IDoStuff<TIn, TOut> where TIn : notnull where TOut : notnull { TOut DoStuff(TIn input); }
2) اضافه شدن یک سری ویژگی توکار جدید برای «پیش» بررسی کار با نوعهای ارجاعی نال نپذیر
ویژگی AllowNull به فراخوانها امکان ارسال نال را حتی اگر مجاز نباشد، میدهد. این ویژگی جدید در فضای نام System.Diagnostics.CodeAnalysis تعریف شدهاست. برعکس آن ویژگی DisallowNull، سبب خواهد شد تا فراخوانها حتی در صورت مجاز بودن نیز نتوانند نال ارسال کنند.
using System; using System.Diagnostics.CodeAnalysis; namespace ConsoleApp { public class MyClass { private string _innerValue = string.Empty; [AllowNull] public string MyValue { get { return _innerValue; } set { _innerValue = value ?? string.Empty; } } }
دو ویژگی یاد شده را میتوان بر روی پارامترهای متدها، پارامترهایی از نوع in و ref، فیلدها، خواص و ایندکسرها اعمال کرد.
3) اضافه شدن یک سری ویژگی توکار جدید برای «پس» بررسی کار با نوعهای ارجاعی نال نپذیر
دو ویژگی جدید MaybeNull و NotNull کار پس بررسی نال پذیری را انجام میدهند:
public class MyArray { // Result is the default of T if no match is found [return: MaybeNull] public static T Find<T>(T[] array, Func<T, bool> match) { //... } // Never gives back a null when called public static void Resize<T>([NotNull] ref T[]? array, int newSize) { //... } }
در متد Resize، پارامتر array، میتواند نال دریافت کند، چون نالپذیر تعریف شدهاست؛ اما چون به ویژگی NotNull مزین است، حاصل تغییرات بر روی آن (خروجی از متد، از طریق پارامتری از نوع ref) نمیتواند نال باشد.
دو ویژگی یاد شده را میتوان بر روی خروجی متدها، پارامترهایی از نوع out و ref، فیلدها، خواص و ایندکسرها اعمال کرد.
4) اضافه شدن یک سری ویژگی توکار جدید برای «پس» بررسی «شرطی» کار با نوعهای ارجاعی نال نپذیر
در مثالهای زیر کاربردهای دو ویژگی شرطی جدید NotNullWhen و MaybeNullWhen را مشاهده میکنید:
public class MyString { // True when 'value' is null public static bool IsNullOrEmpty([NotNullWhen(false)] string? value) { //... } }
public class MyVersion { // If it parses successfully, the Version will not be null. public static bool TryParse(string? input, [NotNullWhen(true)] out Version? version) { //... } }
public class MyQueue<T> { // 'result' could be null if we couldn't Dequeue it. public bool TryDequeue([MaybeNullWhen(false)] out T result) { //... } }
5) اضافه شدن یک سری ویژگی توکار جدید برای شرط گذاشتن بین ورودی و خروجی، در حین کار با نوعهای ارجاعی نال نپذیر
در متد زیر، هم خروجی و هم ورودی آن میتوانند نال باشند. اما میخواهیم اگر path نال نباشد، اطمینان حاصل کنیم که استفاده کننده میداند، خروجی این متد، حتما نال نخواهد بود:
class MyPath { [return: NotNullIfNotNull("path")] public static string? GetFileName(string? path) { //... } }
6) اضافه شدن یک سری ویژگی توکار جدید برای بررسی سیلان برنامه، در حین کار با نوعهای ارجاعی نال نپذیر
در اینجا نحوهی استفاده از دو ویژگی جدید DoesNotReturn و DoesNotReturnIf را مشاهده میکنید:
internal static class ThrowHelper { [DoesNotReturn] public static void ThrowArgumentNullException(ExceptionArgument arg) { //... } }
public static class MyAssertionLibrary { public static void MyAssert([DoesNotReturnIf(false)] bool condition) { //... } }
اشتراکها