این صفحه برداشتی از کتاب The Icon handbook است
ساخت Favicon آنلاین
با رعایت نکات چک لیست تهیه یک برنامه ASP.NET MVC و ساخت favicon.ico و apple-touch-icon-XxY.png
تهیه یک بانک اطلاعاتی نمونه
برای نمایش امکانات کار با روش Database first، نیاز است یک بانک اطلاعاتی را به صورت مستقل و متداولی ایجاد کنیم. به همین جهت اسکریپت SQL ذیل را توسط Management studio اجرا کنید تا بانک اطلاعاتی BloggingCore2016، به همراه دو جدول به هم وابسته، در آن ایجاد شوند:
CREATE DATABASE [BloggingCore2016] GO USE [BloggingCore2016] GO CREATE TABLE [Blog] ( [BlogId] int NOT NULL IDENTITY, [Url] nvarchar(max) NOT NULL, CONSTRAINT [PK_Blog] PRIMARY KEY ([BlogId]) ); GO CREATE TABLE [Post] ( [PostId] int NOT NULL IDENTITY, [BlogId] int NOT NULL, [Content] nvarchar(max), [Title] nvarchar(max), CONSTRAINT [PK_Post] PRIMARY KEY ([PostId]), CONSTRAINT [FK_Post_Blog_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blog] ([BlogId]) ON DELETE CASCADE ); GO INSERT INTO [Blog] (Url) VALUES ('https://www.dntips.ir/'), ('http://blogs.msdn.com/dotnet'), ('http://blogs.msdn.com/webdev'), ('http://blogs.msdn.com/visualstudio') GO
پیشنیازهای مهندسی معکوس ساختار بانک اطلاعاتی در EF Core
در قسمت اول در حین بررسی «برپایی تنظیمات اولیهی EF Core 1.0 در یک برنامهی ASP.NET Core 1.0»، چهار مدخل جدید را به فایل project.json برنامه اضافه کردیم. مدخل جدید Microsoft.EntityFrameworkCore.Tools که به قسمت tools آن اضافه شد، پیشنیاز اصلی کار با EF Core Migrations است. همچنین وجود مدخل Microsoft.EntityFrameworkCore.SqlServer.Design برای تدارک امکانات مهندسی معکوس ساختار یک بانک اطلاعاتی SQL Server ضروری است.
تبدیل ساختار دیتابیس BloggingCore2016 به کدهای معادل EF Core آن
پس از فعال سازی ابزارهای خط فرمان EF Core، به پوشهی اصلی پروژه مراجعه کرده، کلید shift را نگه دارید. سپس کلیک راست کرده و گزینهی Open command window here را انتخاب کنید تا خط فرمان از این پوشه آغاز شود. در ادامه دستور ذیل را صادر کنید:
dotnet ef dbcontext scaffold "Data Source=(local);Initial Catalog=BloggingCore2016;Integrated Security = true" Microsoft.EntityFrameworkCore.SqlServer -o Entities --context MyDBDataContext --verbose
اجرا این دستور سبب اتصال به رشتهی اتصالی ذکر شده که به بانک اطلاعاتی BloggingCore2016 اشاره میکند، میشود. سپس پروایدر مدنظر ذکر شدهاست. سوئیچ o محل درج فایلهای نهایی را مشخص میکند. برای مثال در اینجا فایلهای نهایی مهندسی معکوس شده در پوشهی Entities درج میشوند (تصویر فوق). همچنین در اینجا امکان ذکر فایل context تولیدی نیز وجود دارد. اگر علاقمند باشید تا تمام ریز جزئیات این عملیات را نیز مشاهده کنید، میتوانید پارامتر اختیاری verbose را نیز به انتهای دستور اضافه نمائید.
بقیه مراحل کار با این فایلهای تولید شده، با نکاتی که تاکنون عنوان شدهاند یکی است. برای مثال اگر میخواهید رشتهی اتصالی پیش فرض را از این Context تولید شده خارج کنید:
public partial class MyDBDataContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Data Source=(local);Initial Catalog=BloggingCore2016;Integrated Security = true"); }
بررسی پارامترهای دیگر ابزار مهندسی معکوس به Code First
اگر دستور dotnet ef dbcontext scaffold --help را صادر کنیم، خروجی راهنمای ذیل را میتوان مشاهده کرد:
Usage: dotnet ef dbcontext scaffold [arguments] [options] Arguments: [connection] The connection string of the database [provider] The provider to use. For example, Microsoft.EntityFrameworkCore.SqlServer Options: -a|--data-annotations Use DataAnnotation attributes to configure the model where possible. If omitted, the output code will use only the fluent API. -c|--context <name> Name of the generated DbContext class. -f|--force Force scaffolding to overwrite existing files. Otherwise, the code will only proceed if no output files would be overwritten. -o|--output-dir <path> Directory of the project where the classes should be output. If omitted, the top-level project directory is used. --schema <schema> Selects a schema for which to generate classes. -t|--table <schema.table> Selects a table for which to generate classes. -e|--environment <environment> The environment to use. If omitted, "Development" is used. -h|--help Show help information -v|--verbose Enable verbose output
- حالت پیش فرض تنظیمات روابط مدلها در این روش، حالت استفاده از Fluent API است. اگر میخواهید آنرا به حالت استفادهی از Data Annotations تغییر دهید، پارامتر a- و یا data-annotations-- را در دستور نهایی ذکر کنید.
- حالت پیش فرض تولید فایلهای نهایی این روش، عدم بازنویسی فایلهای موجود است. اگر میخواهید پس از تغییر بانک اطلاعاتی، مجددا این فایلها را از صفر تولید کنید، پارامتر f- و یا force- را در دستور نهایی ذکر کنید.
بنابراین اگر میخواهید هربار فایلهای نهایی را بازنویسی کنید و همچنین روش کار با Data Annotations را ترجیح میدهید، دستور نهایی، شکل زیر را پیدا خواهد کرد:
dotnet ef dbcontext scaffold "Data Source=(local);Initial Catalog=BloggingCore2016;Integrated Security = true" Microsoft.EntityFrameworkCore.SqlServer -o Entities --context MyDBDataContext --verbose --force --data-annotations
کار با یک بانک اطلاعاتی موجود، با روش مهاجرتهای Code First
فرض کنید میخواهید از یک بانک اطلاعاتی از پیش موجود EF 6.x (یا هر بانک اطلاعاتی از پیش موجود دیگری)، به روش پیش فرض EF Core استفاده کنید. برای این منظور:
- ابتدا جدول migration history قدیمی آنرا حذف کنید؛ چون ساختار آن با EF Core یکی نیست.
- سپس با استفاده از دستور dotnet ef dbcontext scaffold فوق، معادل کلاسها، روابط و Context سازگار با EF Core آنرا تولید کنید.
- در ادامه رشتهی اتصالی پیش فرض آنرا از کلاس Context تولیدی خارج کرده و از یکی از روشهای مطرح شدهی در مطلب «شروع به کار با EF Core 1.0 - قسمت 1 - برپایی تنظیمات اولیه» استفاده کنید.
- سپس نیاز است این Context جدید را توسط متد services.AddDbContext به لیست سرویسهای برنامه اضافه کنید. این مورد نیز در قسمت اول بررسی شدهاست.
- مرحلهی بعد، افزودن جدول __EFMigrationsHistory جدید EF Core، به این بانک اطلاعاتی است. برای این منظور به روش متداول فعال کردن مهاجرتها، دستور ذیل را صادر کنید:
dotnet ef migrations add InitialDatabase
using Microsoft.EntityFrameworkCore.Migrations; namespace Core1RtmEmptyTest.DataLayer.Migrations { public partial class InitialDatabase : Migration { protected override void Up(MigrationBuilder migrationBuilder) { } protected override void Down(MigrationBuilder migrationBuilder) { } } }
سپس دستور به روز رسانی بانک اطلاعاتی را صادر کنید:
dotnet ef database update
پس از این مرحله، روش کار، Code first خواهد بود. برای مثال خاصیتی را به کلاسی اضافه میکنید و سپس دو دستور ذیل را صادر خواهید کرد که در آن v2 یک نام دلخواه است:
dotnet ef migrations add v2 dotnet ef database update
Please open an issue in the library repository to alert its author and ask them to package the library using the Angular Package Format (https://goo.gl/jB3GVv).
مراحل ایجاد یک پروژهی «کتابخانه» توسط Angular CLI 6.0
مرحلهی اول ایجاد یک پروژهی کتابخانه، مانند قبل، توسط دستور ng new و ایجاد یک پروژهی دلخواه جدید است:
ng new my-lib-test
پس از ایجاد پروژهی my-lib-test توسط دستور فوق و وارد شدن به پوشهی اصلی آن توسط خط فرمان، میتوان با اجرای دستور زیر، پروژههای دیگری را به پروژهی جاری افزود:
ng generate application my-app-name
ng generate library my-lib
همچنین یک پوشهی جدید به نام projects نیز ایجاد شده و پروژهی my-lib داخل آن قرار گرفتهاست.
فایل جدید public_api.ts
پس از ایجاد کتابخانهی جدید «my-lib»، فایل جدیدی به نام projects\my-lib\src\public_api.ts نیز به آن اضافه شدهاست:
با این محتوا:
/* * Public API Surface of my-lib */ export * from './lib/my-lib.service'; export * from './lib/my-lib.component'; export * from './lib/my-lib.module';
برای مثال اگر فایل جدید projects\my-lib\src\lib\my-lib.models.ts را به این کتابخانه اضافه کنیم که شامل تعدادی مدل و اینترفیس قابل دسترسی توسط استفاده کنندگان باشد، باید یک سطر زیر را به انتهای فایل public_api.ts اضافه کنیم:
export * from './lib/my-lib.models';
این پروژهی کتابخانه حتی به همراه فایلهای package.json, tsconfig.json, tslint.json مخصوص به خود نیز میباشد تا بتوان آنها را صرفا جهت این پروژه سفارشی سازی کرد.
ساختار my-lib.service پیشفرض یک پروژهی کتابخانه
اگر به فایل projects\my-lib\src\lib\my-lib.service.ts دقت کنیم:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class MyLibService { constructor() { } }
شاید بپرسید چرا؟ هدف اصلی از آن، بهبود فرآیند tree-shaking یا حذف کدهای مرده و استفاده نشدهاست. ممکن است سرویسی را تعریف کنید، اما در برنامه استفاده نشود. این حالت خصوصا در پروژههای کتابخانههای ثالث ممکن است زیاد رخ دهد. به همین جهت با ارائهی این قابلیت، امکان حذف سادهتر سرویسهایی که در برنامه استفاده نشدهاند از خروجی نهایی کامپایل شده، وجود خواهد داشت.
چگونه به پروژهی کتابخانهی جدید، یک کامپوننت جدید را اضافه کنیم؟
تمام دستورات Angular CLI، در اینجا نیز کار میکنند. تنها تفاوت آنها، ذکر صریح نام پروژهی مورد استفاده است:
ng generate component show-data --project=my-lib
البته در اینجا باید فایل my-lib.module.ts را اندکی ویرایش کرد و ShowDataComponent را به قسمت exports نیز افزود:
@NgModule({ imports: [ CommonModule, HttpClientModule ], declarations: [MyLibComponent, ShowDataComponent], exports: [MyLibComponent, ShowDataComponent] }) export class MyLibModule { }
همچنین قسمت imports آن نیز به صورت پیشفرض خالی است. اگر نیاز است با ngIf کار کنید، باید CommonModule را در اینجا قید کنید و اگر نیاز است تبادلات HTTP وجود داشته باشد، ذکر HttpClientModule نیز ضروری است.
مرحلهی ساخت پروژه
پیش از استفادهی از این پروژهی کتابخانه، باید آنرا build کرد:
ng build my-lib
پس از اجرای این دستور، خروجی ذیل مشاهده میشود:
Building Angular Package Building entry point 'my-lib' Rendering Stylesheets Rendering Templates Compiling TypeScript sources through ngc Downleveling ESM2015 sources through tsc Bundling to FESM2015 Bundling to FESM5 Bundling to UMD Minifying UMD bundle Remap source maps Relocating source maps Copying declaration files Writing package metadata Removing scripts section in package.json as it's considered a potential security vulnerability. Built my-lib Built Angular Package! - from: D:\my-lib-test\projects\my-lib - to: D:\my-lib-test\dist\my-lib
استفادهی از کتابخانهی تولید شده
پس از پایان موفقیت آمیز مرحلهی Build، اکنون نوبت به استفادهی از این کتابخانه است. استفادهی از آن نیز همانند تمام کتابخانهها و وابستگیهای ثالثی است که تا پیش از این از آنها استفاده کردهایم. برای مثال ماژول آنرا در قسمت imports مربوط به NgModule کلاس AppModule معرفی میکنیم. برای این منظور به فایل src\app\app.module.ts مراجعه کرده و MyLibModule را به نحو ذیل اضافه میکنیم:
import { MyLibModule } from "my-lib"; @NgModule({ imports: [ BrowserModule, MyLibModule ] }) export class AppModule { }
اما سؤال اینجا است که آیا این پوشه پس از build، داخل پوشهی node_modules نیز کپی شدهاست؟ پاسخ آن خیر است و برای مدیریت خودکار آن، به صورت زیر عمل شدهاست:
اگر به فایل tsconfig.json اصلی و واقع در ریشهی workspace دقت کنید، پس از اجرای دستور «ng generate library my-lib»، قسمت paths آن نیز به صورت خودکار ویرایش شدهاست:
{ "compilerOptions": { "paths": { "my-lib": [ "dist/my-lib" ] } } }
برای نمونه اگر شارهگر ماوس را بر روی my-lib قرار دهید، به درستی مسیر خوانده شدن آن، تشخیص داده میشود.
به این ترتیب مسیر این import، چه در این پروژهی محلی و چه برای کسانیکه پوشهی dist/my-lib را به صورت یک بستهی npm جدید دریافت کردهاند، یکی خواهد بود.
در ادامه اگر به فایل app.component.html مراجعه کرده و selector کامپوننت show-data را به آن اضافه کنیم:
<lib-show-data></lib-show-data>
توزیع کتابخانهی ایجاد شده برای عموم
برای اینکه این کتابخانهی تولیدی را در اختیار عموم، در سایت npm قرار دهیم، ابتدا باید کتابخانه را در حالت production build تولید و سپس آنرا publish کرد:
ng build my-lib --prod cd dist/my-lib npm publish
البته دستور آخر نیاز به ایجاد یک اکانت در سایت npm و وارد شدن به آنرا دارد. جزئیات بیشتر آن در اینجا.
> npm install @angular/animations@latest --save
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @NgModule({ imports: [ //… BrowserAnimationsModule ], })
در اینجا مثال سادهای را مشاهده خواهید کرد که انتقال (transition) المنت li را از حالت active به inactive، با یک انمیشن ساده همراه خواهد کرد. محل تعریف انیمیشنها در Angular، داخل متادیتای component@ و توسط خصوصیت animations میباشد. در ابتدای کار باید توابع مختص انیمیشن را در فایل component مورد نظر خود توسط دستور import به شکل زیر وارد کنیم:
import { trigger, state, style, transition, animate } from '@angular/animations';
import { Component } from '@angular/core'; import { trigger,state,style,transition,animate } from '@angular/animations'; @Component({ selector: 'app-root', template: ` <div style='width:50%; margin:auto'> <ul> <li [@myState]="currentState" style='width:100px; padding:10px; list-style-type: none' (click)="toggleState()"> {{currentState}} </li> </ul> </div> `, styleUrls: ['./app.component.css'], animations: [ trigger('myState', [ state('inactive', style({ backgroundColor: '#eee', transform: 'scale(1)' })), state('active', style({ backgroundColor: '#cfd8dc', transform: 'scale(1.1)' })), transition('inactive => active', animate('100ms ease-in')), transition('active => inactive', animate('100ms ease-out')) ]) ] }) export class AppComponent { currentState: string = 'inactive'; toggleState():void{ this.currentState = this.currentState === 'inactive' ? 'active' : 'inactive'; } }
توضیحات تکمیلی
trigger: جهت تعریف یک انیمیشن، از تابع trigger استفاده میکنیم. این تابع استفاده شدهی در خصوصیت animations، در پارامتر اول خود، یک نام یکتا را دریافت خواهد کرد و در پارامتر بعدی، لیستی از توابع وارد شده مختص انیمیشن را دریافت میکند.
نکته: خصوصیت animations، قابلیت دریافت چندین تابع trigger را برای داشتن چندین انیمیشن مجزا، دارا است.
state: با استفاده از این تابع قادر به تعریف وضعیتهای مختلف خواهیم بود. انیمشن در Angular بر دو منطق وضعیت (state) و گذار (transition) پیاده سازی میشود. به عبارت دیگر انیمیشن در Angular بر روی گذار (transition) بین وضعیتها (states) قابل تعریف هستند. این تابع در پارامتر اول خود یک نام و در پارامتر دوم خود تابع style را دریافت میکند.
style: با استفاده از این تابع قادر به تعریف استایلی برای وضعیت تعریف شده خواهیم بود.
transition: برای ساخت انیمیشن واقعی مورد استفاده قرار میگیرد. این تابع در پارامتر اول خود، مسیر حرکت بین دو حالت (state) را به صورت یک رشته دریافت کرده و در پارامتر دوم خود، تابع animate را دریافت میکند. در این مثال مسیر حرکت به صورت 'inactive => active' تعریف شده است. یعنی هنگام گذار از وضعیت inactive به وضعیت active، تابع animate در پارامتر دوم اجرا خواهد شد.
animate: این تابع دو پارامتر timing و styles را دریافت میکند. timing مقداری از جنس رشته (string) است که میتواند ترکیبی از مدت زمان (duration) با مقدار اختیاری تاخیر(delay) و مقدار easing باشد. به عنوان مثال مقدار کامل زیر را درنظر بگیرید:
'1s 100ms ease-out'
در این اینجا مدت زمان، برابر ۱ ثانیه، تاخیر، ۱۰۰ میلی ثانیه و easing، مقدار ease-out خواهد بود و به معنی اجرای انیمیشن با افکت ease-out و در مدت زمان ۱ ثانیه و با تاخیر در شروع به مدت ۱۰۰ میلی ثانیه میباشد. در صورتیکه به این پارامتر مقداری عددی را ارسال کنیم، عدد به عنوان مدت زمان (duration) و بر مبنای واحد میلی ثانیه در نظر گرفته خواهد شد. تمامی مقادیر زیر برای ارسال به این پارامتر معتبر میباشند:
500 // duration=500ms "1s" // duration=1s "100ms 0.5s" // duration=100ms, delay=0.5s "5s ease" // duration=5s, easing=ease "5s 10ms cubic-bezier(.17,.67,.88,.1)" // duration=5s, delay=10ms, easing=cubic-bezier(.17,067,.88,.1)
پارامتر styles در تابع animate یکی از توابع style یا keyframes میباشد. البته این پارامتر اختیاری است و در قسمت نکات تکمیلی توضیح داده خواهد شد. در مثال بالا از این پارامتر صرف نظر شده است.
برای متصل کردن انیمیشن تعریف شده به المنتهای موجود در صفحه، از خصوصیت [triggerName@] استفاده کنید. trigger تعریف شده در قطعه کد بالا myState نام دارد. بنابراین برای اینکه المنت li در گذار بین حالتها، این انیمیشن را داشته باشد، باید [myState@] را به المنت خود اضافه کنید. همچنین مقدار حالت جاری را باید برای این خصوصیت تامین کرد. این مقدار میتواند یک رشته استاتیک یا یک عبارت محاسبه شده توسط یک تابع یا یک متغیر باشد.
همانطور که در مثال بالا مشاهده میکنید، با استفاده از متغیر currentState، المنت li در ابتدا در حالت inactive قرار دارد. با کلیک اول بر روی المنت، تابع toggleState باعث تغییر وضعیت المنت از وضعیت inactive به وضعیت active خواهد شد (inactive=>active) بنابراین انیمیشن زیر اجرا خواهد شد.
transition('inactive => active', animate('100ms ease-in'))
با کلیک دوباره، المنت از وضعیت active به inactive خواهد رفت (active=>inactive)، بنابراین انیمیشن زیر اجرا میشود.
نکات تکمیلی
transition('inactive => active, active => inactive', animate('100ms ease-out'))
یا به شکل سادهتر:
transition('inactive <=> active', animate('100ms ease-out'))
کاراکتر * جایگزین تمامی حالتهای موجود در برنامه خواهد بود. برای مثال:
'* <= active': بر روی تمامی گذارهای از حالت active به هر حالت دیگر، اعمال خواهد شد.
'active <= *': بر روی تمامی گذارهای از هر حالت به حالت active، اعمال خواهد شد.
'* <= *': بر روی تمامی گذارها اعمال خواهد شد.
همانطور که قبلا ذکر شد، تابع animate در پارامتر دوم خود یک تابع style یا keyframes را دریافت میکند. در صورتیکه بخواهیم در مدت زمان اجرای انیمیشن بر روی المنت، استایلی را نیز همراه کنیم، میتوانیم از تابع style استفاده کنیم. اما این استایل بعد از اتمام انیمیشن بر روی المنت باقی نخواهد ماند؛ چون حالت (state) مقصد برای خود استایل تعریف شدهاست. علاوه بر این، در تابع transition میتوان به شکل زیر یک استایل خطی را نیز تعریف کرد:
transition('inactive => active', [ style({ backgroundColor: '#cfd8dc', transform: 'scale(1.3)' }), animate('80ms ease-in', style({ backgroundColor: '#eee', transform: 'scale(1)' })) ]),
این تعریف استایل در تابع transition در شروع گذار بلافاصله بر روی المنت اعمال خواهد شد و در طول انیمیشن استایل را از دست خواهد داد و به استایل مقصد خواهد رسید.
در اینجا یک مثال بسیار ساده از نحوه پیاده سازی انیمیشن در Angular را مورد برسی قرار دادیم. در بخش بعدی نحوهی پیاده سازی انیمیشنهای پیشرفته که قابلیت اعمال بر روی المنتها را هنگام اضافه شدن به صفحه و ترک صفحه را خواهند داشت، مورد بررسی قرار میدهیم.
<span class='button goodDetail' id='123' >جزییات کالا</span>
$('.goodDetail').click(function(){ //add to hash data that you need to make the AJAX request later $(window).location.hash = $(this).attr('id'); $.ajax({ type: "POST", url: 'some url', dataType: "html", data:'GoodId='+$(this).attr('id'), success: function(html) { //do something } }) })
$(document).ready(function(){ if ($(window).location.hash.length)) { //we will use $(window).location.hash.replace('#','') in the "data" section of the AJAX request $.ajax({ type: "POST", url: current_url, dataType: "html", data:'GoodId='+$(window).location.hash.replace('#',''), success: function(html) { //do something } }) } });
C# 7 - Generalized Async Return Types
ASP.NET MVC #11
شرح مسئله :
View من به شکل زیره :
<% using (Html.BeginForm("Forget", "Account", FormMethod.Post, new { encType = "multipart/form-data", id = "forgetForm", name = "forgetForm" })) { %> <%: Html.AntiForgeryToken() %> <%: Html.ValidationSummary(true) %> <input id="User_Name" name="User_Name" type="text" data-original-title="لطفاً نام کاربری انتخابی خود را وارد کنید" data-toggle="tooltip" data-placement="top"> <button id="submitForgetForm" type="button"><i></i> بازیابی کلمه عبور </button> <% } %>
کجای کار اشتباه است ؟ با تشکر