We're going to use Dapper.NET on our project; that much is not in doubt. However, we're not going to start development with it, and it will not be the only ORM in use. The plan is to develop this project using Entity Framework, and later optimize to use Dapper.NET in certain scenarios where the system needs a performance boost.
JSON.NET نسخه 7 منتشر شد
.NET Core + Angular Dashboard
Topics Covered:
- Building a dashboard application in Angular
- Building a Web API in .NET Core 2.0
- Using Chart.js to build stunning charts of different types
- Making HTTP requests using Angular to query a Web API
- Using Postman to send requests
- Working with Observables
- Using Input and Output decorators in Angular
- Using PostgreSQL and pgAdmin
- Automatically seeding a database with large amounts of sample data
- Styling an application using custom CSS and Bootstrap 4
- Using Map, Filter, and Reduce in Javascript
- Creating Routes in Angular
- Get, Put, Post, Patch Web API Controller Action request types
- Configuring your API for CORS
معرفی Scala برای توسعهدهندگان #C
Scala is a general purpose programming language designed to express common programming patterns in a concise, elegant, and type-safe way. It smoothly integrates features of object-oriented and functional languages, enabling Java and other programmers to be more productive. Code sizes are typically reduced by a factor of two to three when compared to an equivalent Java application.
انواع providers در Angular
سیستم تزریق وابستگیهای Angular، تامین کنندههای ذیل را نیز به همراه دارد:
- تامین کنندهی مقادیر که با useValue مشخص میشود.
- تامین کنندهی Factoryها که با useFactory تعریف خواهد شد.
- تامین کنندهی کلاسها که با useClass تعریف میشود.
- تامین کنندهی کلاسهایی با نامهای مستعار که توسط useExisting مشخص میشود.
یک تامین کننده مشخص میکند که سیستم تزریق کنندهی وابستگیها، با درخواست توکن/کلیدی مشخص، چه وابستگی را باید وهله سازی کند.
تزریق وابستگیهایی از نوع ثوابت در برنامههای Angular
فرض کنید برنامهی Angular شما در مسیر دیگری نسبت به Web API سمت سرور آن قرار دارد. به همین جهت در تمام سرویسهای برنامه نیاز به تعریف مسیر پایهی Web API مانند API_BASE_HREF را خواهید داشت. یک روش حل این مساله، تعریف این ثابت به صورت یک وابستگی و سپس تزریق آن به کلاسهای سرویسها و یا کامپوننتهای برنامه است:
@NgModule({ imports: [ CommonModule, InjectionBeyondClassesRoutingModule ], declarations: [TestProvidersComponent], providers: [ { provide: "API_BASE_HREF", useValue: "http://localhost:5000" }, { provide: "APP_BASE_HREF", useValue: document.location.pathname }, { provide: "IS_PROD", useValue: true }, { provide: "APIKey", useValue: "XYZ1234ABC" }, { provide: "Random", useValue: Math.random() }, { provide: "emailApiConfig", useValue: Object.freeze({ apiKey: "email-key", context: "registration" }) }, { provide: "languages", useValue: "en", multi: true }, { provide: "languages", useValue: "fa", multi: true } ] }) export class InjectionBeyondClassesModule { }
- در این مثال، حالتهای مختلفی از تامین کنندهی useValue را نیز مشاهده میکنید. انتساب یک رشته، یک مقدار boolean و یا یک مقدار که در زمان انتساب محاسبه خواهد شد مانند Math.random.
- همچنین در اینجا میتوان در قسمت useValue مانند emailApiConfig، یک شیء را نیز تعریف کرد. علت استفادهی از Object.freeze، تعریف این شیء به صورت read only است.
- در حین تعریف provideها اگر کلید توکن بکار رفته یکی باشد، آخرین مقدار، مابقی را بازنویسی میکند؛ مانند حالت languages که در اینجا دوبار تعریف شدهاست. اما با ذکر خاصیت multi، میتوان به کلید languages به صورت یک آرایه دسترسی یافت و در این حالت مقادیر آن بازنویسی نمیشوند.
اکنون برای استفادهی از این توکنهای تعریف شده توسط سیستم تزریق وابستگیها، میتوان به صورت ذیل عمل کرد:
import { Component, OnInit, Inject } from "@angular/core"; import { inject } from "@angular/core/testing"; @Component({ selector: "app-test-providers", templateUrl: "./test-providers.component.html", styleUrls: ["./test-providers.component.css"] }) export class TestProvidersComponent implements OnInit { constructor( @Inject("API_BASE_HREF") public apiBaseHref: string, @Inject("APP_BASE_HREF") public appBaseHref: string, @Inject("IS_PROD") public isProd: boolean, @Inject("APIKey") public apiKey: string, @Inject("Random") public random: string, @Inject("emailApiConfig") public emailApiConfig: any, @Inject("languages") public languages: string[] ) { } ngOnInit() { } }
<h1> Injection Beyond Classes </h1> <div class="alert alert-info"> <ul> <li>API_BASE_HREF: {{apiBaseHref}}</li> <li>APP_BASE_HREF: {{appBaseHref}}</li> <li>IS_PROD: {{isProd}}</li> <li>APIKey: {{apiKey}}</li> <li>Random-1: {{random}}</li> <li>Random-2: {{random}}</li> <li>emailApiConfig {{emailApiConfig | json}}</li> <li>languages: {{languages | json}}</li> </ul> </div>
در اینجا همانطور که مشاهده میکنید، languages از نوع multi: true به یک آرایه تبدیل شدهاست و یا emailApiConfig نیز یک شیء است که توسط کلیدهای آن میتوان به مقادیر متناظر آن دسترسی یافت. Random نیز تنها یکبار دریافت شدهاست و مهم نیست که چندبار صدا زده شود؛ همواره مقدار آن مساوی اولین مقداری است که در زمان انتساب دریافت میکند.
تزریق تنظیمات برنامه توسط تامین کنندهی مقادیر
یک نمونه از تزریق شیء emailApiConfig: any را در مثال فوق ملاحظه کردید. روش بهتر و نوع دار آن به صورت ذیل است. ابتدا یک فایل جدید thismodule.config.ts یا app.config.ts را ایجاد میکنیم:
import { InjectionToken } from "@angular/core"; export let APP_CONFIG = new InjectionToken<string>("this.module.config"); export interface IThisModuleConfig { apiEndpoint: string; } export const ThisModuleConfig: IThisModuleConfig = { apiEndpoint: "http://localhost:45043/api/" };
import { InjectionToken } from '@angular/core'; export const EmailService1 = new InjectionToken<string>("EmailService"); export const EmailService2 = new InjectionToken<string>("EmailService"); console.log(EmailService1 === EmailService2); // false
سپس نوع تنظیمات را توسط اینترفیس IThisModuleConfig تعریف کردهایم (که نسبت به استفادهی از any یک پیشرفت محسوب میشود). در آخر وهلهای از این اینترفیس را به نحوی که مشاهده میکنید export کردهایم.
اکنون نحوهی تعریف تزریق وابستگی از نوع IThisModuleConfig در یک NgModule به صورت ذیل است:
import { ThisModuleConfig, APP_CONFIG } from "./thismodule.config"; @NgModule({ providers: [ { provide: APP_CONFIG, useValue: ThisModuleConfig } ] }) export class InjectionBeyondClassesModule { }
در آخر، تزریق آن به سازندهی یک کامپوننت بر اساس توکن APP_CONFIG و از نوع مشخص اینترفیس آن خواهد بود:
import { IThisModuleConfig, APP_CONFIG } from "./../thismodule.config"; @Component() export class TestProvidersComponent implements OnInit { constructor( @Inject(APP_CONFIG) public config: IThisModuleConfig ) { } ngOnInit() { } }
تزریق وابستگیها توسط تامین کنندهی Factory ها
تا اینجا useValue را بررسی کردیم. نوع دیگر تامین کنندههای قابل تعریف، useFactory هستند:
@NgModule({ providers: [ // ------ useFactory { provide: "BASE_URL", useFactory: getBaseUrl }, { provide: "RandomFactory", useFactory: randomFactory } ] }) export class InjectionBeyondClassesModule { } export function getBaseUrl() { return document.getElementsByTagName("base")[0].href; } export function randomFactory() { return Math.random(); }
روش استفادهی از آن نیز همانند توکنهای useValue است که توسط ویژگی Inject مشخص میشوند:
export class TestProvidersComponent implements OnInit { constructor( @Inject("BASE_URL") public baseUrl: string, @Inject("RandomFactory") public randomFactory: string ) { }
حالت useFactory علاوه بر امکان دریافت یک منطق سفارشی توسط یک function، امکان دریافت یک سری وابستگی را نیز دارد. فرض کنید کلاس سرویس خودرو به صورت زیر تعریف شدهاست که دارای وابستگی از نوع HttpClient تزریق شدهی در سازندهی آن است:
import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; @Injectable() export class CarService { constructor(private http: HttpClient) { } }
import { CarService } from "./car.service"; import { HttpClient } from "@angular/common/http"; @NgModule({ providers: [ // ------ useFactory { provide: "Car_Service", useFactory: carServiceFactory, deps: [HttpClient] } ] }) export class InjectionBeyondClassesModule { } export function carServiceFactory(http: HttpClient) { return new CarService(http); }
تزریق وابستگیها توسط تامین کنندهی کلاسها
تا اینجا useValue و useFactory را بررسی کردیم. نوع دیگر تامین کنندههای قابل تعریف، useClass هستند. در حالت استفادهی useClass، نام یک نوع مشخص میشود و سپس Angular وهلهای از آنرا تامین خواهد کرد. در این حالت اگر این وابستگی دارای پارامترهای تزریق شدهای در سازندهی آن باشد، آنها نیز به صورت خودکار وهله سازی میشوند.
import { CarService } from "./car.service"; @NgModule({ providers: [ // ------ useClass { provide: "Car_Service_Name1", useClass: CarService }, ] }) export class InjectionBeyondClassesModule { }
import { CarService } from "./car.service"; @NgModule({ providers: [ CarService ] }) export class InjectionBeyondClassesModule { }
تزریق وابستگیها توسط تامین کنندهی کلاسهایی با نامهای مستعار
چگونه میتوان دو تامین کننده را برای کلاسی مشابه، با توکنهایی متفاوت ایجاد کرد؟ در این حالت از useExisting استفاده میشود:
import { CarService } from "./car.service"; @NgModule({ providers: [ // ------ useClass { provide: "Car_Service_Name1", useClass: CarService }, // ------ useExisting { provide: "Car_Service_Token2", useExisting: "Car_Service_Name1" }, ] }) export class InjectionBeyondClassesModule { }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
- و فقط در نگارشهای SQL Server 2014 Enterprise, Developer, and Evaluation editions پشتیبانی میشود. برای مثال نگارش Standard این قابلیت را ندارد.
- هنگام نصب هم باید گزینهی «Database Engine Services -> install support for In-Memory OLTP engine» انتخاب شده باشد.
Visual Studio 2017 15.6 منتشر شد
- We improved solution load performance by optimizing design time build.
- We've added installation progress details on Visual Studio Installer.
- You can pause your installation and resume at a later time.
- We streamlined the update process so the notification takes you directly to the Installer.
- Non-administrators can create a VS layout.
- We added a new shortcut for Edit.Duplicate in the keyboard mapping.
- We made significant improvements to the F# language and tools, particularly for .NET Core SDK projects.
- The C++ compiler optimizes your code to run faster through improved optimizations.
- C++ Mapfile generation overhead is reduced in full linking scenarios.
- Debug options are available for Embedded ARM GCC support.
- We added strong name signing on CoreCLR for the C# compiler.
- Visual Studio Tools for Xamarin has lots of new productivity updates for iOS and Android developers.
- Python no longer requires a completion DB, and Anaconda users have support for conda.
- The Performance Profiler's CPU Usage Tool can display logical call stacks for asynchronous code.
- The CPU Usage tool displays source line highlighting and async/await code with logical 'Call Stack Stitching'.
- The debugger supports thread names set via SetThreadDescription APIs in dump debugging.
- Snapshot Debugging can be started from the Debug Target dropdown for ASP.NET applications.
- We've launched the initial implementation of Navigate to decompiled sources for .NET code navigation.
- New enhancements for Configure Continuous Delivery include support for TFVC, Git authentication over SSH, and containerized projects.
- You can now click on the Continuous Delivery tile in Team Explorer to configure automated build and deployments for your application.
- Team Explorer supports Git tags and checking out pull request branches.
- Service Fabric Tooling for the 6.1 Service Fabric release is now available.
- The Windows 10 Insider Preview SDK can be installed as an optional component.
- File versions for a number of Visual Studio executables now reflect the minor release.
- Test Explorer has a hierarchy view and real time test discovery is now on by default.
- We have added support for testing Win10 IoT Core applications.
- Visual Studio Build Tools supports TypeScript and Node.js.
- ClickOnce Tools support signing application and deployment manifests with CNG certificate.
- You can access Azure resources such as Key Vault using your Visual Studio accounts.
EF Code First #11
استفاده از الگوی Repository اضافی در EF Code first؛ آری یا خیر؟!
اگر در ویژوال استودیو، اشارهگر ماوس را بر روی تعریف DbContext قرار دهیم، راهنمای زیر ظاهر میشود:
A DbContext instance represents a combination of the Unit Of Work and Repository patterns such that
it can be used to query from a database and group together changes that will then be written back to
the store as a unit. DbContext is conceptually similar to ObjectContext.
در اینجا تیم EF صراحتا عنوان میکند که DbContext در EF Code first همان الگوی Unit Of Work را پیاده سازی کرده و در داخل کلاس مشتق شده از آن، DbSetها همان Repositories هستند (فقط نامها تغییر کردهاند؛ اصول یکی است).
به عبارت دیگر با نام بردن صریح از این الگوها، مقصود زیر را دنبال میکنند:
لطفا بر روی این لایه Abstraction ایی که ما تهیه دیدهایم، یک لایه Abstraction دیگر را ایجاد نکنید!
«لایه Abstraction دیگر» یعنی پیاده سازی الگوهای Unit Of Work و Repository جدید، برفراز الگوهای Unit Of Work و Repository توکار موجود!
کار اضافهای که در بسیاری از سایتها مشاهده میشود و ... متاسفانه اکثر آنها هم اشتباه هستند! در ذیل روشهای تشخیص پیاده سازیهای نادرست الگوی Repository را بر خواهیم شمرد:
1) قرار دادن متد Save تغییرات نهایی انجام شده، در داخل کلاس Repository
متد Save باید داخل کلاس Unit of work تعریف شود نه داخل کلاس Repository. دقیقا همان کاری که در EF Code first به درستی انجام شده. متد SaveChanges توسط DbContext ارائه میشود. علت هم این است که در زمان Save ممکن است با چندین Entity و چندین جدول مشغول به کار باشیم. حاصل یک تراکنش، باید نهایتا ذخیره شود نه اینکه هر کدام از اینها، تراکنش خاص خودشان را داشته باشند.
2) نداشتن درکی از الگوی Unit of work
به Unit of work به شکل یک تراکنش نگاه کنید. در داخل آن با انواع و اقسام موجودیتها از کلاسها و جداول مختلف کار شده و حاصل عملیات، به بانک اطلاعاتی اعمال میگردد. پیاده سازیهای اشتباه الگوی Repository، تمام امکانات را در داخل همان کلاس Repository قرار میدهند؛ که اشتباه است. این نوع کلاسها فقط برای کار با یک Entity بهینه شدهاند؛ در حالیکه در دنیای واقعی، اطلاعات ممکن است از دو Entity مختلف دریافت و نتیجه محاسبات مفروضی به Entity سوم اعمال شود. تمام این عملیات یک تراکنش را تشکیل میدهد، نه اینکه هر کدام، تراکنش مجزای خود را داشته باشند.
3) وهله سازی از DbContext به صورت مستقیم داخل کلاس Repository
4) Dispose اشیاء DbContext داخل کلاس Repository
هر بار وهله سازی DbContext مساوی است با باز شدن یک اتصال به بانک اطلاعاتی و همچنین از آنجائیکه راهنمای ذکر شده فوق را در مورد DbContext مطالعه نکردهاند، زمانیکه در یک متد با سه وهله از سه Repository موجودیتهای مختلف کار میکنید، سه تراکنش و سه اتصال مختلف به بانک اطلاعاتی گشوده شده است. این مورد ذاتا اشتباه است و سربار بالایی را نیز به همراه دارد.
ضمن اینکه بستن DbContext در یک Repository، امکان اعمال کوئریهای بعدی LINQ را غیرممکن میکند. به ظاهر یک شیء IQueryable در اختیار داریم که میتوان بر روی آن انواع و اقسام کوئریهای LINQ را تعریف کرد اما ... در اینجا با LINQ to Objects که بر روی اطلاعات موجود در حافظه کار میکند سر و کار نداریم. اتصال به بانک اطلاعاتی با بستن DbContext قطع شده، بنابراین کوئری LINQ بعدی شما کار نخواهد کرد.
همچنین در EF نمیتوان یک Entity را از یک Context به Context دیگری ارسال کرد. در پیاده سازی صحیح الگوی Repository (دقیقا همان چیزی که در EF Code first به صورت توکار وجود دارد)، Context باید بین Repositories که در اینجا فقط نامش DbSet تعریف شده، به اشتراک گذاشته شود. علت هم این است که EF از Context برای ردیابی تغییرات انجام شده بر روی موجودیتها استفاده میکند (همان سطح اول کش که در قسمتهای قبل به آن اشاره شد). اگر به ازای هر Repository یکبار وهله سازی DbContext انجام شود، هر کدام کش جداگانه خاص خود را خواهند داشت.
5) عدم امکان استفاده از تنها یک DbConetext به ازای یک Http Request
هنگامیکه وهله سازی DbContext به داخل یک Repository منتقل میشود و الگوی واحد کار رعایت نمیگردد، امکان به اشتراک گذاری آن بین Repositoryهای تعریف شده وجود نخواهد داشت. این مساله در برنامههای وب سبب کاهش کارآیی میگردد (باز و بسته شدن بیش از حد اتصال به بانک اطلاعاتی در حالیکه میشد تمام این عملیات را با یک DbContext انجام داد).
نمونهای از این پیاده سازی اشتباه را در اینجا میتوانید پیدا کنید. متاسفانه شبیه به همین پیاده سازی، در پروژه MVC Scaffolding نیز بکارگرفته شده است.
چرا تعریف لایه دیگری بر روی لایه Abstraction موجود در EF Code first اشتباه است؟
یکی از دلایلی که حین تعریف الگوی Repository دوم بر روی لایه موجود عنوان میشود، این است:
«به این ترتیب به سادگی میتوان ORM مورد استفاده را تغییر داد» چون پیاده سازی استفاده از ORM، در پشت این لایه مخفی شده و ما هر زمان که بخواهیم به ORM دیگری کوچ کنیم، فقط کافی است این لایه را تغییر دهیم و نه کل برنامه را.
ولی سؤال این است که هرچند این مساله از هزار فرسنگ بالاتر درست است، اما واقعا تابحال دیدهاید که پروژهای را با یک ORM شروع کنند و بعد سوئیچ کنند به ORM دیگری؟!
ضمنا برای اینکه واقعا لایه اضافی پیاده سازی شده انتقال پذیر باشد، شما باید کاملا دست و پای ORM موجود را بریده و تواناییهای در دسترس آن را به سطح نازلی کاهش دهید تا پیاده سازی شما قابل انتقال باشد. برای مثال یک سری از قابلیتهای پیشرفته و بسیار جالب در NH هست که در EF نیست و برعکس. آیا واقعا میتوان به همین سادگی ORM مورد استفاده را تغییر داد؟ فقط در یک حالت این امر میسر است: از قابلیتهای پیشرفته ابزار موجود استفاده نکنیم و از آن در سطحی بسیار ساده و ابتدایی کمک بگیریم تا از قابلیتهای مشترک بین ORMهای موجود استفاده شود.
ضمن اینکه مباحث نگاشت کلاسها به جداول را چکار خواهید کرد؟ EF راه و روش خاص خودش را دارد، NH چندین و چند روش خاص خودش را دارد! اینها به این سادگی قابل انتقال نیستند که شخصی عنوان کند: «هر زمان که علاقمند بودیم، ORM مورد استفاده را میشود عوض کرد!»
دلیل دومی که برای تهیه لایه اضافهتری بر روی DbContext عنوان میکنند این است:
«با استفاده از الگوی Repository نوشتن آزمونهای واحد سادهتر میشود». زمانیکه برنامه بر اساس Interfaceها کار میکند میتوان آنها را بجای اشاره به بانک اطلاعاتی، به نمونهای موجود در حافظه، در زمان آزمون تغییر داد.
این مورد در حالت کلی درست است اما .... نه در مورد بانکهای اطلاعاتی!
زمانیکه در یک آزمون واحد، پیاده سازی جدیدی از الگوی Interface مخزن ما تهیه میشود و اینبار بجای بانک اطلاعاتی با یک سری شیء قرارگرفته در حافظه سروکار داریم، آیا موارد زیر را هم میتوان به سادگی آزمایش کرد؟
ارتباطات بین جداولرا، cascade delete، فیلدهای identity، فیلدهای unique، کلیدهای ترکیبی، نوعهای خاص تعریف شده در بانک اطلاعاتی و مسایلی از این دست.
پاسخ: خیر! تغییر انجام شده، سبب کار برنامه با اطلاعات موجود در حافظه خواهد شد، یعنی LINQ to Objects.
شما در حالت استفاده از LINQ to Objects آزادی عمل فوق العادهای دارید. میتوانید از انواع و اقسام متدها حین تهیه کوئریهای LINQ استفاده کنید که هیچکدام معادلی در بانک اطلاعاتی نداشته و ... به ظاهر آزمون واحد شما پاس میشود؛ اما در عمل بر روی یک بانک اطلاعاتی واقعی کار نخواهد کرد.
البته شاید شخصی عنوان که بله میشود تمام اینها نیازمندیها را در حالت کار با اشیاء درون حافظه هم پیاده سازی کرد ولی ... در نهایت پیاده سازی آن بسیار پیچیده و در حد پیاده سازی یک بانک اطلاعاتی واقعی خواهد شد که واقعا ضرورتی ندارد.
و پاسخ صحیح در اینجا و این مساله خاص این است:
لطفا در حین کار با بانکهای اطلاعاتی مباحث mocking را فراموش کنید. بجای SQL Server، رشته اتصالی و تنظیمات برنامه را به SQL Server CE تغییر داده و آزمایشات خود را انجام دهید. پس از پایان کار هم بانک اطلاعاتی را delete کنید. به این نوع آزمونها اصطلاحا integration tests گفته میشود. لازم است برنامه با یک بانک اطلاعاتی واقعی تست شود و نه یک سری شیء ساده قرار گرفته در حافظه که هیچ قیدی همانند شرایط کار با یک بانک اطلاعاتی واقعی، بر روی آنها اعمال نمیشود.
ضمنا باید درنظر داشت بانکهای اطلاعاتی که تنها در حافظه کار کنند نیز وجود دارند. برای مثال SQLite حالت کار کردن صرفا در حافظه را پشتیبانی میکند. زمانیکه آزمون واحد شروع میشود، یک بانک اطلاعاتی واقعی را در حافظه تشکیل داده و پس از پایان کار هم ... اثری از این بانک اطلاعاتی باقی نخواهد ماند و برای این نوع کارها بسیار سریع است.
نتیجه گیری:
حین استفاده از EF code first، الگوی واحد کار، همان DbContext است و الگوی مخزن، همان DbSetها. ضرورتی به ایجاد یک لایه محافظ اضافی بر روی اینها وجود ندارد.
در اینجا بهتر است یک لایه اضافی را به نام مثلا Service ایجاد کرد و تمام اعمال کار با EF را به آن منتقل نمود. سپس در قسمتهای مختلف برنامه میتوان از متدهای این لایه استفاده کرد. به عبارتی در فایلهای Code behind برنامه شما نباید کدهای EF مشاهده شوند. یا در کنترلرهای MVC نیز به همین ترتیب. اینها مصرف کننده نهایی لایه سرویس ایجاد شده خواهند بود.
همچنین بجای نوشتن آزمونهای واحد، به Integration tests سوئیچ کنید تا بتوان برنامه را در شرایط کار با یک بانک اطلاعاتی واقعی تست کرد.
برای مطالعه بیشتر:
مقاله درباره ViewModel
Use ViewModels to manage data & organize code in ASP.NET MVC applications
The concept of the ViewModel isn't just for ASP.NET MVC, as you'll see references to ViewModels throughout the web in articles and blog posts about the MVC, MVP, and MVVM patterns. Those posts and articles can center around any number of technologies such as ASP.NET, Silverlight, WPF, or MVC... This post will investigate ViewModels as they apply to the world of ASP.NET MVC.