توصیه هایی در استفاده از NGEN
در محل کار از کدام نگارش دات نت استفاده میکنید؟
در محل کار از کدام نگارش دات نت استفاده میکنید؟
سری آموزشی Blazor Hybrid
Blazor Hybrid for Beginners
Join James Montemagno as he takes you on a journey of building your first Hybrid applications across iOS, Android, Mac, Windows, and Web with ASP.NET Core, Blazor, Blazor Hybrid, and .NET MAUI! You will learn how to use Blazor Hybrid to blend desktop and mobile native client frameworks with .NET and Blazor.
In a Blazor Hybrid app, Razor components run natively on the device. Components render to an embedded Web View control through a local interop channel. Components don't run in the browser, and WebAssembly isn't involved. Razor components load and execute code quickly, and components have full access to the native capabilities of the device through the .NET platform. Component styles rendered in a Web View are platform dependent and may require you to account for rendering differences across platforms using custom stylesheets.
Blazor Hybrid support is built into the .NET Multi-platform App UI (.NET MAUI) framework. .NET MAUI includes the BlazorWebView control that permits rendering Razor components into an embedded Web View. By using .NET MAUI and Blazor together, you can reuse one set of web UI components across mobile, desktop, and web.
انواع 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 { }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
نمونهی این نکته را پیشتر با Structure Map ملاحظه کرده بودید. پیاده سازی آن با امکانات توکار تزریق وابستگیهای NET Core. یا بر اساس روش Factory است که در نکتهی قبل ملاحظه میکنید و یا اگر از یک اینترفیس چندین پیاده سازی در برنامه وجود داشته باشند و ارتباطات آنها در ابتدای کار برنامه به سیستم توکار تزریق وابستگیهای NET Core. معرفی شده باشند، فقط کافی است یک <IEnumerable<IMultiple را به سازندهی کلاس سرویس استفاده کننده تزریق کنیم:
private readonly IEnumerable<IMultiple> _services; public HomeController (IEnumerable<IMultiple> services) { _services = services; }
مرحلهی بعد، تشخیص و یا انتخاب یک پیاده سازی خاص است. الان لیستی از وهلههای تزریق شده را در اختیار داریم؛ اما میخواهیم فقط از یکی از آنها استفاده کنیم:
الف) انتخاب سرویس مدنظر بر اساس نوع کلاسی خاص
var serviceA = services.First(o => o.GetType() == typeof(ImplementationOne));
این روشی است که برای مثال در Structure Map هم استفاده میشود (تحت عنوان named instances). یک خاصیت Name را به اینترفیسی که چندین پیاده سازی دارد، اضافه کنید. سپس بر اساس این Name کوئری بگیرید:
var serviceB = services.First(o => o.Name.Equals("MyClassName"));