نظرات مطالب
بررسی تصویر امنیتی (Captcha) سایت - قسمت دوم
تا الان که کسی مشکلی گزارش نکرده.
 همچنین مقدار ذخیره شده در این کش با طول عمر کوتاه 5 دقیقه‌ای به ازای هر بار ریفرش صفحه متفاوت خواهد بود.
مطالب
بهینه‌سازی سایت برای شبکه‌های اجتماعی
چند سالی ست که شبکه‌های اجتماعی رشد چشمگیری در دنیای مجازی داشته‌اند و به حرف اول و پیشتاز آن بدل شده‌اند. این شبکه‌ها در همه‌ی زمینه‌ها از معرفی و فروش محصولات گرفته تا معرفی سایت و وبلاگ و ... بکار گرفته می‌شوند و لذا مقوله‌ی بهینه سازی یک وب سایت برای این شبکه‌های اجتماعی امری ناگزیر است. مباحث سئو که پیرامون بهینه سازی یک وب سایت برای موتورهای جستجو می‌باشند، امروزه با پدیده‌ی جدید دیگری آمیخته شده‌اند و به تعاریف قدیمی و معمول، گزینه‌هایی افزوده شده است. از این‌رو شناخت و بکارگیری روش‌های بهینه، برای این مباحث ضروری می‌باشند.
همانطور که می‌دانید هنگامیگه در ادیتور ارسال مطلب یکی از شبکه‌های اجتماعی (فیسبوک ، توییتر ، گوگل پلاس و ...) آدرس سایتی را وارد میکنید، بلافاصله لینک پردازش شده و پیش نمایشی از آن وبسایت در ادیتور نمایش داده می‌شود. برخی پیش نمایش‌ها حاوی عکس، عنوان لینک و خیلی منظم هستند و برخی دیگر فقط نام و عنوان سایت را در برمی‌گیرند.
برای نمونه به تصاویر زیر دقت کنید:

تصویر فوق مربوط به لینک یک سایت خبری در ادیتور فیسبوک می‌باشد. همانطور که می‌بینید عنوان خبر، عکس و توضیح مختصری در مورد خبر، با نظم و ترتیبی خاص نمایش داده شده است.

حال به تصویر زیر که مربوط به لینکی از همین سایت می‌باشد دقت کنید:


همانطور که می‌بینید، تنها لینکی ساده، بدون هیچ پیش نمایشی از وب سایت نشان داده شده است.
دلیل این اتفاق وجود یا عدم وجود متاتگ‌هایی که Social Media Metadata نامیده می‌شوند می‌باشند. چنانچه وب سایتی بوسیله‌ی این متاتگ‌ها برای شبکه‌های اجتماعی بهینه سازی شده باشد، با قرار دادن هر لینکی در ادیتور شبکه‌های اجتماعی، پیش نمایشی خوب از آن مطلب به نمایش گذاشته می‌شود. اهمیت این متاتگ‌ها در سایت‌های خبری، فروشگاه‌ها، سایت‌های آموزشی و ... بسیار بالا می‌باشد تا از مزایای جذب کاربر توسط شبکه‌های اجتماعی بهره‌مند شوند. با این مقدمه می‌رویم به سراغ معرفی و چگونگی بکارگیری این متاتگ ها.

بخش اول: متاتگ‌های موردنیاز برای لینک‌های حاوی مقالات:

ابتدا تگ html خود را به شکل زیر تغییر دهید:
<html itemscope itemtype="http://schema.org/Article">
برای شناخت و اهمیت بکارگیری خصوصیت itemscope میتوانید اینجا را ببینید. این خصوصیت زوج‌های کلید/مقداری را تعریف می‌کند که جزئی از سند می‌باشند. در ادامه itemtype به تشریح یک زوج کلید/مقدار می‌پردازد که به مرورگر می‌گوید کدام آیتم‌ها برای یک مقاله بهینه شده‌اند.
<title>عنوان صفحه ، حداکثر 60 تا 70 کارکتر باشد</title>
<meta name="description" content="شرح صفحه ، حداکثر 150 کارکتر باشد" /> 

<!-- Schema.org markup for Google+ -->
<meta itemprop="name" content="نام یا عنوان صفحه">
<meta itemprop="description" content="شرح صفحه">
<meta itemprop="image" content="نشانی اینترنتی عکسی که در پیشنمایش نشان داده میشود"> 

<!-- Twitter Card data -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="کپی رایت نام  سایت">
<meta name="twitter:title" content="نام یا عنوان صفحه">
<meta name="twitter:description" content="شرح صفحه">
<meta name="twitter:creator" content="نویسنده">
<!-- Picture size at least  280x150px -->عکس پیشنمایش با ابعاد حداقل   
<meta name="twitter:image:src" content="نشانی اینترنتی عکس مطلب"> 

<!-- Open Graph data -->
<meta property="og:title" content="عنوان صفحه" />
<meta property="og:type" content="article" />
<meta property="og:url" content="نشانی سایت" />
<meta property="og:image" content="نشانی عکس مطلب" />
<meta property="og:description" content="شرح مطلب" />
<meta property="og:site_name" content="نام سایت" />
<meta property="article:published_time" content="تاریخ انتشار" />
<meta property="article:modified_time" content="تاریخ بروزرسانی" />
<meta property="article:section" content="نام بخش محتوی متن مقاله" />
<meta property="article:tag" content="نام تگ محتوی متن مقاله" />
<meta property="fb:admins" content="شناسه عددی کاربری شما در فیسبوک" />

بخش دوم: متاتگ‌های موردنیاز برای لینک‌های حاوی محصولات:
ابتدا تگ html خود را به شکل زیر تغییر دهید:
<html itemscope itemtype="http://schema.org/Product">
و در نهایت متاتگ‌های زیر را به صفحه خود اضافه کنید:
<title>عنوان صفحه</title>
<meta name="description" content="شرح صفحه" />

<!-- Schema.org markup for Google+ -->
<meta itemprop="name" content="عنوان صفحه">
<meta itemprop="description" content="Tشرح صفحه">
<meta itemprop="image" content="نشانی عکس محصول یا کالا">

<!-- Twitter Card data -->
<meta name="twitter:card" content="product">
<meta name="twitter:site" content="کپی رایت سایت">
<meta name="twitter:title" content="عنوان صفحه">
<meta name="twitter:description" content="شرح محصول یا کالا">
<meta name="twitter:creator" content="نویسنده">
<meta name="twitter:image" content="نشانی عکس محصول یا کالا">
<meta name="twitter:data1" content="قیمت محصول یا کالا">
<meta name="twitter:label1" content="Price">
<meta name="twitter:data2" content="رنگ کالا یا محصحول">
<meta name="twitter:label2" content="Color">

<!-- Open Graph data -->
<meta property="og:title" content="عنوان صفحه" />
<meta property="og:type" content="article" />
<meta property="og:url" content="نشانی سایت" />
<meta property="og:image" content="عکس محصول یا کالا" />
<meta property="og:description" content="شرح مصحول" />
<meta property="og:site_name" content="نام سایت" />
<meta property="og:price:amount" content="قیمت محصول یا کالا" />
<meta property="og:price:currency" content="واحد ارزی قیمت" />

در پایان
برای مشاهده لیست کاملی از اسکیما اینجا را ببینید.
og مخفف Open Graph Protocol می‌باشد که می‌توانید مطالب کاملی را در مورد آن اینجا بخوانید.
و برای آشنایی بیشتر با TwitterCard هم این لینک را مشاهده کنید.
نظرات اشتراک‌ها
کتابخانه EasyCaching : اCaching با امکانات زیاد اما یکپارچه و آسان
اگر از EasyCaching برای کش کردن اطلاعات خود استفاده میکنید کتابخانه  EasyCaching.Extensions.EasyCompressor رو توصیه میکنم.
این کتابخانه امکان استفاده از کتابخانه فشرده سازی EasyCompressor را بر روی EasyCaching فراهم میکنه
این کتابخانه با فشرده سازی اطلاعات کش در سناریو‌های Distributed Cache مانند Redis باعث کاهش ترافیک شبکه و در نتیجه افزایش پرفرمنس خواهد شد.
 
نظرات اشتراک‌ها
EasyCompressor : کتابخانه فشرده سازی با الگوریتم های مختلف
اگر از EasyCaching برای کش کردن اطلاعات خود استفاده میکنید کتابخانه  EasyCaching.Extensions.EasyCompressor رو توصیه میکنم.
این کتابخانه امکان استفاده از کتابخانه فشرده سازی EasyCompressor را بر روی EasyCaching فراهم میکنه
این کتابخانه با فشرده سازی اطلاعات کش در سناریو‌های Distributed Cache مانند Redis باعث کاهش ترافیک شبکه و در نتیجه افزایش پرفرمنس خواهد شد.


نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 15 - بررسی تغییرات Caching
فیلتر Authorize هم در ASP.NET Core هدرهای مربوط به کش کردن را بازنویسی و تنظیم می‌کند. به عبارتی صفحه‌ای که از این فیلتر رد شود، فقط دارای "CacheControl = "no-cache خواهد بود (تا به اشتباه اینگونه صفحات دارای سطح دسترسی، کش نشوند؛ موردی که در نگارش قبلی ASP.NET MVC به صورت توکار بررسی نمی‌شد).
مطالب
Blazor 5x - قسمت 28 - برنامه‌ی Blazor WASM - نمایش لیست اطلاعات دریافتی از Web API
در قسمت قبل، سرویس و کامپوننت دریافت اطلاعات اتاق‌ها را از Web API برنامه، تکمیل کردیم. در این قسمت با استفاده از اطلاعات مهیا شده، UI آن‌را نیز تکمیل خواهیم کرد.


نمایش منتظر بمانید در حین بارگذاری اولیه‌ی کامپوننت

کامپوننت‌هایی که قرار است اطلاعات را از یک Web API دریافت کنند، مدتی باید منتظر بمانند تا عملیات رفت و برگشت به سرور، تکمیل شود. در این بین می‌توان یک loading را به کاربر نمایش داد:
@page "/hotel/rooms"

@if (Rooms is not null && Rooms.Any())
{

}
else
{
    <div style="position:fixed;top:50%;left:50%;margin-top:-50px;margin-left:-100px;">
        <img src="images/loader.gif" />
    </div>
}

@code {
    IEnumerable<HotelRoomDTO> Rooms = new List<HotelRoomDTO>();
    // ... 
}
- فیلد Rooms را در قسمت قبل، در متد LoadRooms، از Web API دریافت و مقدار دهی کردیم. تا زمان تکمیل عملیات این متد، فیلد Rooms، فاقد عضوی است؛ بنابراین قسمت else شرط فوق اجرا می‌شود که یک loading را نمایش خواهد داد. مابقی UI برنامه در قسمت if آن قرار می‌گیرد.
- هر زمانیکه کار روال رویدادگردان OnInitializedAsync به پایان برسد (که شامل اجرای متد LoadRooms نیز هست)، سبب فراخوانی خودکار StateHasChanged می‌شود. این فراخوانی، UI را مجددا رندر می‌کند. به همین جهت است که پس از پایان کار، محتوای if، رندر خواهد شد.
- از این loading سفارشی که در میانه‌ی صفحه نمایش داده می‌شود، می‌توان در فایل wwwroot\index.html نیز بجای loading پیش‌فرض آن استفاده کرد:
  <body>
    <div id="app">
      <div
        style="
          position: fixed;
          top: 50%;
          left: 50%;
          margin-top: -50px;
          margin-left: -100px;
        "
      >
        <img src="images/ajax-loader.gif" />
      </div>
    </div>

افزودن خواصی جدید به HotelRoomDTO

می‌خواهیم به کاربر امکان تغییر تعداد روزهای اقامت را بدهیم. این انتخاب باید در لیست اتاق‌های نمایش داده شده، با تغییر تعداد روزهای اقامت (TotalDays) و هزینه‌ی جدید متناظر با آن (TotalAmount)، منعکس شود. به همین جهت این خواص را به HotelRoomDTO، اضافه می‌کنیم:
namespace BlazorServer.Models
{
    public class HotelRoomDTO
    {
        // ...

        public int TotalDays { get; set; }
        public decimal TotalAmount { get; set; }
    }
}
محاسبات مربوط به این خواص را هم می‌توان در همان کامپوننت HotelRooms.razor، پس از بارگذاری لیست اتاق‌ها از Web API، انجام داد:
@code
{
     HomeVM HomeModel = new HomeVM();
    // ...

    private async Task LoadRoomsAsync()
    {
        Rooms = await HotelRoomService.GetHotelRoomsAsync(HomeModel.StartDate, HomeModel.EndDate);
        foreach (var room in Rooms)
        {
            room.TotalAmount = room.RegularRate * HomeModel.NoOfNights;
            room.TotalDays = HomeModel.NoOfNights;
        }
    }
}


افزودن امکان تغییر تعداد روزهای اقامت در همان صفحه‌ی نمایش لیست اتاق‌ها


همانطور که در تصویر فوق هم مشاهده می‌کنید، می‌خواهیم در این صفحه نیز کاربر بتواند زمان شروع اقامت و مدت مدنظر را تغییر دهد. به همین جهت، HomeModel ای را که در قسمت قبل از Local Storage دریافت کردیم، به فرم زیر متصل می‌کنیم تا اجزای آن در این فرم، نمایش داده شده و قابل تغییر شوند:
@if (Rooms is not null && Rooms.Any())
{
    <EditForm Model="HomeModel" OnValidSubmit="SaveBookingInfo" class="bg-light">
        <div class="pt-3 pb-2 px-5 mx-1 mx-md-0 bg-secondary">
            <DataAnnotationsValidator />
            <div class="row px-3 mx-3">
                <div class="col-6 col-md-4">
                    <div class="form-group">
                        <label class="text-warning">Check in Date</label>
                        <InputDate @bind-Value="HomeModel.StartDate" class="form-control" />
                    </div>
                </div>
                <div class="col-6 col-md-4">
                    <div class="form-group">
                        <label class="text-warning">Check Out Date</label>
                        <input @bind="HomeModel.EndDate" disabled="disabled"
                            readonly="readonly" type="date" class="form-control" />
                    </div>
                </div>
                <div class=" col-4 col-md-2">
                    <div class="form-group">
                        <label class="text-warning">No. of nights</label>
                        <select class="form-control" @bind="HomeModel.NoOfNights">
                            <option value="Select" selected disabled="disabled">(Select No. Of Nights)</option>
                            @for (var i = 1; i <= 10; i++)
                            {
                                <option value="@i">@i</option>
                            }
                        </select>
                    </div>
                </div>

                <div class="col-8 col-md-2">
                    <div class="form-group" style="margin-top: 1.9rem !important;">
                        @if (IsProcessing)
                        {
                            <button class="btn btn-success btn-block form-control">
                                <i class="fa fa-spin fa-spinner"></i>Processing...
                            </button>
                        }
                        else
                        {
                            <input type="submit" value="Update" class="btn btn-success btn-block form-control" />
                        }
                    </div>
                </div>
            </div>
        </div>
    </EditForm>
نکته‌ی مهم این فرم، مدیریت قسمت کلیک بر روی دکمه‌ی Update است که سبب فراخوانی روال رویدادگران OnValidSubmit می‌شود:
@code {
    bool IsProcessing;

    // ...

    private async Task SaveBookingInfo()
    {
        IsProcessing = true;
        HomeModel.EndDate = HomeModel.StartDate.AddDays(HomeModel.NoOfNights);
        await LocalStorage.SetItemAsync(ConstantKeys.LocalInitialBooking, HomeModel);
        await LoadRoomsAsync();
        IsProcessing = false;
    }
}
در ابتدای عملیات، فیلد جدید IsProcessing را به true تنظیم می‌کنیم. این مورد سبب می‌شود تا برچسب دکمه‌ی Update به Processing... تغییر کند. سپس فیلد محاسباتی EndDate را بر اساس اطلاعات جدید فرم، به روز رسانی می‌کنیم. در ادامه، مجددا این اطلاعات را در Local Storage ذخیره سازی کرده و کار LoadRoomsAsync را انجام می‌دهیم که به همراه آن، خواص جدید تعداد روزها و هزینه‌ی اقامت نیز مجددا محاسبه می‌شوند. در آخر برچسب دکمه‌ی Update را به حالت اول باز می‌گردانیم.

سؤال: زمانیکه IsProcessing به true تنظیم می‌شود که هنوز کار متد رویدادگردان SaveBookingInfo به پایان نرسیده‌است و فراخوانی خودکار StateHasChanged در پایان متدهای رویدادگردان صورت می‌گیرد. پس چطور است که سبب رندر مجدد UI و تغییر برچسب دکمه‌ی Update می‌شود؟
پاسخ به این سؤال را در قسمت 6 این سری با بررسی چرخه‌ی حیات کامپوننت‌ها، مشاهده کردیم:
«البته متدهای رویدادگردان async، دوبار سبب فراخوانی ضمنی StateHasChanged می‌شوند؛ یکبار زمانیکه قسمت sync متد به پایان می‌رسد (در این مثال یعنی تا قبل از اولین await نوشته شده) و یکبار هم زمانیکه کار فراخوانی کلی متد به پایان خواهد رسید»


نمایش لیست اتاق‌ها


نمایش لیست اتاق‌ها مطابق تصویر فوق، دو قسمت اصلی را دارد:
الف) نمایش لیست تصاویر منتسب به یک اتاق، توسط کامپوننت carousel بوت استرپ
@foreach (var room in Rooms)
{
            <div class="row p-2 my-3 " style="border-radius:20px; border: 1px solid gray">
                <div class="col-12 col-lg-3 col-md-4">
                    <div id="carouselExampleIndicators_@room.Id"
                        class="carousel slide mb-4 m-md-3 m-0 pt-3 pt-md-0"
                        data-ride="carousel">
                        <ol class="carousel-indicators">
                            @{
                                int imageIndex = 0;
                                int innerImageIndex = 0;
                            }
                            @foreach (var image in room.HotelRoomImages)
                            {
                                if (imageIndex == 0)
                                {
                                    <li data-target="#carouselExampleIndicators_@room.Id"
                                        data-slide-to="@imageIndex" class="active"></li>

                                }
                                else
                                {
                                    <li data-target="#carouselExampleIndicators_@room.Id"
                                        data-slide-to="@imageIndex"></li>
                                }
                                imageIndex++;
                            }
                        </ol>
                        <div class="carousel-inner">
                            @foreach (var image in room.HotelRoomImages)
                            {
                                var imageUrl = $"{ImagesBaseAddress}/{image.RoomImageUrl}";
                                if (innerImageIndex == 0)
                                {
                                    <div class="carousel-item active">
                                        <img class="d-block w-100" style="border-radius:20px;"
                                            src="@imageUrl" alt="First slide">
                                    </div>
                                }
                                else
                                {
                                    <div class="carousel-item">
                                        <img class="d-block w-100" style="border-radius:20px;"
                                            src="@imageUrl" alt="First slide">
                                    </div>
                                }

                                innerImageIndex++;
                            }
                        </div>
                        <a class="carousel-control-prev" href="#carouselExampleIndicators_@room.Id"
                            role="button" data-slide="prev">
                            <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                            <span class="sr-only">Previous</span>
                        </a>
                        <a class="carousel-control-next" href="#carouselExampleIndicators_@room.Id"
                            role="button" data-slide="next">
                            <span class="carousel-control-next-icon" aria-hidden="true"></span>
                            <span class="sr-only">Next</span>
                        </a>
                    </div>
                </div>
}
- هرچند این قطعه کد، طولانی به نظر می‌رسد اما قسمت‌های مختلف آن صرفا بر اساس مستندات سایت بوت استرپ، جهت تشکیل ساختار ابتدایی و استاندارد کامپوننت carousel، تهیه شده‌اند.
- سپس در حلقه‌ای که برای نمایش لیست اتاق‌ها تهیه کرده‌ایم، قسمت‌های مختلف carousel را تکمیل می‌کنیم که در اینجا نیاز به ایندکس تصاویر، لیست تصاویر و یک Id منحصربفرد برای این carousel خاص را دارد تا بتوان چندین وهله از آن‌را در صفحه قرار داد که این id را بر اساس Id اتاق مشخص کرد‌ه‌ایم.

دو نکته:
- در این مثال برای تعریف لینک به تصاویر، کد زیر را مشاهده می‌کنید:
var imageUrl = $"{ImagesBaseAddress}/{image.RoomImageUrl}";
و این ImagesBaseAddress، به صورت زیر تعریف شده که همان آدرس برنامه‌ی blazor server ای است که مشخصات اتاق‌ها و تصاویر را ثبت می‌کند:
@code {
   string ImagesBaseAddress = "https://localhost:5006";
بنابراین اگر می‌خواهید تصاویر را هم مشاهده کنید، باید برنامه‌ی مجزای blazor server این سری نیز در حال اجرا باشد.
- کامپوننت carousel برای اجرا، نیاز به فایل lib/bootstrap/dist/js/bootstrap.bundle.min.js را نیز دارد. به همین جهت مدخل اسکریپت آن‌را باید به فایل wwwroot\index.html اضافه کرد.

ب) نمایش جزئیات نام و هزینه‌ی اتاق
قسمت دوم حلقه‌ی foreach نمایش لیست اتاق‌ها، جهت نمایش جزئیات هر اتاق تعریف شده‌است:
@foreach (var room in Rooms)
{
                <div class="col-12 col-lg-9 col-md-8">
                    <div class="row pt-3">
                        <div class="col-12 col-lg-8">
                            <p class="card-title text-warning" style="font-size:xx-large">@room.Name</p>
                            <p class="card-text">
                                @((MarkupString)room.Details)
                            </p>
                        </div>
                        <div class="col-12 col-lg-4">
                            <div class="row pb-3 pt-2">
                                <div class="col-12 col-lg-11 offset-lg-1">
                                    <a href="@($"hotel/room-details/{room.Id}")" class="btn btn-success btn-block">Book</a>
                                </div>
                            </div>
                            <div class="row ">
                                <div class="col-12 pb-5">
                                    <span class="float-right">
                                        <span class="float-right">Occupancy : @room.Occupancy adults </span><br />
                                        <span class="float-right pt-1">Room Size : @room.SqFt sqft</span><br />
                                        <h4 class="text-warning font-weight-bold pt-4">
                                            <span style="border-bottom:1px solid #ff6a00">
                                                @room.TotalAmount.ToString("#,#.00;(#,#.00#)")
                                            </span>
                                        </h4>
                                        <span class="float-right">Cost for  @room.TotalDays nights</span>
                                    </span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
}
- در این مثال از MarkupString استفاده شده تا بتوان یک محتوای HTML ای را در صفحه نمایش داد.
- هر اتاق نمایش داده شده، لینکی را به صفحه‌ی خاص خودش نیز دارد که آن‌را در قسمت بعدی تکمیل می‌کنیم.
- در اینجا TotalAmount و TotalDays محاسباتی و قابل تغییر بر اساس انتخاب کاربر نیز درج شده‌اند.


یک تمرین: در برنامه‌ی Blazor Server، سرویسی را جهت درج مشخصات امکانات رفاهی هتل تهیه کردیم. این امکانات رفاهی را از طریق Web API برنامه دریافت و سپس در برنامه‌ی سمت کلاینت نمایش دهید.
بنابراین تکمیل این تمرین شامل تهیه‌ی موارد زیر است که کدنویسی آن، با دو قسمت اخیر این سری دقیقا یکی است و نکته‌ی جدیدی را به همراه ندارد (و کدهای کامل آن را از انتهای بحث می‌توانید دریافت کنید):
- تهیه‌ی HotelAmenityController در پروژه‌ی Web API که به کمک IAmenityService، لیست امکانات رفاهی را بازگشت می‌دهد.
- تهیه‌ی ‍ClientHotelAmenityService در پروژه‌ی WASM که همانند ClientHotelRoomService قسمت قبل ، از Web API، لیست HotelAmenityDTO‌ها را دریافت می‌کند.
- ثبت سرویس جدید ‍ClientHotelAmenityService در Program.cs.
- در آخر حلقه‌ای را بر روی لیست HotelAmenityDTO دریافتی از ClientHotelRoomService در کامپوننت Index.razor تشکیل داده و آن‌ها را نمایش می‌دهیم.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-28.zip
نظرات مطالب
آپلود فایل‌ها توسط برنامه‌های React به یک سرور ASP.NET Core به همراه نمایش درصد پیشرفت
- مسیر نهایی تصویر را مطلق وارد کنید. مسیر مطلق https://localhost:5001/uploads/name.png که توسط برنامه‌ی ASP.NET Core مدیریت می‌شود، در یک مرورگر و یا هر برنامه‌ی دیگری، مستقل از برنامه‌ی React شما قابل دسترسی و نمایش است.
- برای آزمایش برنامه می‌توانید برنامه‌ی React و برنامه‌ی ASP.NET Core را بر روی یک پورت هم اجرا کنید (اگر می‌خواهید مسیرها را نسبی وارد کنید). اطلاعات بیشتر
نظرات مطالب
بررسی تصویر امنیتی (Captcha) سایت - قسمت دوم
هدف من از طرح این مساله این بود که نمایش تصویر امنیتی بدون در نظر گرفتن این مورد هیچ فایده ای برای سایت نخواهد داشت و کار به ماژول آنتی داس و مشکلات مربوط به آن کشیده می‌شود (یک لایه عقب تر).
در واقع کپچا یک تصویر تزیینی می‌شود.

دستکاری اطلاعات ارسالی به سرور، فقط در اینجا مطرح نیست. عمده مورد استفاده آن در ویرایش id‌ها (مثلا در DropDownList ها) ست. (ثبت نام مسکن برای شهری که هنوز ثبت نام آن فعال نشده، خرید بلیت جایگاه ویژه برای یک کنسرت، پرداخت بیمه با دستکاری هزینه و ...)
مطالب
OpenCVSharp #3
در قسمت دوم با نحوه‌ی بارگذاری تصاویر در OpenCVSharp آشنا شدیم. در این قسمت قصد داریم با نحوه‌ی ایجاد یک clone و نمونه‌ای مشابه از تصویر اصلی بارگذاری شده آشنا شویم. برای مثال هرچند متد LoadImage، دارای پارامتر بارگذاری تصویر، به صورت سیاه و سفید است، اما توصیه نمی‌شود که در بدو امر، تصویر را سیاه و سفید بارگذاری کنید. چون هرگونه تغییری در تصویر اصلی، امکان استفاده‌ی از آن‌را در سایر متدها و الگوریتم‌ها با مشکل مواجه می‌کند و استفاده‌ی از حالت LoadMode.GrayScale جهت بالا بردن سرعت عملیات، در کارهای پردازش تصویر بسیار معمول است.


تهیه‌ی یک نمونه‌ی سیاه و سفید از تصویر اصلی در OpenCVSharp

برای تهیه‌ی یک نمونه‌ی مشابه تصویر اصلی، از متد CreateImage استفاده می‌شود:
using (var src = Cv.LoadImage(@"..\..\images\ocv02.jpg", LoadMode.Color))
using (var dst = Cv.CreateImage(new CvSize(src.Width, src.Height), BitDepth.U8, 1))
{
    Cv.CvtColor(src, dst, ColorConversion.BgrToGray);
 
    using (new CvWindow("src", image: src))
    using (new CvWindow("dst", image: dst))
    {
        Cv.WaitKey();
    }
}
با این خروجی



معرفی متد CreateImage

پارامتر اول متد CreateImage، اندازه‌ی تصویر تولیدی را مشخص می‌کند. پارامتر دوم آن تعداد بیت تصویر را تعیین خواهد کرد. این تعداد بیت عموما بر اساس نیاز متدهای مختلف پردازش تصویر، متغیر خواهند بود و برای تعیین آن نیاز است مستندات هر متد را مطالعه کرد. BitDepth.U8 به معنای 8bit unsigned است.
پارامتر سوم این متد، تعیین کننده‌ی تعداد کانال تصویر است. تصاویر رنگی دارای سه کانال سبز، قرمز و آبی‌، هستند. چون در اینجا قصد داریم تصویر را سیاه و سفید کنیم، تعداد کانال را به عدد یک تنظیم کرده‌ایم.

متد CreateImage جهت سازگاری با اینترفیس C مربوط به OpenCV در اینجا وجود دارد. معادل
 using (var dst = Cv.CreateImage(new CvSize(src.Width, src.Height), BitDepth.U8, 1))
را می‌توان به نحو ذیل نیز نوشت:
 var dst = new IplImage(new CvSize(src.Width, src.Height), BitDepth.U8, 1)
و یا حتی پارامتر تعیین اندازه‌ی تصویر را نیز می‌توان ساده‌تر کرد:
 using (var dst = new IplImage(src.Size, BitDepth.U8, 1))


تبدیل تصویر به حالت سیاه و سفید

متد CvtColor جهت تغییر color space بکار می‌رود که در اینجا (BGR (Blue/Green/Red را به Gray تبدیل کرده‌است:
 Cv.CvtColor(src, dst, ColorConversion.BgrToGray);
این متد را در OpenCVSharp به نحو ذیل نیز می‌توان بازنویسی کرد:
 src.CvtColor(dst, ColorConversion.BgrToGray);

بنابراین به صورت خلاصه می‌توان کدهای ابتدای بحث را به صورت زیر نیز نوشت که با کلاس‌های OpenCVSharp بیشتر سازگاری دارد:
using (var src = new IplImage(@"..\..\images\ocv02.jpg", LoadMode.Color))
//using (var dst = new IplImage(new CvSize(src.Width, src.Height), BitDepth.U8, 1))
using (var dst = new IplImage(src.Size, BitDepth.U8, 1))
{
    src.CvtColor(dst, ColorConversion.BgrToGray);
 
    using (new CvWindow("src", image: src))
    using (new CvWindow("dst", image: dst))
    {
        Cv.WaitKey();
    }
}

کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.
نظرات مطالب
EF Code First #12
این یعنی به ازاء تمام درخواستهایی که سمت سرور ارسال میشه context تشکیل میشه (نیاز به context باشه یا نه ، مثلا یه تصویر قرار هست در صفحه نمایش داده شود و نیاز به کنترلر هم نداره. خطای گزارش شده نمایش آیکون بود) ؟
آیا از لحاظ Performance مشکلی نداره ؟

ممنون