نظرات مطالب
احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت ششم - کار با منابع محافظت شده‌ی سمت سرور
خطای 400، صرفا یک خطای عددی نیست. ظاهر آن یک عدد است، اما می‌تواند شامل ریز جزئیات خطاهای ارسالی از سمت سرور هم باشد:

bad requestها (یا خطای 400) همان return BadRequest‌های اعتبارسنجی اطلاعات (و یا سایر خطاهایی که خود فریم ورک ارسال می‌کند و نه اعتبارسنجی کاربران با خطای 401) هستند. مطلب « نمایش خطاهای اعتبارسنجی سمت سرور ASP.NET Core در برنامه‌های Angular » را مطالعه کنید تا با روش استخراج ریز جزئیات ارسالی این نوع خطاها از سمت سرور و نمایش آن‌ها به کاربر آشنا شوید. به علاوه لاگ‌های سمت سرور را هم مدنظر داشته باشید.
مطالب
Blazor 5x - قسمت 22 - احراز هویت و اعتبارسنجی کاربران Blazor Server - بخش 2 - ورود به سیستم و خروج از آن
در قسمت قبل، نحوه‌ی افزودن قالب ابتدایی ASP.NET Core Identity را به یک برنامه‌ی Blazor Server بررسی کردیم. در این مطلب، قسمت‌های ورود و خروج آن‌را به همراه نمایش قسمتی از صفحه، تنها به کاربران اعتبارسنجی شده، بررسی می‌کنیم تا روش دسترسی به اطلاعات ASP.NET Core Identity را در یک برنامه‌ی Blazor Server یکپارچه شده‌ی با آن، مطالعه کنیم.


نمایش قسمتی از صفحه بر اساس وضعیت اعتبارسنجی کاربر

فرض کنید می‌خواهیم در کامپوننت Shared\LoginDisplay.razor که در قسمت قبل آن‌را اضافه کردیم، لینک‌های ثبت نام و لاگین را به کاربران غیر اعتبارسنجی شده (هنوز لاگین نکرده) نمایش دهیم و اگر کاربر، اعتبارسنجی شده بود (لاگین کرده بود)، لینک خروج را به او نمایش دهیم. برای این منظور کامپوننت Shared\LoginDisplay.razor را به صورت زیر تغییر می‌دهیم:
<AuthorizeView>
    <Authorized>
        <a href="Identity/Account/Logout">Logout</a>
    </Authorized>
    <NotAuthorized>
        <a href="Identity/Account/Register">Register</a>
        <a href="Identity/Account/Login">Login</a>
    </NotAuthorized>
</AuthorizeView>
AuthorizeView، یکی از کامپوننت‌های استاندارد Blazor Server است. زمانیکه کاربری به سیستم لاگین کرده باشد، فرگمنت Authorized و در غیر اینصورت قسمت NotAuthorized آن‌را مشاهده خواهد کرد.
البته اگر برنامه را در همین حالت اجرا کنیم، به استثنای زیر خواهیم رسید:
InvalidOperationException: Authorization requires a cascading parameter of type Task<AuthenticationState>.
Consider using CascadingAuthenticationState to supply this.
Microsoft.AspNetCore.Components.Authorization.AuthorizeViewCore.OnParametersSetAsync()
برای رفع این مشکل و ارائه‌ی AuthenticationState به تمام کامپوننت‌های یک برنامه‌ی Blazor Server، نیاز است از کامپوننت CascadingAuthenticationState استفاده کرد. در مورد پارامترهای آبشاری، در قسمت نهم این سری بیشتر بحث شد و هدف از آن، ارائه‌ی یکسری اطلاعات، به تمام زیر کامپوننت‌های یک کامپوننت والد است؛ بدون اینکه نیاز باشد مدام این پارامترها را در هر زیر کامپوننتی، تعریف و تنظیم کنیم. همینقدر که آن‌ها را در بالاترین سطح سلسله مراتب کامپوننت‌های تعریف شده تعریف کردیم، در تمام زیر کامپوننت‌های آن نیز در دسترس خواهند بود.
بنابراین به فایل BlazorServer.App\App.razor که محل تعریف ریشه‌ی مسیریابی برنامه‌است، مراجعه کرده و کامپوننت آن‌را با کامپوننت توکار CascadingAuthenticationState محصور می‌کنیم:
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        <Found Context="routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
اینکار سبب می‌شود تا اطلاعات AuthenticationState، بین تمام کامپوننت‌های یک برنامه‌ی Blazor به اشتراک گذاشته شود.

اکنون اگر برنامه را اجرا کنیم، مشاهده خواهیم کرد که در اولین بار مراجعه‌ی به آن (پیش از لاگین)، لینک به صفحه‌ی خروج، نمایش داده نشده‌است؛ چون آن‌را در فرگمنت مخصوص Authorized قرار دادیم:



آزمایش نمایش منوی خروج برنامه

برای آزمایش برنامه، نیاز است ابتدا یک کاربر جدید را ثبت کنیم؛ چون هنوز هیچ کاربری در آن ثبت نشده‌است و همچنین کاربر پیش‌فرضی را هم به همراه ندارد. در مورد روش ثبت کاربران پیش‌فرض ASP.NET Core Identity، می‌توانید به مطلب «بازنویسی متد مقدار دهی اولیه‌ی کاربر ادمین در ASP.NET Core Identity توسط متد HasData در EF Core» مراجعه کنید و تمام نکات آن، در اینجا هم صادق است (چون پایه‌ی سیستم Identity مورد استفاده، یکی است و هدف ما در اینجا بیشتر بررسی نکات یکپارچه سازی آن با Blazor Server است و نه مرور تمام نکات ریز Identity).
بنابراین ابتدا از منوی بالای صفحه، گزینه‌ی Register را انتخاب کرده و کاربری را ثبت می‌کنیم. پس از ثبت نام، بلافاصله به منوی جدید زیر می‌رسیم که در آن گزینه‌های ورود و ثبت نام، مخفی شده‌اند و اکنون گزینه‌ی خروج از سیستم را نمایش می‌دهد:



بهبود تجربه‌ی کاربری خروج از سیستم

در همین حال که گزینه‌ی خروج نمایش داده شده‌است، اگر بر روی لینک آن کلیک کنیم، ابتدا ما را به صفحه‌ی مجزای logout هدایت می‌کند. سپس باید در این صفحه، مجددا بر روی لینک logout بالای آن کلیک کنیم. زمانیکه اینکار را انجام دادیم، اکنون صفحه‌ی دیگری را نمایش می‌دهد که به همراه پیام «خروج موفقیت آمیز از سیستم» است! در این پروسه، کاربر احساس می‌کند که کاملا از برنامه‌ی اصلی خارج شده‌است و همچنین مراحل طولانی را نیز باید طی کند.
مدیریت این مراحل توسط دو فایل زیر انجام می‌شوند:
Areas\Identity\Pages\Account\Logout.cshtml
Areas\Identity\Pages\Account\Logout.cshtml.cs

می‌خواهیم کدهای این دو فایل را به نحوی تغییر دهیم که اگر کاربری بر روی لینک logout برنامه‌ی اصلی کلیک کرد، به صورت خودکار logout شده و سپس مجددا به صفحه‌ی اصلی برنامه‌ی Blazor Server هدایت شود و مجبور نباشد تا مراحل طولانی یاد شده را تکرار کند.
به همین جهت ابتدا فایل Logout.cshtml.cs را حذف می‌کنیم؛ چون نیازی به آن نداریم. سپس محتوای فایل Logout.cshtml را به صورت زیر تغییر می‌دهیم:
@page
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager

@functions
{
    public async Task<IActionResult> OnGet()
    {
        if (SignInManager.IsSignedIn(User))
        {
            <p>You have successfully logged out of the application.</p>
            await SignInManager.SignOutAsync();
        }
        return Redirect("~/");
    }
}
با استفاده از سرویس SignInManager در ASP.NET Core Identity می‌توان یک کاربر را logout کرد که نمونه‌ای از آن‌را در اینجا مشاهده می‌کنید. در این حالت بررسی می‌شود که آیا کاربر جاری، به سیستم وارد شده‌است؟ اگر بله، کوکی‌های او حذف شده و سپس به صفحه‌ی اصلی برنامه، Redirect می‌شود. به این ترتیب به تجربه‌ی کاربری خروج بهتری خواهیم رسید.


نمایش User Claims، در یک برنامه‌ی Blazor Server

سیستم ASP.NET Core Identity، بر اساس User Claims کار می‌کند؛ اطلاعات بیشتر. پس از استفاده از CascadingAuthenticationState در بالاترین سطح برنامه، اطلاعات آن در سراسر برنامه‌ی Blazor Server هم قابل دسترسی است. برای مثال در کامپوننت Shared\LoginDisplay.razor، به نحو زیر می‌توان نام کاربر ثبت نام شده را که یکی از User Claims او است، نمایش داد:
<AuthorizeView>
    <Authorized>
        Hello, @context.User.Identity.Name
        <a href="Identity/Account/Logout">Logout</a>
    </Authorized>



محدود کردن دسترسی به صفحات برنامه تنها برای کاربران اعتبارسنجی شده

پس از لاگین موفق به سیستم، اکنون می‌خواهیم دسترسی به صفحات تعریف اتاق‌ها و یا امکانات رفاهی هتل را تنها به کاربران لاگین شده، محدود کنیم. برای اینکار تنها کافی است از ویژگی Authorize استفاده کنیم. برای مثال به کامپوننت Pages\HotelRoom\HotelRoomList.razor مراجعه کرده و یک سطر زیر را به آن اضافه می‌کنیم:
@attribute [Authorize]
دسترسی به کامپوننتی که دارای دایرکتیو فوق باشد، تنها مختص به کاربران اعتبارسنجی شده‌ی سیستم است.

مشکل! با اینکه تمام کامپوننت‌های مثال جاری را به ویژگی Authorize مزین کرده‌ایم، اما ... کار نمی‌کند! و هنوز هم می‌توان بدون لاگین به سیستم، به محتوای آن‌ها دسترسی داشت.
برای رفع این مشکل، مجددا نیاز است کامپوننت BlazorServer.App\App.razor را ویرایش کرد:
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        <Found Context="routeData">
            @*<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />*@
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    <p>Sorry, you do not have access to this page</p>
                </NotAuthorized>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
در اینجا RouteView پیشین را کامنت کرده و با AuthorizeRouteView، جایگزین کرده‌ایم. کار آن فعالسازی پردازش ویژگی Authorize افزوده شده‌ی به کامپوننت‌های برنامه‌است. همچنین در اینجا محتوای سفارشی را که در صورت درخواست یک چنین کامپوننت‌هایی نمایش داده می‌شود، در فرگمنت NotAuthorized مشاهده می‌کنید؛ که حتی می‌تواند یک کامپوننت مجزا هم باشد:



کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-22.zip
مطالب
معرفی سرویس‌های ارائه شده توسط شرکت‌های گوگل، آمازون و مایکروسافت در قالب رایانش ابری - قسمت دوم
همانطور که که در قسمت اول اشاره گردید، شرکت گوگل به ارائه سرویس‌های متنوعی بر اساس فناوری رایانش ابری پرداخته است. در این بخش به معرفی سرویس‌های ابری ارائه شده توسط شرکت آمازون پرداخته می‌شود. 
وب سایت این شرکت برای پوشش ترافیک در تمام طول سال به میزان بالایی زیرساخت نرم افزاری و سخت افزاری خود را گسترش داده است. بر همین اساس، این شرکت به منظور جلوگیری از اتلاف منابع ایجاد شده و کسب منافع مالی قابل توجه، به مرور امکان استفاده از منابع شبکه­‌اش را برای کاربران مهیا ساخته است. آمازون در سال 2006 سکوی وب سرویس خود را به عنوان مدل مصرفی در دسترس توسعه دهندگان قرار داد. این شرکت از طریق مجازی سازی سخت افزار بر روی Xen Hypervisor می­تواند سرورهای مجازی ایجاد کند. وب سرویس­های آمازون (Amazon Web Services -AWS) چیزی که اصولاً ظرفیت استفاده نشده زیر ساخت شبکه آمازون است را می­گیرد و آن را به تجارتی سودمند تبدیل می­کند.
سرویس‌های آمازون بی تردید نمایانگر بزرگترین IaaS محض در دنیای امروز هستند. ابر محاسباتی توسعه پذیر آمازون(Amazon Elastic Compute Cloud - EC2) که بزرگترین مولفه محصولات آمازون است در سال 2009 بالغ بر 220 میلیون دلار درآمد داشته است و تخمین زده می‌شود که EC2 بر روی بیش از چهل هزار سرور جهانی که در شش نقطه جهان تقسیم شده اند، اجرا می‌گردد.

صفحه اصلی وب سرویس‌های آمازون

سرویس‌ها و اجزای وب سرویس آمازون:

وب سرویس­های آمازون دارای اجزای زیادی می­باشند. تعدادی از این سرویس­ها برای ارائه خدمات پردازشی و تعداد دیگری برای ارائه فضای ذخیره­سازی، عرضه شده‌­اند. در ادامه گروهی از این سرویس­ها معرفی می­گردد: 

  1. ابر محاسباتی توسعه پذیر آمازون (EC2)
این سرویس، استفاده و مدیریت سرورهای اختصاصی مجازی که سیستم عامل­های لینوکس یا ویندوز را بر روی Xen Hypervisor  اجرا می­کنند، میسر کرده است. نمونه­‌های ماشین با توان­های پردازشی مختلف موجود می­باشد و بر اساس محاسبات/ساعت اجاره می­شوند. برنامه­‌های مستقر بر روی این ماشین­ها بسیار توسعه پذیر و با تحمل پذیری بالای خطا می­باشند. ذکر تفاوت میان یک نمونه ماشین و یک تصویر ماشین می­تواند به درک مفاهیم موجود در سرویس آمازون کمک کند. به طور کلی نمونه ماشین در واقع تقلید یا همسان­سازی(Emulation) سکوی سخت­افزاری مانند x86 و غیره بر روی لایه نرم­افزار مجازی Xen می­باشد. در حالی که تصویر ماشین، نرم افزار و سیستم عاملی است که در سطح یک نمونه ماشین اجرا می­شود و می­توان به محتویات یک درایو راه‌­انداز تشبیه نمود. تعدادی از ابزارهایی که برای پشتیبانی سرویس­های EC2 استفاده می­شوند به شرح زیر است:

  • سرویس صف ساده آمازون(Simple Queue Service):  یک صف پیام یا سیستم تراکنش برای برنامه­‌های مبتنی بر اینترنت توزیع شده می­باشد. این سرویس تضمین می­کند که پیام­ها حتی در زمانی که مؤلفه‌ای موجود نیست، گم نشود و برای انتقال پیام میان مؤلفه‌های مختلف که هرکدام کار جداگانه‌­ای را انجام می­دهند، بسیار مناسب است.
  • سرویس آگاه سازی ساده آمازون(Simple Notification Service):  ): وب سرویسی است که می­تواند پیام یک برنامه را منتشر کند و آن­ها را به برنامه­‌ها یا مشترکین دیگر منتقل کند. SNS  متدی را برای راه­‌اندازی فعالیت­ها ارائه می­نماید که برنامه­‌ها را قادر می­سازد تا در مورد اطلاعات جدید یا تغییر یافته از آن‌ها نظرسنجی شود یا به روز رسانی­‌ها را انجام دهند.
  • سرویس نظارت ابر آمازون(Amazon Cloud Watch):  کنسولی را فراهم می­کند که در آن مصرف منابع، شاخص­‌های کلیدی عملکرد سایت و نشانگرهای عملیاتی برای عواملی همچون تقاضای پردازشگر، مصرف دیسک و ورودی و خروجی شبکه را ارائه می­دهد.  نتایج معیارهایی که توسط آن کسب ­می­شود برای فعال‌سازی قابلیتی به نام Auto Scaling  مورد استفاده قرار می­گیرد که به صورت خودکار می­تواند یک سایت EC2 را بر مبنای مجموعه‌­ای از قوانین که توسعه دهنده ایجاد می­کند، توسعه دهد.
  •   توازن بار منعطف(Elastic Load Balancing): نمونه­‌های ماشین آمازون(Amazon Machine Image) با استفاده از این قابلیت، دارای امکان توازن بار ترافیکی می­شوند. این قابلیت هنگامی که نمونه‌­ای دچار شکست می­شود آن را کشف کرده و ترافیک را به یک نمونه سالم حتی نمونه‌­ای در محیط­‌های دیگر AWS  مسیریابی مجدد می­کند.
    2.  سیستم ذخیره سازی ساده آمازون (Amazon Simple Storage Service - S3)
یک سیستم ذخیره­سازی و پشتیبان گیری آنلاین است و دارای قابلیت انتقال سریع داده به نام  AWS Import/Export  می­باشد و داده را با استفاده از شبکه داخلی آمازون از AWS به دستگاه­‌های ذخیره­‌سازی قابل حمل منتقل می­نماید. این سیستم دسترسی به واحدهای اطلاعاتی را از طریق API وب S3 به کمک استانداردهای SOAP یا REST فراهم می‌کند. از آنجایی که دسترسی به داده با پهنای باند پایین میسر است، از این نوع حافظه بیشتر برای کارهای غیر عملیاتی مانند آرشیو و بازیابی یا پشتیبان گیری از دیسک استفاده می­شود.
    3.  انبار بلوک بسط پذیر آمازون (Amazon Elastic Block Store - EBS)
سیستمی است برای ساخت دیسک‌­های مجازی یا دستگاه­‌های ذخیره­سازی بلوکی که برای نمونه­‌های ماشین آمازون در EC2  مورد استفاده قرار می­گیرند. مزیت این سیستم این است دارای عملکرد بالاتر و قابل اعتماد‌تر از آمازون S3 است به همین دلیل یک واسط ذخیره سازی داده عملیاتی بسیار ارزشمند برای AWS  است. همچنین هزینه ایجاد EBS  مناسب‌تر از مشابه S3 می‌باشد. هر EBS پس از ایجاد بر روی یک نمونه مشخص سوار یا نصب می­شود و تنها برای آن نمونه قابل دسترسی خواهد بود. از این‌رو اشتراک آن­ها بین نمونه­‌ها امکان پذیر نمی­باشد. این سرویس بر اساس فضای ذخیره سازی مصرفی، مدت زمان استفاده و تعداد تقاضاهای ورودی/خروجی قیمت گزاری می­شود.
    4.  پایگاه داده ساده آمازون (Amazon Relational Database Service - RDS) 
این سرویس نمونه­‌های پایگاه داده MySQL را برای پشتیبانی از وب سایت و سایر برنامه‌­هایی که متکی بر سرویس‌­های داده محور(Data Driven) می­باشند، ایجاد می­کند. این سرویس برنامه­‌های پایگاه داده­‌ای که قبلاً در محیط دیگری ساخته شده­‌ا‌ند را پشتیبانی می­نماید و هر برنامه­‌ای که با پایگاه داده MySQL کار می‌کند با RDS نیز کار خواهد کرد. یکی از ویژگی­‌های مهم RDS سیستم پشتیبان گیری خودکار برای داده‌­های درون پایگاه و گزارشات تراکنش MySQL می­باشد. فایل­های پشتیبان به مدت 8 روز ذخیره می­شوند و علاوه بر آن امکان تصویر برداری از پایگاه داده نیز وجود دارد.

مدل قیمت گذاری:

قیمت گذاری انواع مختلف نمونه­ ماشین آمازون به سه پارامتر وابسته است. اولین مورد سیستم عامل مورد استفاده است. دومین عامل مرکز داده­‌ای است که در آن قرار گرفته و سومین عامل مدت زمانی است که اجرا می­شود. نرخ‌­ها بر مبنای ساعت محاسبه می­شوند. علاوه بر آن مبالغ اضافی نیز بابت موارد زیر اخذ می­شود: 

  • میزان داده منتقل شده 
  • آدرس‌های IP اختصاصی
  • استفاده سرور اختصاصی مجازی از فضای ذخیره­سازی بلوکی توسعه پذیر آمازون
  • استفاده از  توازن بار توسعه پذیر برای دو یا چند سرور 
  • سایر ویژگی­های مورد نیاز 
به طور کلی نمونه­ ماشین‌­های آمازون که ذخیره شده‌­اند و خاموش هستند، هزینه کلی نگهداری کمتری دارند و مبلغ اضافه به ازای هر ساعت محاسبه نمی­شود و فقط هزینه حافظه مورد استفاده پرداخت می­گردد. به طور کلی پرداخت هزینه به منظور استفاده از نمونه­ ماشین آمازون در سه مدل مقدور است: 
  • نمونه مبتنی بر تقاضا: نرخ ساعتی بدون التزام طولانی مدت
  • نمونه رزرو شده: خرید قراردادی هر نمونه با هزینه به مراتب پایین‌تر به ازای هر ساعت بعد از رزرو اولیه

  • نمونه نقطه­‌ای: این متد برای قیمت گذاری بر روی ظرفیت استفاده نشده EC2 بر مبنای قیمت نقطه فعلی است. این قابلیت، قیمت­‌های بسیار پایین را به همراه خواهد داشت اما در زمان­‌های مختلف فرق می­کند یا در زمانی که ظرفیت مازادی نباشد، در دسترس نخواهد بود. 

در جدول زیر  مشخصات سخت افزاری انواع نمونه ماشین­‌های آمازون ذکر شده‌­اند و با توجه به قیمت گذاری نمونه‌ها بر اساس موقعیت جغرافیایی که در آن قرار گرفته‌­اند، بسیار متنوع است، از ذکر این موارد اجتناب نموده و علاقه‌مندان به کسب اطلاعات بیشتر به وب سایت شرکت آمازون ارجاع داده می­شوند. همچنین ذکر این نکته ضروری است که شرکت آمازون به منظور تست و توسعه سرویس‌های ارائه شده، اکانت یکساله رایگان با امکان استفاده از سرویس‌ها به صورت محدود، ارائه می‌نماید.
نوع 
موتور محاسبه    حافظه اصلی(GB)    ذخیره سازی(GB) سکو   
 ریز نمونه   تا دو واحد محاسباتی در انفجار بار    0.613   EBS    32 یا 64 بیتی 
 نمونه کوچک   یک واحد محاسباتی    1.7    160   32 بیتی   
 نمونه بزرگ   چهار واحد محاسباتی    7.5    850    64 بیتی 
 نمونه بسیار بزرگ   هشت واحد محاسباتی    15    1690    64 بیتی 
   
   
مطالب
یک سرویس (میکروسرویس) چیست؟ و چگونه آن را مستند کنیم؟ (قسمت اول)
معماری میکروسرویس (یا به اختصار: میکروسرویس) یک سبک معماری نرم افزار می‌باشد که در آن یک نرم افزار، به مجموعه‌ای از سرویس‌ها خرد می‌شود؛ به نحوی که هر سرویس مسئولیت انجام بخشی از منطق کسب و کار را به عهده داشته باشد.
این تقسیم بندی مزایای متعددی را به همراه دارد که نهایتا پیاده سازی و توسعه راحت‌تر نرم افزار‌های بزرگ و پیچیده را ممکن می‌نماید. از جمله مزایای این معماری می‌توان به راحت‌تر شدن مباحث continuous delivery/deployment، مقیاس پذیری بهتر، تحمل خطا، مهاجرت به (و یا استفاده از) تکنولوژی‌های جدید در بخش‌های مختلف نرم افزار و ... اشاره نمود.

مهم‌ترین بخش و تصمیمات شما به عنوان یک معمار نرم افزار، هنگام طراحی با استفاده از این معماری، شناسایی بخش‌های مختلف کسب و کار، جدا سازی و مرزبندی نمودن آنها و نهایتا طراحی سرویس‌ها و تعیین نحوه همکاری آنها با یکدیگر می‌باشد. لذا در هنگام استفاده از معماری میکروسرویس، مرکز توجهات باید کسب و کار باشد و نه مسائل تکنیکال و موضوعاتی مانند Docker, Kubernetes , Serverless و ... . (DDD می‌تواند به شما جهت مرزبندی بخش‌های مختلف کسب و کار و شناسایی سرویس‌ها کمک نماید)

تا اینجا متوجه شدیم که میکروسرویس در واقع یک سبک معماری نرم افزار محسوب می‌گردد و در واقع میکروسرویس (در اینجا و ادامه مقاله، منظور از میکروسرویس، معماری میکروسرویس می‌باشد) از چندین سرویس مجزا و مستقل تشکیل شده‌است که هر سرویس معمولا مسئولیت بخشی از منطق کسب و کار را بر عهده خواهد داشت.

مشخصات یک سرویس
هر سرویس در معماری میکروسرویس دارای چندین ویژگی اصلی به شرح زیر می‌باشد:
- Loosely coupled with other services - باید به طور مستقل از سایر سرویس‌ها عمل کند. به این معنا که تغییر و توسعه سایر سرویس‌ها موجب اختلالی در عملکرد این سرویس نگردد و برعکس، تغییر و توسعه این سرویس نباید عملکرد سایر سرویس‌ها را مختل نماید.
- Independently deployable - تیم توسعه دهنده سرویس قادر باشد تا بدون نیاز به هماهنگی با سایر تیم‌ها، خدمات خود (شامل ویژگی‌های جدید و تغییرات) را مستقر (Deploy) نماید.
- Capable of being developed by a small team – سرویس، امکان توسعه توسط یک تیم کوچک را داشته باشد. این مورد به جهت جلوگیری از سربار زیاد ناشی از هماهنگی در تیم‌های بزرگ، ضرورت دارد.
- Highly maintainable and testable – سرویس بسیار قابل نگهداری و قابل آزمایش باشد؛ امکان توسعه، تست و استقرار سریع را داشته باشد.

ساختار یک سرویس
حال که با ویژگی‌ها و مشخصات اصلی یک سرویس آشنا شدیم، در دیاگرام زیر، ساختار درونی یک سرویس را که از معماری هگزاگون (hexagonal architecture) استفاده می‌نماید، بررسی میکنیم. در این معماری، هسته سرویس، منطق کسب کار (Business logic) می‌باشد که توسط چندین آداپتور (جهت ارتباط با سایر سرویس‌ها) احاطه شده است.

بیایید با دقت به هر یک از بخش‌های یک سرویس (با توجه به دیاگرام فوق) نگاه کنیم

هر سرویس  احتمالا دارای یک یا چندین API می‌باشد
از دید مصرف کنندگان یک سرویس (Consumers)، تنها مورد با اهمیت یک سرویس، APIهای آن سرویس می‌باشد. APIهای یک سرویس نیز (با توجه به تصویر فوق) شامل عملیات یا Operations و وقایع منتشر شده یا Published events می‌باشند. که در ادامه این انواع را بررسی میکنیم.

- عملیات (Operations)
به صورت کلی و همانطور که در دیاگرام فوق قابل مشاهده می‌باشد، عملیات به دو نوع دستورات (Commands) و جستارها (Queries) تقسیم می‌شوند. دستورات نوعی از عملیات می‌باشند که موجب تغییر داده‌ها می‌شود؛ اما در مقابل جستارها، عملیاتی در جهت واکشی داده‌ها می‌باشند. برای مثال یک سرویس  ثبت سفارش (OrderService) را در نظر بگیرید. عملیاتی مانند ثبت سفارش ()CreateOrder، انصراف از سفارش ثبت شده  ()CancelOrder و ... عملیاتی از نوع دستورات هستند و عملیاتی مانند یافتن یک سفارش خاص ()FindOrder که هیچ دیتایی را تغییر نمیدهد، از نوع جستارها می‌باشند.
عملیات ارائه شده توسط یک سرویس میتواند از ترکیبی از پروتکل‌های همزمان (Synchronous protocols) مانند REST یا gRPC و پروتکل‌های غیر همزمان (Asynchronous protocols) مانند messaging باشند.
پروتکل‌های همزمان، به ویژه REST، بیشتر در مواردی که قصد ارائه API به کلاینت‌های خارجی (External clients) را داریم، مانند موبایل اپلیکیشن‌ها و یا نرم افزارهای تک صفحه‌ای (SPA) کاربرد دارند.
از پروتکل‌های غیر همزمان مانند messaging نیز بیشتر در مواردی که میخواهیم الگوی ساگا (SAGA) را پیاده سازی نماییم و به روز نگه داشتن داده‌ها را بین سرویس‌های مختلف حفظ کنیم، نیاز به استفاده داریم. برای مثال در همان سیستم ثبت سفارش، عملیات ()CreateOrder به صورت Rest و با متد Post در Endpoint ای مانند /Order پیاده سازی می‌شود و پس از فراخوانی، یک عملیات غیرهمزمان مانند CreateOrderSaga را نیز به صورت messaging آغاز میکند.

- وقایع (Events)
سرویس‌ها، اغلب وقایعی (Event) را نیز منتشر میکنند. منظور از وقایع یا events معمولا همان مفهوم domain event درDDD می‌باشد که در همان ادبیات DDD وقایع توسط aggregate‌ها در زمان هایی مانند ایجاد، ویرایش، حذف و یا سایر مفاهیم موجود در منطق کسب و کار منتشر می‌شوند. سرویس نیز معمولا این وقایع را روی یک کانال ارتباطی (message channel) که توسط یک message broker (مانند RabbitMQ, Apache Kafka, ActiveMQ و ...) پیاده سازی شده است، منتشر میکند. و علاقمندان به دریافت این وقایع می‌توانند وقایع را پس از انتشار، بر روی کانال ارتباطی دریافت نمایند.


منطق کسب و کار (Business Logic)
منطق کسب و کار، قلب هر سرویس و دلیل وجود آن سرویس می‌باشد که API هایی را در قالب عملیات (Opertaions) پیاده سازی و همچنین مواردی را در قالب وقایع (Events) منتشر می‌نماید. همچنین منطق کسب و کار می‌تواند بنا بر نیاز خود، عملیات مربوط به سایر سرویس‌ها را فراخوانی و یا در کانال‌های ارتباطی (channels) مربوط به وقایع آنها، مشترک (Subscribes) شود و نهایتا داده‌ها را در دیتابیس خود نگهداری نماید.

نحوه همکاری سرویس‌ها با یکدیگر (Services Collaborations)
با توجه به مفاهیم فوق، زمانی که صحبت از همکاری (collaborate) بین سرویس‌ها می‌شود، معمولا منظور، ارتباط آنها از طریق APIهای یکدیگر (شامل عملیات و وقایع که پیش‌تر توضیح داده شد) می‌باشد (به جای خواندن و نوشتن مستقیم در دیتابیس‌های مربوط به یکدیگر می‌باشد).
برای مثال یک سرویس ممکن است عملیات مربوط به ایجاد سفارش ()CreateOrder را از سرویس ثبت سفارش (OrderService) فراخوانی نماید و یا برعکس خود سرویس ثبت سفارش (OrderService) ممکن است بر حسب نیاز منطق کسب و کار خود، عملیات ارائه شده توسط سرویس انبار را فراخوانی نماید.
همچنین یک سرویس جهت همکاری با دیگر سرویس‌ها میتواند در وقایع منتشر شده (Published events) توسط آنها مشترک (Subscribes) شود. برای مثال سرویس ثبت سفارش احتمالا در وقایع منتشر شده از سوی سرویس رستوران مشترک می‌شود.

دیتابیس اختصاصی
معمولا هر سرویس دارای یک یا چند دیتابیس می‌باشد که دیتای اختصاصی مربوط به منطق کسب و کار خود و در مواردی بخشی از دیتای مربوط به سایر سرویس‌ها را در آن‌(ها) نگهداری میکند. برای مثال اطلاعات سفارش‌ها را هم سرویس ثبت سفارش و هم سرویس رستوران، هر دو نگهداری میکنند و عملا این دیتا ابتدا در سرویس رستوران و سپس در سرویس ثبت سفارش، مجددا نگهداری می‌شود و به نوعی دیتای فوق Replicate شده و تکراری می‌باشد. اما به جهت اطمینان از کاهش وابستگی (loose coupling) این تکرار داده‌ها انجام می‌شود. در مجموع استفاده از یک دیتابیس مشترک (منظور table مشترک می‌باشد) بین سرویس‌ها ایده‌ی بدی می‌باشد و سرویس‌ها باید از طریق API‌های یکدیگر باهم همکاری نمایند.

نتیجه
در این مقاله عنوان شد که میکروسرویس یک سبک معماری می‌باشد و در این معماری، نرم افزار و منطق کسب و کار، به چندین سرویس مختلف  تقسیم می‌شود. مشخصات کلیدی که هر سرویس باید در این سبک معماری (microservice architecture) داشته باشد و همچنین ساختار درونی هر سرویس بررسی شد.
در قسمت بعدی این مقاله، در مورد نحوه مستند سازی این سرویس‌ها صحبت می‌شود. چرا که با زیاد شدن تعداد سرویس‌ها، در صورت عدم وجود یک مستندات مناسب (documents)، ارتباط و هماهنگی تیم‌ها با یکدیگر خود موجب سربار خواهد شد.

منابع
برگرفته شده از مقاله آقای ریچاردسون (whats-a-service
مطالب
اضافه کردن قابلیت از سرگیری مجدد (resume) به HttpWebRequest

مطابق RFC مربوطه، اگر هدر درخواست ارسالی به سرور را کمی تغییر دهیم می‌توان بجای شروع از اولین بایت، از بایت مورد نظر شروع به دریافت فایل نمود. (البته این به شرطی است که سرور آن‌را پشتیبانی کند)

یعنی نیاز داریم که به هدر ارسالی سطر زیر را اضافه کنیم:
Range: bytes=n-
که n در اینجا حجم فایل ناقص دریافتی موجود بر حسب بایت است.
برای بدست آوردن اندازه‌ی فایل ناقص موجود می‌توان از دستور زیر استفاده کرد:
using System.IO;
long brokenLen = new FileInfo(fileNamePath).Length;

سپس اگر شیء webRequest ما به صورت زیر تعریف شده باشد:

HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);

فقط کافی است سطر زیر را جهت افزودن قابلیت از سرگیری مجدد دریافت فایل به این شیء افزود:

//دانلود از ادامه
webRequest.AddRange((int)brokenLen); //resume

نکته:
اگر علاقمند باشید که ریز فعالیت‌های انجام شده توسط فضای نام System.Net را ملاحظه کنید، به فایل config خود (مثلا فایل app.config برنامه)، چند سطر زیر را اضافه کنید:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="true" />
<sources>
<source name="System.Net">
<listeners>
<add name="MyTraceFile"/>
</listeners>
</source>
</sources>

<sharedListeners>
<add
name="MyTraceFile"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="System.Net.trace.log"
/>
</sharedListeners>

<switches>
<add name="System.Net" value="Verbose" />
</switches>

</system.diagnostics>
</configuration>
به این صورت کلیه هدرهای ارسالی به سرور و ریز فعالیت‌های انجام شده در پشت صحنه‌ی کلاس‌های موجود در فایلی به نام System.Net.trace.log برای شما ثبت خواهد شد.


ملاحظات:
بدیهی است پیاده سازی قابلیت resume نیاز به موارد زیر خواهد داشت:
الف) در نظر گرفتن مسیری پیش فرض برای ذخیره سازی فایل‌ها
ب) پیدا کردن اندازه‌ی فایل موجود بر روی یک سرور و مقایسه‌ی آن با حجم فایل موجود بر روی هارد
امکان پیدا کردن اندازه‌ی یک فایل هم بدون دریافت کامل آن میسر است. خاصیت ContentLength مربوط به شیء HttpWebResponse بیانگر اندازه‌ی یک فایل بر روی سرور است و صد البته پیش از استفاده از این عدد، مقدار StatusCode شیء نامبرده را بررسی کنید. اگر مساوی OK بود، یعنی این عدد معتبر است.

نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت اول - نصب پیشنیازها
با سلام و خسته نباشید و تشکر از بابت مطالب مفیدتان
سوال بنده در مورد Angular اینه که آیا برای نوشتن سایت‌های تجاری مفیده یا نه ،
چند مورد که من بهش فکر کردم و جایی نتونستم ازش نتیجه بگیرم موارد زیره که اگه راهنمایی کنید برای ادامه آموزش بهتر
1- ایا آوردن اطلاعات و محتویات سایت همراه اکشن درخواست صفحه بهتر نیست و باعث سرعت بیشتر سایت نمیشه؟(معمولا اطلاعات در ساختار فریمورک انگولا بعد از لود شدن همه html‌ها و فایل‌های js  و با یک سرویس دریافت میشه "مثل لود کردن محتوی ایجکسی" که به نظرم به خاطر دو مرحله ای بودن کندی زیادی داره ) 
2- آیا در Seo تاثیر داره ؟ محتوا و روتینگ‌ها همه با ایجکس انجام میشه و نمیدونم گوگل این چیزا روی میفهمه یا نه 
در کل ممنون میشه که بدونم شما اگه بخواید یه سایت مثلا همین سایتتون را از اول بنویسید آیا از این فریمورک استفاده میکنید یانه
مطالب
مقدار دهی کلیدهای خارجی در NHibernate و Entity framework

ORM های NHibernate و Entity framework روش‌های متفاوتی را برای به روز رسانی کلید خارجی با حداقل رفت و برگشت به دیتابیس ارائه می‌دهند که در ادامه معرفی خواهند شد.

صورت مساله:
فرض کنید می‌خواهیم برنامه‌ای را بنویسیم که ریز پرداخت‌های روزانه‌ی ما را ثبت کند. برای اینکار حداقل به یک جدول گروه‌های اقلام خریداری شده، یک جدول حساب‌های تامین کننده‌ی مخارج، یک جدول فروشنده‌ها و نهایتا یک جدول صورتحساب‌های پرداختی بر اساس جداول ذکر شده نیاز خواهد بود.

الف) بررسی مدل برنامه



در اینجا جهت تعریف ویژگی‌ها یا Attributes تعریف شده در این کلاس‌ها از NHibernate validator استفاده شده (+). مزیت اینکار هم علاوه بر اعتبارسنجی سمت کلاینت (پیش از تبادل اطلاعات با بانک اطلاعاتی)، تولید جداولی با همین مشخصات است. برای مثال Fluent NHibernate بر اساس ویژگی Length تعریف شده با طول حداکثر 120 ، یک فیلد nvarchar با همین طول را ایجاد می‌کند.

public class Account
{
public virtual int Id { get; set; }

[NotNullNotEmpty]
[Length(Min = 3, Max = 120, Message = "طول نام باید بین 3 و 120 کاراکتر باشد")]
public virtual string Name { get; set; }
}

public class Category
{
public virtual int Id { get; set; }

[NotNullNotEmpty]
[Length(Min = 3, Max = 130, Message = "طول نام باید بین 3 و 130 کاراکتر باشد")]
public virtual string Name { get; set; }
}

public class Payee
{
public virtual int Id { get; set; }

[NotNullNotEmpty]
[Length(Min = 3, Max = 120, Message = "طول نام باید بین 3 و 120 کاراکتر باشد")]
public virtual string Name { get; set; }
}

public class Bill
{
public virtual int Id { get; set; }

[NotNull]
public virtual Account Account { get; set; }

[NotNull]
public virtual Category Category { get; set; }

[NotNull]
public virtual Payee Payee { get; set; }

[NotNull]
public virtual decimal Amount { set; get; }

[NotNull]
public virtual DateTime BillDate { set; get; }

[NotNullNotEmpty]
[Length(Min = 1, Max = 500, Message = "طول توضیحات باید بین 1 و 500 کاراکتر باشد")]
public virtual string Description { get; set; }
}




ب) ساختار جداول متناظر (تولید شده به صورت خودکار توسط Fluent NHibernate در اینجا)


در مورد نحوه‌ی استفاده از ویژگی AutoMapping و همچنین تولید خودکار ساختار بانک اطلاعاتی از روی جداول در NHibernate قبلا توضیح داده شده است. البته بدیهی است که ترکیب مقاله‌ی Validation و آشنایی با AutoMapping در اینجا جهت اعمال ویژگی‌ها باید بکار گرفته شود که در همان مقاله‌ی Validation مفصل توضیح داده شده است.
نکته‌ی مهم database schema تولیدی، کلید‌های خارجی (foreign key) تعریف شده بر روی جدول Bills است (همان AccountId، CategoryId و PayeeId تعریف شده) که به primary key جداول متناظر اشاره می‌کند.
    create table Accounts (
AccountId INT IDENTITY NOT NULL,
Name NVARCHAR(120) not null,
primary key (AccountId)
)

create table Bills (
BillId INT IDENTITY NOT NULL,
Amount DECIMAL(19,5) not null,
BillDate DATETIME not null,
Description NVARCHAR(500) not null,
AccountId INT not null,
CategoryId INT not null,
PayeeId INT not null,
primary key (BillId)
)

create table Categories (
CategoryId INT IDENTITY NOT NULL,
Name NVARCHAR(130) not null,
primary key (CategoryId)
)

create table Payees (
PayeeId INT IDENTITY NOT NULL,
Name NVARCHAR(120) not null,
primary key (PayeeId)
)

alter table Bills
add constraint fk_Account_Bill
foreign key (AccountId)
references Accounts

alter table Bills
add constraint fk_Category_Bill
foreign key (CategoryId)
references Categories

alter table Bills
add constraint fk_Payee_Bill
foreign key (PayeeId)
references Payees

ج) صفحه‌ی ثبت صورتحساب‌ها

صفحات ثبت گروه‌های اقلام، حساب‌ها و فروشنده‌ها، نکته‌ی خاصی ندارند. چون این جداول وابستگی خاصی به جایی نداشته و به سادگی اطلاعات آن‌ها را می‌توان ثبت یا به روز کرد.
صفحه‌ی مشکل در این مثال، همان صفحه‌ی ثبت صورتحساب‌ها است که از سه کلید خارجی به سه جدول دیگر تشکیل شده است.
عموما برای طراحی این نوع صفحات، کلیدهای خارجی را با drop down list نمایش می‌دهند و اگر در جهت سهولت کار کاربر قدم برداشته شود، باید از یک Auto complete drop down list استفاده کرد تا کاربر برنامه جهت یافتن آیتم‌های از پیش تعریف شده کمتر سختی بکشد.



اگر از Silverlight یا WPF استفاده شود، امکان بایند یک لیست کامل از اشیاء با تمام خواص مرتبط به آن‌ها وجود دارد (هر رکورد نمایش داده شده در دراپ داون لیست، دقیقا معادل است با یک شیء متناظر با کلاس‌های تعریف شده است). اگر از ASP.NET استفاده شود (یعنی یک محیط بدون حالت که پس از نمایش یک صفحه دیگر خبری از لیست اشیاء بایند شده وجود نخواهد داشت و همگی توسط وب سرور جهت صرفه جویی در منابع تخریب شده‌اند)، بهتر است datatextfield را با فیلد نام و datavaluefield را با فیلد Id مقدار دهی کرد تا کاربر نهایی، نام را جهت ثبت اطلاعات مشاهده کند و برنامه از Id موجود در لیست جهت ثبت کلیدهای خارجی استفاده نماید.
و نکته‌ی اصلی هم همینجا است که چگونه؟! چون ما زمانیکه با یک ORM سر و کار داریم، برای ثبت یک رکورد در جدول Bills باید یک وهله از کلاس Bill را ایجاد کرده و خواص آن‌را مقدار دهی کنیم. اگر به تعریف کلاس Bill مراجعه کنید، سه خاصیت آن از نوع سه کلاس مجزا تعریف شده است. به به عبارتی با داشتن فقط یک id از رکوردهای این کلاس‌ها باید بتوان سه وهله‌ی متناظر آن‌ها را از بانک اطلاعاتی خواند و سپس به این خواص انتساب داد:

var newBill = new Bill
{
Account = accountRepository.GetByKey(1),
Amount = 1,
BillDate = DateTime.Now,
Category = categoryRepository.GetByKey(1),
Description = "testestest...",
Payee = payeeRepository.GetByKey(1)
};
یعنی برای ثبت یک رکورد در جدول Bills فوق، چهار بار رفت و برگشت به دیتابیس خواهیم داشت:
- یکبار برای دریافت رکورد متناظر با گروه‌ها بر اساس کلید اصلی آن (که از دراپ داون لیست مربوطه دریافت می‌شود)
- یکبار برای دریافت رکورد متناظر با فروشند‌ه‌ها بر اساس کلید اصلی آن (که از دراپ داون لیست مربوطه دریافت می‌شود)
- یکبار برای دریافت رکورد متناظر با حساب‌ها بر اساس کلید اصلی آن (که از دراپ داون لیست مربوطه دریافت می‌شود)
- یکبار هم ثبت نهایی اطلاعات در بانک اطلاعاتی

متد GetByKey فوق همان متد session.Get استاندارد NHibernate است (چون به primary key ها از طریق drop down list دسترسی داریم، به سادگی می‌توان بر اساس متد Get استاندارد ذکر شده عمل کرد).

SQL نهایی تولیدی هم به صورت واضحی این مشکل را نمایش می‌دهد (4 بار رفت و برگشت؛ سه بار select یکبار هم insert نهایی):
SELECT account0_.AccountId as AccountId0_0_, account0_.Name as Name0_0_
FROM Accounts account0_ WHERE account0_.AccountId=@p0;@p0 = 1 [Type: Int32 (0)]

SELECT category0_.CategoryId as CategoryId2_0_, category0_.Name as Name2_0_
FROM Categories category0_ WHERE category0_.CategoryId=@p0;@p0 = 1 [Type: Int32 (0)]

SELECT payee0_.PayeeId as PayeeId3_0_, payee0_.Name as Name3_0_
FROM Payees payee0_ WHERE payee0_.PayeeId=@p0;@p0 = 1 [Type: Int32 (0)]

INSERT INTO Bills (Amount, BillDate, Description, AccountId, CategoryId, PayeeId)
VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
select SCOPE_IDENTITY();
@p0 = 1 [Type: Decimal (0)],
@p1 = 2010/12/27 11:48:33 ق.ظ [Type: DateTime (0)],
@p2 = 'testestest...' [Type: String (500)],
@p3 = 1 [Type: Int32 (0)],
@p4 = 1 [Type: Int32 (0)],
@p5 = 1 [Type: Int32 (0)]

کسانی که قبلا با رویه‌های ذخیره شده کار کرده باشند (stored procedures) احتمالا الان خواهند گفت؛ ما که گفتیم این روش کند است! سربار زیادی دارد! فقط کافی است یک SP بنویسید و کل عملیات را با یک رفت و برگشت انجام دهید.
اما در ORMs نیز برای انجام این مورد در طی یک حرکت یک ضرب راه حل‌هایی وجود دارد که در ادامه بحث خواهد شد:

د) پیاده سازی با NHibernate
برای حل این مشکل در NHibernate با داشتن primary key (برای مثال از طریق datavaluefield ذکر شده)، بجای session.Get از session.Load استفاده کنید.
session.Get یعنی همین الان برو به بانک اطلاعاتی مراجعه کن و رکورد متناظر با کلید اصلی ذکر شده را بازگشت بده و یک شیء از آن را ایجاد کن (حالت‌های دیگر دسترسی به اطلاعات مانند استفاده از LINQ یا Criteria API یا هر روش مشابه دیگری نیز در اینجا به همین معنا خواهد بود).
session.Load یعنی فعلا دست نگه دار! مگر در جدول نهایی نگاشت شده، اصلا چیزی به نام شیء مثلا گروه وجود دارد؟ مگر این مورد واقعا یک فیلد عددی در جدول Bills بیشتر نیست؟ ما هم که الان این عدد را داریم (به کمک عناصر دراپ داون لیست)، پس لطفا در پشت صحنه یک پروکسی برای ایجاد شیء مورد نظر ایجاد کن (uninitialized proxy to the entity) و سپس عملیات مرتبط را در حین تشکیل SQL نهایی بر اساس این عدد موجود انجام بده. یعنی نیازی به رفت و برگشت به بانک اطلاعاتی نیست. در این حالت اگر SQL نهایی را بررسی کنیم فقط یک سطر زیر خواهد بود (سه select ذکر شده حذف خواهند شد):
INSERT INTO Bills (Amount, BillDate, Description, AccountId, CategoryId, PayeeId)
VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
select SCOPE_IDENTITY();
@p0 = 1 [Type: Decimal (0)],
@p1 = 2010/12/27 11:58:22 ق.ظ [Type: DateTime (0)],
@p2 = 'testestest...' [Type: String (500)],
@p3 = 1 [Type: Int32 (0)],
@p4 = 1 [Type: Int32 (0)],
@p5 = 1 [Type: Int32 (0)]

ه) پیاده سازی با Entity framework

Entity framework زمانیکه بانک اطلاعاتی فوق را (به روش database first) به کلاس‌های متناظر تبدیل/نگاشت می‌کند، حاصل نهایی مثلا در مورد کلاس Bill به صورت خلاصه به شکل زیر خواهد بود:
public partial class Bill : EntityObject
{
public global::System.Int32 BillId {set;get;}
public global::System.Decimal Amount {set;get;}
public global::System.DateTime BillDate {set;get;}
public global::System.String Description {set;get;}
public global::System.Int32 AccountId {set;get;}
public global::System.Int32 CategoryId {set;get;}
public global::System.Int32 PayeeId {set;get;}
public Account Account {set;get;}
public Category Category {set;get;}
}
به عبارتی فیلدهای کلیدهای خارجی، در تعریف نهایی این کلاس هم مشاهده می‌شوند. در اینجا فقط کافی است سه کلید خارجی، از نوع int مقدار دهی شوند (و نیازی به مقدار دهی سه شیء متناظر نیست). در این حالت نیز برای ثبت اطلاعات، فقط یکبار رفت و برگشت به بانک اطلاعاتی خواهیم داشت.

اشتراک‌ها
از Elasticsearch ها بدون تنظیمات امنیتی استفاده نکنید

«... در راستای برنامه از قبل اعلام شده مرکز ماهر جهت رصد بانک‌های اطلاعاتی، نخستین گزارش این رصدها شامل ۳۶ پایگاه داده Elasticsearch فاقد احراز هویت شناسایی شده و مشخصات و اطلاعات آنها تحلیل شده است... »

از Elasticsearch ها بدون تنظیمات امنیتی استفاده نکنید
اشتراک‌ها
نوشتن برنامه جهت پایش سخت افزار سیستم

با استفاده از این کتابخانه به راحتی می‌توان اطلاعات سخت افزاری زیر را به دست آورد

جزئیات بایوس 

به دست آوردن مدل و مشخصات cpu  و RAM

درجه حرارت cpu

وضعیت فن ها

مدل کارت گرافیک

.

.

.

نوشتن برنامه جهت پایش سخت افزار سیستم
نظرات مطالب
سفارشی سازی ASP.NET Core Identity - قسمت پنجم - سیاست‌های دسترسی پویا
بله. البته باز هم یک کوکی بسیار کوچک و چند بایتی، حاوی مشخصات ID رمزنگاری شده‌ی آن تولید می‌شود که هدف از آن، بازیابی اطلاعات اصلی این کوکی از بانک اطلاعاتی و استفاده‌ی خودکار از آن در برنامه است .