It is common when working on a web application, comprised of a server-side Web API, running on a framework like ASP.NET or NestJS, and a client-side Single Page Application (SPA), running on a framework like Angular, to refer to the server-side as "the back-end" and to the client-side as "the front-end". I've been a culprit of this until recently.
کمپین ضد IF !
لیست کاملش http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx
بکارگیری Microsoft Report در Mvc
Performance of your ASP.NET web application is important. There is a lot of evidence to suggest that slow loading times and clunky interaction, will drive customers elsewhere. Even in the case of internal applications where the users have no option but to use the application, their satisfaction is tightly coupled to speed.
There are a ton of ways to improve the performance of a website, let's look at fifteen of them.
پیشنیازهای کار با EF Core Migrations
در قسمت قبل در حین بررسی «برپایی تنظیمات اولیهی EF Core 1.0 در یک برنامهی ASP.NET Core 1.0»، چهار مدخل جدید را به فایل project.json برنامه اضافه کردیم. مدخل جدید Microsoft.EntityFrameworkCore.Tools که به قسمت tools آن اضافه شد، پیشنیاز اصلی کار با EF Core Migrations است.
بررسی ابزارهای خط فرمان EF Core و تشکیل ساختار بانک اطلاعاتی بر اساس کلاسهای برنامه
پس از تکمیل پیشنیازهای کار با EF Core، از طریق خط فرمان به پوشهی جاری پروژه وارد شده و دستور dotnet ef را صادر کنید.
یک نکته: در ویندوز اگر در پوشهای، کلید shift را نگه دارید و در آن پوشه کلیک راست کنید، در منوی باز شده، گزینهی جدیدی را به نام Open command window here مشاهده خواهید کرد که میتواند به سرعت خط فرمان را از پوشهی جاری شروع کند.
خروجی صدور فرمان dotnet ef را در ذیل مشاهده میکنید:
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef _/\__ ---==/ \\ ___ ___ |. \|\ | __|| __| | ) \\\ | _| | _| \_/ | //|\\ |___||_| / \\\/\\ Entity Framework .NET Core CLI Commands 1.0.0-preview2-21431 Usage: dotnet ef [options] [command] Options: -h|--help Show help information -v|--verbose Enable verbose output --version Show version information --assembly <ASSEMBLY> The assembly file to load. --startup-assembly <ASSEMBLY> The assembly file containing the startup class. --data-dir <DIR> The folder used as the data directory (defaults to current working directory). --project-dir <DIR> The folder used as the project directory (defaults to current working directory). --content-root-path <DIR> The folder used as the content root path for the application (defaults to application base directory). --root-namespace <NAMESPACE> The root namespace of the target project (defaults to the project assembly name). Commands: database Commands to manage your database dbcontext Commands to manage your DbContext types migrations Commands to manage your migrations Use "dotnet ef [command] --help" for more information about a command.
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef migrations add InitialDatabase
نام دلخواه InitialDatabase را در انتهای نام فایل 13950526050417_InitialDatabase مشاهده میکنید.
اگر قصد حذف این مرحله را داشته باشیم، میتوان دستور dotnet ef migrations remove را مجددا صادر کرد.
فایل 13950526050417_InitialDatabase به همراه کلاسی است که در آن دو متد Up و Down قابل مشاهده هستند. متد Up نحوهی ایجاد جدول جدیدی را از کلاس Person بیان میکند و متد Down نحوهی Drop این جدول را پیاده سازی کردهاست.
فایل ApplicationDbContextModelSnapshot.cs دارای کلاسی است که خلاصهای از تعاریف موجودیتهای ذکر شدهی در DB Context برنامه را به همراه دارد و تفسیر آنها را از دیدگاه EF در اینجا میتوان مشاهده کرد.
پس از مرحلهی افزودن migrations، نوبت به اعمال آن به بانک اطلاعاتی است. تا اینجا EF تنها متدهای Up و Down مربوط به ساخت و حذف ساختار جداول را ایجاد کردهاست. اما هنوز آنها را به بانک اطلاعاتی برنامه اعمال نکردهاست. برای اینکار در پوشهی جاری دستور ذیل را صادر کنید:
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef database update Applying migration '13950526050417_InitialDatabase'. Done.
اکنون اگر به لیست بانکهای اطلاعاتی مراجعه کنیم، بانک اطلاعاتی جدید TestDbCore2016 را به همراه جدول متناظر کلاس Person میتوان مشاهده کرد:
در اینجا جدول دیگری به نام __EFMigrationsHistory نیز قابل مشاهدهاست که کار آن ذخیره سازی وضعیت فعلی Migrations در بانک اطلاعاتی، جهت مقایسههای آتی است. این جدول صرفا توسط ابزارهای EF استفاده میشود و نباید به صورت مستقیم تغییری در آن ایجاد کنید.
مقدار دهی اولیهی جداول بانکهای اطلاعاتی در EF Core
در همین حالت اگر کنترلر TestDB مطرح شدهی در انتهای بحث قسمت قبل را اجرا کنیم، به این استثناء خواهیم رسید:
این تصویر بدین معنا است که کار Migrations موفقیت آمیز بودهاست و اینبار امکان اتصال و کار با بانک اطلاعاتی وجود دارد، اما این جدول حاوی اطلاعات اولیهای برای نمایش نیست.
در نگارش قبلی EF Code First، امکانات Migrations به همراه یک متد Seed نیز بود که توسط آن کار مقدار دهی اولیهی جداول را میتوان انجام داد (زمانیکه جدولی ایجاد میشود، در همان هنگام، چند رکورد خاص نیز به آن اضافه شوند. برای مثال به جدول کاربران، رکورد اولین کاربر یا همان Admin اضافه شود). این متد در EF Core 1.0 وجود ندارد.
برای این منظور کلاس جدیدی را به نام ApplicationDbContextSeedData به همان پوشهی جدید Migrations اضافه کنید؛ با این محتوا:
using System.Collections.Generic; using System.Linq; using Core1RtmEmptyTest.Entities; using Microsoft.Extensions.DependencyInjection; namespace Core1RtmEmptyTest.Migrations { public static class ApplicationDbContextSeedData { public static void SeedData(this IServiceScopeFactory scopeFactory) { using (var serviceScope = scopeFactory.CreateScope()) { var context = serviceScope.ServiceProvider.GetService<ApplicationDbContext>(); if (!context.Persons.Any()) { var persons = new List<Person> { new Person { FirstName = "Admin", LastName = "User" } }; context.AddRange(persons); context.SaveChanges(); } } } } }
public void Configure(IServiceScopeFactory scopeFactory) { scopeFactory.SeedData();
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(ServiceLifetime.Scoped);
- برای پیاده سازی الگوی واحد کار، اولین قدم، مشخص سازی طول عمر Db Context برنامه است. برای اینکه تنها یک Context در طول یک درخواست وهله سازی شود، نیاز است به نحو صریحی طول عمر آنرا به حالت Scoped تنظیم کرد. متد AddDbContext دارای پارامتری است که این طول عمر را دریافت میکند. بنابراین در اینجا ServiceLifetime.Scoped ذکر شدهاست. همچنین در این مثال از نمونهای که IConfigurationRoot به سازندهی کلاس ApplicationDbContext تزریق شده (نکتهی انتهای بحث قسمت قبل)، استفاده شدهاست. به همین جهت تنظیمات options آنرا ملاحظه نمیکنید.
- مرحلهی بعد نحوهی دسترسی به این سرویس ثبت شده در یک کلاس static دارای متدی الحاقی است. در اینجا دیگر دسترسی مستقیمی به تزریق وابستگیها نداریم و باید کار را با IServiceScopeFactory شروع کنیم. در اینجا میتوانیم به صورت دستی یک Scope را ایجاد کرده، سپس توسط ServiceProvider آن، به سرویس ApplicationDbContext دسترسی پیدا کنیم و در ادامه از آن به نحو متداولی استفاده نمائیم. IServiceScopeFactory جزو سرویسهای توکار ASP.NET Core است و در صورت ذکر آن به عنوان پارامتر جدیدی در متد Configure، به صورت خودکار وهله سازی شده و در اختیار ما قرار میگیرد.
- نکتهی مهمی که در اینجا بکار رفتهاست، ایجاد Scope و dispose خودکار آن توسط عبارت using است. باید دقت داشت که ایجاد Scope و تخریب آن به صورت خودکار در ابتدا و انتهای درخواستها توسط ASP.NET Core انجام میشود. اما چون شروع کار ما از متد Configure است، در اینجا خارج از Scope قرار داریم و باید مدیریت ایجاد و تخریب آنرا به صورت دستی انجام دهیم که نمونهای از آنرا در متد SeedData کلاس ApplicationDbContextSeedData ملاحظه میکنید. در اینجا Scope ایی ایجاد شدهاست. سپس دادههای اولیهی مدنظر به بانک اطلاعاتی اضافه گردیده و در آخر این Scope تخریب شدهاست.
- اگر کار ایجاد و تخریب scope، به نحوی که مشخص شدهاست انجام نگیرد، طول عمر درخواستی خارج از Scope، همواره Singleton خواهد بود. چون خارج از طول عمر درخواست جاری قرار داریم و هنوز کار به سرویس دهی درخواستها نرسیدهاست. بنابراین مدیریت Scopeها هنوز شروع نشدهاست و باید به صورت دستی انجام شود.
در این حالت اگر برنامه را اجرا کنیم، این خروجی قابل مشاهده است:
که به معنای کار کردن متد SeedData و ثبت اطلاعات اولیهای در بانک اطلاعاتی است.
اعمال تغییرات به مدلهای برنامه و به روز رسانی ساختار بانک اطلاعاتی
فرض کنید به کلاس Person قسمت قبل، خاصیت Age را هم اضافه کردهایم:
namespace Core1RtmEmptyTest.Entities { public class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } } }
An unhandled exception occurred while processing the request. SqlException: Invalid column name 'Age'.
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef migrations add v2 D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef database update
با اجرا این دستورات، فایل جدید 13950526073248_v2 به پوشهی Migrations اضافه میشود. این فایل حاوی نحوهی به روز رسانی بانک اطلاعاتی، بر اساس خاصیت جدید Age است. سپس با اجرای دستور dotnet ef database update، کار به روز رسانی بانک اطلاعاتی بر اساس مرحلهی v2 انجام میشود.
بنابراین هر بار که تغییری را در مدلهای خود ایجاد میکنید، یکبار باید کلاس مهاجرت آنرا ایجاد کنید و سپس آنرا به بانک اطلاعاتی اعمال نمائید.
تهیه اسکریپت تغییرات بجای اعمال تغییرات توسط ابزارهای EF
شاید علاقمند باشید که پیش از اعمال تغییرات به بانک اطلاعاتی، یک اسکریپت SQL از آن تهیه کنید (جهت مطالعه و یا اعمال دستی آن توسط خودتان). برای اینکار میتوانید دستور ذیل را در پوشهی جاری پروژه اجرا کنید:
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef migrations script -o v2.sql
تغییرات ساختار جدول __EFMigrationsHistory در EF Core 1.0
در EF 6.x، ساختار اطلاعات جدول نگهداری تاریخچهی تغییرات، بسیار پیچیده بود و شامل رشتهای gzip شدهی حاوی یک snapshot از کل ساختار دیتابیس در هر مرحلهی migration بود. در این نگارش، این snapshot حذف شدهاست و بجای آن فایل ApplicationDbContextModelSnapshot.cs را مشاهده میکنید (تنها یک snapshot به ازای کل context برنامه). همچنین در اینجا کاملا مشخص است که چه مراحلی به بانک اطلاعاتی اعمال شدهاند و دیگر خبری از رشتهی gzip شدهی قبلی نیست (تصویر فوق).
در شکل زیر ساختار قبلی این جدول را در EF 6.x مشاهده میکنید. در EF 6.x حتی فضای نام کلاسهای موجودیتهای برنامه هم مهم هستند و در صورت تغییر، مشکل ایجاد میشود:
مهاجرت خودکار از EF Core حذف شدهاست
در EF 6.x در کنار کلاس Db Context یک کلاس Configuration هم وجود داشت که برای مثال امکان چنین تعریفی در آن میسر هست:
public Configuration() { AutomaticMigrationsEnabled = true; }
با حذف مهاجرت خودکار:
- دیگر نیازی نیست تا model snapshots در بانک اطلاعاتی ذخیره شوند (همان ساده شدن ساختار جدول ذخیره سازی تاریخچهی مهاجرتهای فوق).
- در حالت افزودن یک مرحلهی مهاجرت، دیگر نیازی به کوئری گرفتن از بانک اطلاعاتی نخواهد بود (سرعت بیشتر).
- میتوان چندین مرحلهی مهاجرت را افزود بدون اینکه الزاما مجبور به اعمال آنها به بانک اطلاعاتی باشیم.
- کاهش کدهای مدیریت ساختار بانک اطلاعاتی.
- تیمها برای یکی کردن تغییرات خود مشکلی نخواهند داشت چون دیگر snapshot مدلها در جدول __EFMigrationsHistory ذخیره نمیشود.
بنابراین در EF Core میتوان مهاجرت v1 را اضافه کرد. سپس تغییراتی را در کدها اعمال کرد. در ادامه مهاجرت v2 را تولید کرد و در آخر کار اعمال یکجای اینها را به بانک اطلاعاتی انجام داد.
هرچند در اینجا اگر میخواهید مرحلهی اجرای دستور dotnet ef database update را حذف کنید، میتوانید از کدهای ذیل نیز استفاده نمائید:
using Core1RtmEmptyTest.Entities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace Core1RtmEmptyTest.Migrations { public static class DbInitialization { public static void Initialize(this IServiceScopeFactory scopeFactory) { using (var serviceScope = scopeFactory.CreateScope()) { var context = serviceScope.ServiceProvider.GetService<ApplicationDbContext>(); // Applies any pending migrations for the context to the database. // Will create the database if it does not already exist. context.Database.Migrate(); } } } }
کار متد Migrate، ایجاد بانک اطلاعاتی در صورت عدم وجود و سپس اعمال تمام مراحل migration ایی است که در جدول __EFMigrationsHistory ثبت نشدهاند (دقیقا همان کار دستور dotnet ef database update را انجام میدهد).
تفاوت متد Database.EnsureCreated با متد Database.Migrate
اگر به متدهای context.Database دقت کنید، یکی از آنها EnsureCreated نام دارد. این متد نیز سبب تولید بانک اطلاعاتی بر اساس ساختار Context برنامه میشود. اما هدف آن صرفا استفادهی از آن در آزمونهای واحد سریع است. از این جهت که جدول __EFMigrationsHistory را تولید نمیکند (برخلاف متد Migrate). بنابراین بجز آزمونهای واحد، در جای دیگری از آن استفاده نکنید چون به دلیل عدم تولید جدول __EFMigrationsHistory توسط آن، قابلیت استفادهی از بانک اطلاعاتی تولید شدهی توسط آن با امکانات migrations وجود ندارد. در پایان آزمون واحد نیز میتوان از متد EnsureDeleted برای حذف این بانک اطلاعاتی موقتی استفاده کرد.
در قسمت بعد، مطالب تکمیلی مهاجرتها را بررسی خواهیم کرد. برای مثال چگونه میتوان کلاسهای موجودیتها را به اسمبلیهای دیگری منتقل کرد.
ایجاد کامپوننت جدید BlazorJS
برای بررسی نحوهی تعامل جاوا اسکریپت و Blazor، در ابتدا کامپوننت جدید Pages\LearnBlazor\BlazorJS.razor را ایجاد کرده:
@page "/BlazorJS" <h3>BlazorJS</h3> @code { }
<li class="nav-item px-3"> <NavLink class="nav-link" href="BlazorJS"> <span class="oi oi-list-rich" aria-hidden="true"></span> BlazorJS </NavLink> </li>
روش فراخوانی کدهای جاوا اسکریپتی از طریق کدهای سیشارپ Blazor
فرض کنید میخواهیم در حین کلیک بر روی دکمهای مانند دکمهی حذف، ابتدا تائیدیهای را توسط تابع confirm جاوا اسکریپتی، از کاربر اخذ کنیم. روش انجام چنین کاری در برنامههای مبتنی بر Blazor به صورت زیر است:
@page "/BlazorJS" @inject IJSRuntime JsRuntime <h3>BlazorJS</h3> <div class="row"> <button class="btn btn-secondary" @onclick="TestConfirmBox">Test Confirm Button</button> </div> <div class="row"> @if (ConfirmResult) { <p>Confirmation has been made!</p> } else { <p>Confirmation Pending!</p> } </div> @code { string ConfirmMessage = "Are you sure you want to click?"; bool ConfirmResult; async Task TestConfirmBox() { ConfirmResult = await JsRuntime.InvokeAsync<bool>("confirm", ConfirmMessage); } }
- در اینجا میخواهیم تابع استاندارد confirm جاوا اسکریپتی را از طریق کدهای سیشارپ، با کلیک بر روی دکمهی Test Confirm Button، فراخوانی کنیم. به همین جهت onclick@ این دکمه، به متد TestConfirmBox کدهای UI سیشارپ این کامپوننت، متصل شدهاست.
- برای دسترسی به توابع جاوا اسکریپتی، نیاز است سرویس توکار IJSRuntime را به کدهای کامپوننت تزریق کنیم که روش انجام آنرا توسط دایرکتیو inject@ مشاهده میکنید. برای دسترسی به این سرویس توکار، نیاز به تنظیمات ابتدایی خاصی نیست و اینکار پیشتر انجام شدهاست.
- سرویس JsRuntime تزریق شده، دو متد مهم InvokeVoidAsync و InvokeAsync را جهت فراخوانی توابع جاوا اسکریپتی به همراه دارد. اگر تابعی، خروجی غیر void داشته باشد، باید از متد InvokeAsync استفاده کرد. برای مثال خروجی تابع استاندارد confirm، از نوع boolean است. بنابراین نوع این خروجی را به صورت یک آرگومان جنریک متد InvokeAsync مشخص کردهایم.
- اولین پارامتر متد InvokeAsync، نام رشتهای تابع جاوا اسکریپتی است که قرار است صدا زده شود. پارامترهای اختیاری بعدی که به صورت params object?[]? args تعریف شدهاند، لیست نامحدود آرگومانهای ورودی این متد هستند.
- فیلد ConfirmMessage، پیامی را جهت اخذ تائید، تعریف میکند که به عنوان پارامتر متد confirm، توسط JsRuntime.InvokeAsync فراخوانی خواهد شد.
- فیلد ConfirmResult، نتیجهی فراخوانی متد confirm جاوا اسکریپتی را به همراه دارد.
- در اینجا روش عکس العمل نشان دادن به خروجی دریافتی از متد جاوااسکریپتی را نیز مشاهده میکنید. پس از پایان متد TestConfirmBox که یک متد رویدادگران است، همانطور که در مطلب بررسی «چرخهی حیات کامپوننتها» نیز بررسی کردیم، متد StateHasChanged، در پشت صحنه فراخوانی میشود که سبب رندر مجدد UI خواهد شد. بنابراین در حین رندر مجدد UI، بر اساس مقدار جدید ConfirmResult دریافت شدهی از کاربر، با تشکیل یک if/else@، میتوان به نتیجهی تائید یا عدم تائید کاربر، واکنش نشان داد. با این توضیحات در اولین بار نمایش کامپوننت جاری چون مقدار ConfirmResult مساوی false است، پیام زیر را مشاهده میکنیم:
اما در ادامه با کلیک بر روی دکمه و تائید پیام ظاهر شده، عبارت زیر ظاهر میشود:
روش افزودن یک کتابخانهی خارجی جاوا اسکریپتی به پروژههای Blazor
فرض کنید میخواهیم پیامهای برنامه را توسط کتابخانهی معروف جاوا اسکریپتی Toastr نمایش دهیم؛ با این دمو.
مرحلهی اول کار با این کتابخانه، دریافت فایلهای CSS و JS آن است. برای این منظور قصد داریم از برنامهی مدیریت بستههای LibMan استفاده کنیم:
dotnet tool install -g Microsoft.Web.LibraryManager.Cli libman init libman install bootstrap --provider unpkg --destination wwwroot/lib/bootstrap libman install jquery --provider unpkg --destination wwwroot/lib/jquery libman install toastr --provider unpkg --destination wwwroot/lib/toastr
البته تعاریف مداخل آنها به فایل libman.json نیز اضافه میشوند. مزیت آن، اجرای دستور libman restore برای بازیابی و نصب مجدد تمام بستههای ذکر شدهی در فایل libman.json است.
پس از دریافت بستههای سمت کلاینت آن، مداخل مرتبط را به فایل Pages\_Host.cshtml برنامهی Blazor Server اضافه خواهیم کرد (و یا در فایل wwwroot/index.html برنامههای Blazor WASM).
<head> <base href="~/" /> <link rel="stylesheet" href="lib/toastr/build/toastr.min.css" /> </head> <body> <script src="lib/jquery/dist/jquery.min.js"></script> <script src="lib/toastr/build/toastr.min.js"></script> <script src="_framework/blazor.server.js"></script> </body>
یک نکته: میتوان فایل csproj برنامه را به صورت زیر تغییر داد تا کار اجرای دستور libman restore را قبل از build، به صورت خودکار انجام دهد:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <Target Name="DebugEnsureLibManEnv" BeforeTargets="BeforeBuild" Condition=" '$(Configuration)' == 'Debug' "> <!-- Ensure libman is installed --> <Exec Command="libman --version" ContinueOnError="true"> <Output TaskParameter="ExitCode" PropertyName="ErrorCode" /> </Exec> <Error Condition="'$(ErrorCode)' != '0'" Text="libman is required to build and run this project. To continue, please run `dotnet tool install -g Microsoft.Web.LibraryManager.Cli`, and then restart your command prompt or IDE." /> <Message Importance="high" Text="Restoring dependencies using 'libman'. This may take several minutes..." /> <Exec WorkingDirectory="$(MSBuildProjectDirectory)" Command="libman restore" /> </Target> </Project>
روش فراخوانی یک کتابخانهی خارجی جاوا اسکریپتی در پروژههای Blazor
پس از افزودن فایلهای سمت کلاینت toastr و تعریف مداخل آن در فایل Pages\_Host.cshtml برنامهی Blazor Server جاری، اکنون میخواهیم از این کتابخانه استفاده کنیم. یک روش کار با این نوع کتابخانههای عمومی و سراسری به صورت زیر است:
- ابتدا فایل خالی جدید wwwroot\js\common.js را ایجاد میکنیم.
- سپس تابع عمومی و سراسری ShowToastr را بر اساس امکانات کتابخانهی toastr و مستندات آن، به صورت زیر ایجاد میکنیم:
window.ShowToastr = (type, message) => { // Toastr don't work with Bootstrap 4.2 toastr.options.toastClass = "toastr"; // https://github.com/CodeSeven/toastr/issues/599 if (type === "success") { toastr.success(message, "Operation Successful", { timeOut: 20000 }); } if (type === "error") { toastr.error(message, "Operation Failed", { timeOut: 20000 }); } };
سطر اول آن هم برای رفع عدم تداخل با بوت استرپ 4x اضافه شدهاست. بوت استرپ 4x به همراه کلاسهای CSS مشابهی است که نیاز است با تنظیم toastClass به مقداری دیگر، این تداخل را برطرف کرد.
- در ادامه مدخل تعریف فایل wwwroot\js\common.js را به انتهای تگ body فایل Pages\_Host.cshtml اضافه میکنیم:
<script src="lib/jquery/dist/jquery.min.js"></script> <script src="lib/toastr/build/toastr.min.js"></script> <script src="js/common.js"></script> <script src="_framework/blazor.server.js"></script> </body>
در آخر برای آزمایش آن به کامپوننت Pages\LearnBlazor\BlazorJS.razor مراجعه کرده و تابع سراسری ShowToastr را دقیقا مانند روشی که در مورد تابع confirm بکار بردیم، توسط سرویس JsRuntime، فراخوانی میکنیم:
@page "/BlazorJS" @inject IJSRuntime JsRuntime <div class="row"> <button class="btn btn-success" @onclick="@(()=>TestSuccess("Success Message"))">Test Toastr Success</button> <button class="btn btn-danger" @onclick="@(()=>TestFailure("Error Message"))">Test Toastr Failure</button> </div> @code { async Task TestSuccess(string message) { await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message); } async Task TestFailure(string message) { await JsRuntime.InvokeVoidAsync("ShowToastr", "error", message); } }
کاهش کدهای تکراری فراخوانی متدهای جاوا اسکریپتی با تعریف متدهای الحاقی
میتوان جهت کاهش تکرار کدهای استفاده از تابع ShowToastr، متدهای الحاقی زیر را برای سرویس IJSRuntime تهیه کرد:
using System.Threading.Tasks; using Microsoft.JSInterop; namespace BlazorServerSample.Utils { public static class JSRuntimeExtensions { public static ValueTask ToastrSuccess(this IJSRuntime JSRuntime, string message) { return JSRuntime.InvokeVoidAsync("ShowToastr", "success", message); } public static ValueTask ToastrError(this IJSRuntime JSRuntime, string message) { return JSRuntime.InvokeVoidAsync("ShowToastr", "error", message); } } }
@using BlazorServerSample.Utils
async Task TestSuccess(string message) { //await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message); await JsRuntime.ToastrSuccess(message); }
فراخوانی یک متد عمومی واقع در کامپوننت فرزند از طریق کامپوننت والد
فرض کنید در کامپوننت فرزند Pages\LearnBlazor\LearnBlazorComponents\ChildComponent.razor که در قسمتهای قبل آنرا تکمیل کردیم، متد عمومی زیر تعریف شدهاست:
@inject IJSRuntime JsRuntime @code { // ... public async Task TestSuccess(string message) { await JsRuntime.ToastrSuccess(message); } }
@page "/ParentComponent" <ChildComponent OnClickBtnMethod="ShowMessage" @ref="ChildComp" Title="This title is passed as a parameter from the Parent Component"> <ChildContent> A `Render Fragment` from the parent! </ChildContent> <DangerChildContent> A danger content from the parent! </DangerChildContent> </ChildComponent> <div class="row"> <button class="btn btn-success" @onclick="@(()=>ChildComp.TestSuccess("Done!"))">Show Alert</button> </div> @code { ChildComponent ChildComp; // ... }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-11.zip
LocalDB FAQ
اگر به تصویر فوق دقت کنید، یک child process جدید به نام sqlservr.exe نیز به همراه برنامهی آزمایشی ما به صورت خودکار اجرا شدهاست. این child process به همراه پارامترهای ذیل است (که توسط NET Framework. مقدار دهی میشوند و مدیریت نهایی آن خودکار است):
"C:\Program Files\Microsoft SQL Server\120\LocalDB\Binn\\sqlservr.exe" -c -SMSSQL12E.LOCALDB -sLOCALDB#5657074F -d"C:\Users\Vahid\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\MSSQLLocalDB\master.mdf" -l"C:\Users\Vahid\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\MSSQLLocalDB\mastlog.ldf" -e"C:\Users\Vahid\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\MSSQLLocalDB\error.log"
اما LocalDB یک بانک اطلاعاتی user-mode است و در پروفایل کاربر جاری سیستم اجرا میشود. این بانک اطلاعاتی یک بار بر روی سیستم نصب میشود و در هر برنامهای که از آن استفاده میکنید، یک child process مجزای خاص خودش را (sqlservr.exe) اجرا خواهد کرد. اجرا و خاتمهی این child processها نیز خودکار هستند و نیازی به دخالت مستقیم برنامه ندارند.
البته به نظر توسعهی SQL Server CE متوقف شدهاست و دیگر پشتیبانی نمیشود. بنابراین گزینهی ترجیح داده شدهی برای کارهایی با حجمهای بانک اطلاعاتی زیر 10 گیگابایت ، میتواند LocalDB باشد. به علاوه اینکه قابلیتهای T-SQL بیشتری را نیز پشتیبانی میکند و همچنین پشتیبانی منظمی نیز از آن وجود دارد. برای مثال پیش نمایش نگارش 2016 آن نیز موجود است.
در ادامه، یک سری پرسش و پاسخ متداول جهت کار با LocalDB را مرور خواهیم کرد.
محل دریافت آخرین نگارش مستقل آن کجاست؟
همانطور که عنوان شد، یکی از مهمترین اهداف LocalDB، سهولت توزیع آن است و عدم نیاز به یک Admin سیستم، برای نصب و نگهداری آن. نگارش 2014 SP1 آنرا از آدرس ذیل میتوانید دریافت کنید:
https://www.microsoft.com/en-us/download/details.aspx?id=46697
در اینجا نسخههای متعددی وجود دارند. برای مثال اگر سیستم شما 64 بیتی است، تنها نیاز است ENU\x64\SqlLocalDB.msi را دریافت و نصب کنید:
پارامترهای نصب خاموش آن برای توزیع سادهی برنامه کدامند؟
اگر میخواهید نصاب LocalDB را به همراه setup برنامهی خود توزیع کنید، میتوانید روش توزیع خاموش را با ذکر پارامترهای ذیل، مورد استفاده قرار دهید:
msiexec /i SqlLocalDB.msi /qn IACCEPTSQLLOCALDBLICENSETERMS=YES
رشتهی اتصالی مخصوص آن کدام است؟
<connectionStrings> <add name="Sample35Context" connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\test.mdf;Integrated Security=True;" providerName="System.Data.SqlClient" /> </connectionStrings>
الف) ذکر وهلهی مدنظر
در اینجا وهلهی MSSQLLocalDB ذکر شدهاست؛ اما چه وهلههایی بر روی سیستم نصب هستند و چطور میتوان وهلهی دیگری را ایجاد کرد؟ برای این منظور، به پارامترهای sqlservr.exe ابتدای بحث دقت کنید. اکثر آنها به پوشهی ذیل اشاره میکنند:
C:\Users\your_user_name_here\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances
در این پوشه، وهلههای موجود و نصب شدهی بر روی سیستم شما نمایش داده میشوند که یکی از آنها را میتوانید در رشتهی اتصالی فوق ذکر کنید.
به علاوه، این لیست را توسط برنامهی کمکی SqlLocalDB.exe، به همراه پارامتر info یا i نیز میتوانید دریافت و بررسی کنید:
برنامهی کمکی SqlLocalDB.exe به همراه نصاب LocalDB، نصب میشود و توسط آن میتوان نگارشهای مختلف نصب شدهرا با پارامتر v و وهلهی مختلف موجود را با پارامتر i مشاهده کرد.
همچنین اگر میخواهید وهلهی جدیدی را بجز وهلهی پیش فرض MSSQLLocalDB ایجاد کنید، میتوانید از پارامتر create آن به نحو ذیل استفاده نمائید:
For LocalDB SQL EXPRESS 2014 "C:\Program Files\Microsoft SQL Server\120\Tools\Binn\SqlLocalDB.exe" create "v12.0" 12.0 -s For LocalDB SQL Express 2012 "C:\Program Files\Microsoft SQL Server\110\Tools\Binn\SqlLocalDB.exe" create "v11.0" 11.0 -s
ب) ذکر DataDirectory
در رشتهی اتصالی فوق، پارامتر DataDirectory نیز ذکر شدهاست تا بتوان مسیر بانک اطلاعاتی را به صورت نسبی و بدون ذکر عبارت دقیق آن که ممکن است در سیستمهای دیگر متفاوت باشد، پردازش کرد. این پارامتر در برنامههای وب به پوشهی استاندارد app_data اشاره میکند و نیازی به تنظیم اضافهتری ندارد. اما در برنامههای دسکتاپ باید به نحو ذیل به صورت دستی، در آغاز برنامه مقدار دهی شود:
AppDomain.CurrentDomain.SetData("DataDirectory", AppDomain.CurrentDomain.BaseDirectory);
AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "db"));
محل نصب بانکهای اطلاعاتی پیش فرض آن کدام است؟
ذکر AttachDbFilename در رشتهی اتصالی فوق، اختیاری است. در صورت عدم ذکر آن، بانک اطلاعاتی ایجاد شده را در یکی از مسیرهای ذیل میتوانید جستجو کنید:
C:\Users\USERNAME\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances C:\Users\USERNAME\AppData\Local\Microsoft\VisualStudio\SSDT
آیا میتوان فایلهای mdf و ldf آنرا به نگارش کامل SQL Server متصل (attach) کرد؟
بله. اما باید دقت داشته باشید که SQL Server به محض اتصال یک بانک اطلاعاتی با نگارش پایینتر به آن، ابتدا شماره نگارش آنرا به روز میکند. یعنی دیگر نخواهید توانست این بانک اطلاعاتی را با نگارش پایینتر LocalDB باز کنید و یک چنین پیام خطایی را دریافت خواهید کرد:
The database xyz cannot be opened because it is version 706. This server supports version 663 and earlier. A downgrade path is not supported.
چگونه محتوای بانکهای اطلاعاتی LocalDB را با VS.NET مشاهده کنیم؟
از منوی view گزینهی server explorer را انتخاب کنید. بر روی data connections کلیک راست کرده و گزینهی Add connection را انتخاب کنید.
در صفحهی باز شده، گزینهی Microsoft SQL server را انتخاب کنید. در صفحهی بعد، ذکر server name مطابق data source رشتهی اتصالی بحث شده و سپس انتخاب گزینهی attach a database file کفایت میکند:
پس از کلیک بر روی ok، امکان کار با اجزای این بانک اطلاعاتی را خواهید داشت:
چگونه از LocalDB با EF استفاده کنیم؟
EF 6.x به صورت پیش فرض از بانک اطلاعاتی LocalDB استفاده میکند و تنها داشتن یک چنین تنظیمی در فایل کانفیگ برنامه، برای کار با آن کافی است:
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <connectionStrings> <add name="Sample35Context" connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\test.mdf;Integrated Security=True;" providerName="System.Data.SqlClient" /> </connectionStrings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="mssqllocaldb" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> </configuration>
البته باید دقت داشت که اسمبلی EntityFramework.SqlServer نیز به صورت خودکار به همراه بستهی نیوگت EF 6.x به برنامه اضافه میشود که استفادهی از connection factory ذکر شده را میسر میکند.
استفادهی از LocalDB به همراه برنامههای وب چگونه است؟
سه نکته را باید در حین استفادهی از LocalDB، در برنامههای وب اجرا شدهی بر روی IIS مدنظر داشت:
الف) LocalDB یک بانک اطلاعاتی user-mode است و child process آن تحت مجوز اکانت تنظیم شدهی برای آن کار میکند.
ب) همانطور که عنوان شد، در رشتهی اتصالی ذکر شده، پارامتر DataDirectory به پوشهی استاندارد app_data اشاره میکند که فایلهای قرار گرفتهی در آن توسط IIS محافظت میشوند و از طریق وب قابل دسترسی و دانلود نیستند.
ج) child process مربوط به LocalDB، نیاز به دسترسی write، برای کار با فایلهای mdf و ldf خود دارد.
برای مورد الف نیاز است تا به تنظیمات application pool برنامه مراجعه کرده و سپس بر روی آن کلیک راست کرد و گزینهی advanced settings را انتخاب نمود. در اینجا گزینهی load user profile باید true باشد:
تنظیم load user profile ضروری است اما کافی نیست. پس از آن باید setProfileEnvironment را نیز به true تنظیم کرد. تنظیم این مورد در کنسول مدیریتی IIS به صورت زیر است.
ابتدا ریشهی اصلی سرور را انتخاب کنید و سپس به configuration editor آن وارد شوید:
در ادامه از دارپ داون آن، گزینهی system.applicationHost و زیر شاخهی applicationPools آنرا انتخاب کنید:
در اینجا application pool defaults و سپس در آن processModel را نیز باز کنید:
اکنون امکان ویرایش setProfileEnvironment را به true خواهید داشت:
پس از این تنظیم، ابتدا بر روی دکمهی apply سمت راست صفحه کلیک کرده و سپس نیاز است یکبار IIS را نیز ریست کنید تا تنظیمات اعمال شوند.
در ادامه برای تنظیم دسترسی write (موارد ب و ج)، ابتدا بر روی پوشهی app_data برنامه، کلیک راست کرده و برگهی security آنرا باز کنید. سپس بر روی دکمهی edit کلیک کرده و در صفحهی باز شده بر روی دکمهی add کلیک کنید تا بتوان به کاربر application pool برنامه دسترسی write داد:
در اینجا iis apppool\TestLocalDB را وارد کرده و بر روی دکمهی check name کلیک کنید.
iis apppool آن که مشخص است. عبارت TestLocalDB نام application pool ایی است که برای برنامهی وب خود ایجاد کردهایم (بهتر است به ازای هر برنامهی وب، یک application pool مجزا تعریف شود).
در اینجا بر روی OK کلیک کرده و به این کاربر جدید اضافه شده، دسترسی full control را بدهید تا برنامه و یوزر آن بتواند فایلهای mdf و ldf را ایجاد کرده و به روز رسانی کنند.
پس از تنظیم load user profile و همچنین set profile environment و دادن دسترسی write به کاربر application pool برنامه، اکنون child process مربوط به local db را میتوان ذیل پروسهی IIS مشاهده کرد و برنامه قادر به استفادهی از LocalDB خواهد بود:
کتابخانه SmartWizard
Smart Wizard is a flexible and heavily customizable jQuery step wizard plugin with Bootstrap support. It is easy to implement and gives a neat and stylish interface for your forms, checkout screen, registration steps etc. Based on the feedback from our users over the past years we have come up with the best ever built jQuery wizard plugin of all time. Demo
Features:
- Bootstrap support
- Responsive themes
- Heavily customizable toolbar, option to add extra buttons
- Theme support with various themes included
- Customizable css styles
- Url navigation and step selection
- Public methods for external function call
- Enhanced event support
- In-built wizard reset method
- Ajax content loading with option to specify individual url for steps
- Keyboard navigation
کتاب Bing Maps V8 Succinctly
At least 80% of all information being collected by enterprises includes geolocation data. The Bing Maps V8 library is a very large collection of JavaScript code that allows web developers to place a map on a webpage, query for data, and manipulate objects on a map, creating a geo-application. In Bing Maps V8 Succinctly, James McCaffrey takes readers through utilizing this library, from creating the simplest application that uses it, to mastering more advanced functions like creating color-gradient legends and custom-styled Infobox objects.
- Getting Started
- Fundamental Techniques
- Working with Data
- Advanced Techniques