اگر هدف به اشتراک گذاشتن اطلاعات است، برای اینکار از سرویسها استفاده میشود و نه کامپوننتها.
بررسی رابطهی One-to-Zero-or-One
زمانیکه نیاز است موجودیت A با هیچ و یا حداکثر یک وهله از موجودیت B در ارتباط باشد، به یک چنین رابطهای One-to-Zero-or-One میگویند. برای اینکه یک چنین ارتباطی را تشکیل دهیم، نیاز است کلید اصلی یک جدول، در جدول مرتبط به آن، هم به عنوان کلید اصلی و هم به عنوان کلید خارجی معرفی شود؛ همانند شکل زیر که در آن CartableId، همزمان به صورت PK و FK تعریف شدهاست که به آن one-to-one association with shared primary key نیز میگویند:
الف) مدلسازی رابطهی One-to-Zero-or-One توسط Fluent API
در اینجا دو موجودیت را ملاحظه میکنید که توسط دو navigation property به هم متصل شدهاند:
برای اینکه بتوان CartableId را هم به عنوان PK و هم FK معرفی کرد، نیاز است از Fluent API به نحو ذیل استفاده کنیم:
در اینجا رابطهی یک به یک، توسط متدهای HasOne و WithOne معرفی شدهاست. به علاوه FK بودن CartableId به صورت صریح توسط متد HasForeignKey نیز مشخص گردیدهاست.
همچنین بر روی CartableId، فراخوانی متد ValueGeneratedNever را مشاهده میکنید. این متد را در قسمت «روشهای مختلف تولید خودکار مقادیر خواص» مطلب «شروع به کار با EF Core 1.0 - قسمت 5 - استراتژهای تعیین کلید اصلی جداول و ایندکسها» پیشتر بررسی کردیم. هدف آن این است که به بانک اطلاعاتی اعلام کند، این فیلد یک کلید اصلی از نوع خود افزایش یابنده نیست و مقدار آن توسط برنامه به صورت صریح تنظیم میشود (چون کلید خارجی نیز هست و به کلید اصلی جدول سمت دیگر رابطه اشاره میکند).
ب) مدلسازی رابطهی One-to-Zero-or-One توسط Data Annotations
برای تنظیم این رابطه توسط ویژگیها نیاز است DatabaseGenerated به None تنظیم شود تا کلید اصلی CartableId به صورت خودکار توسط بانک اطلاعاتی تولید نشود. همچنین این کلید اصلی نیز باید به صورت کلید خارجی نیز معرفی شود. به علاوه توسط InversePropertyها، باید هر دو سطر به هم مرتبط، ذکر شوند:
بررسی رابطهی One-to-One
تشکیل رابطهی One-to-One که در آن برخلاف رابطهی One-to-Zero-or-One، وجود هر دو سر رابطه اجباری هستند، در SQL Server میسر نیست (زیرا زمانیکه یک چنین رابطهای تشکیل شود، نمیتوان رکوردی را Insert کرد! چون زمانیکه هنوز یک سر رابطه ثبت نشدهاست، چگونه میتوان Id آنرا در سر دیگری به اجبار ثبت کرد؟!). SQL Server این رابطه را نیز به صورت همان One-to-Zero-or-One تشکیل میدهد. تنظیمات آن نیز با قبل تفاوتی ندارد. در این حالت اجباری بودن و یا نبودن یک سر رابطه همانند نکات قسمت «تعیین اجباری بودن یا نبودن ستونها در EF Core» در مطلب «شروع به کار با EF Core 1.0 - قسمت 6 - تعیین نوعهای داده و ویژگیهای آنها» است و این تنظیمات در اینجا صرفا از دیدگاه EF Core مفهوم دارند.
جهت تکمیل بحث، روش تشکیل رابطهی One-to-One بدون استفاده از روش به اشتراک گذاری کلید اصلی (one-to-one association with shared primary key) به صورت زیر است:
همانطور که مشاهده میکنید، در اینجا هر بلاگ حداکثر یک تصویر را میتواند داشته باشد. علت آن نیز به ذکر MyBlogForeignKey بر میگردد که یک کلید خارجی نال نپذیر است.
با این تنظیمات:
در اینجا جدول MyBlogImage هنوز Id خود افزایندهی خود را دارد، اما ثبت رکورد آن بدون وجود کلید خارجی MyBlog میسر نیست.
زمانیکه نیاز است موجودیت A با هیچ و یا حداکثر یک وهله از موجودیت B در ارتباط باشد، به یک چنین رابطهای One-to-Zero-or-One میگویند. برای اینکه یک چنین ارتباطی را تشکیل دهیم، نیاز است کلید اصلی یک جدول، در جدول مرتبط به آن، هم به عنوان کلید اصلی و هم به عنوان کلید خارجی معرفی شود؛ همانند شکل زیر که در آن CartableId، همزمان به صورت PK و FK تعریف شدهاست که به آن one-to-one association with shared primary key نیز میگویند:
الف) مدلسازی رابطهی One-to-Zero-or-One توسط Fluent API
در اینجا دو موجودیت را ملاحظه میکنید که توسط دو navigation property به هم متصل شدهاند:
public class UserProfile { public int UserProfileId { get; set; } public string UserName { get; set; } public virtual Cartable Cartable { get; set; } }
public class Cartable { public int CartableId { get; set; } public virtual UserProfile UserProfile { get; set; } }
public class MyDBDataContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Data Source=(local);Initial Catalog=testdb2;Integrated Security = true"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Cartable>(entity => { entity.Property(e => e.CartableId).ValueGeneratedNever(); entity.HasOne(d => d.UserProfile) .WithOne(p => p.Cartable) .HasForeignKey<Cartable>(d => d.CartableId); }); } public virtual DbSet<Cartable> Cartables { get; set; } public virtual DbSet<UserProfile> UserProfiles { get; set; } }
همچنین بر روی CartableId، فراخوانی متد ValueGeneratedNever را مشاهده میکنید. این متد را در قسمت «روشهای مختلف تولید خودکار مقادیر خواص» مطلب «شروع به کار با EF Core 1.0 - قسمت 5 - استراتژهای تعیین کلید اصلی جداول و ایندکسها» پیشتر بررسی کردیم. هدف آن این است که به بانک اطلاعاتی اعلام کند، این فیلد یک کلید اصلی از نوع خود افزایش یابنده نیست و مقدار آن توسط برنامه به صورت صریح تنظیم میشود (چون کلید خارجی نیز هست و به کلید اصلی جدول سمت دیگر رابطه اشاره میکند).
ب) مدلسازی رابطهی One-to-Zero-or-One توسط Data Annotations
برای تنظیم این رابطه توسط ویژگیها نیاز است DatabaseGenerated به None تنظیم شود تا کلید اصلی CartableId به صورت خودکار توسط بانک اطلاعاتی تولید نشود. همچنین این کلید اصلی نیز باید به صورت کلید خارجی نیز معرفی شود. به علاوه توسط InversePropertyها، باید هر دو سطر به هم مرتبط، ذکر شوند:
public class Cartable { [DatabaseGenerated(DatabaseGeneratedOption.None)] public int CartableId { get; set; } [ForeignKey("CartableId")] [InverseProperty("Cartable")] public virtual UserProfile UserProfile { get; set; } }
public class UserProfile { public int UserProfileId { get; set; } public string UserName { get; set; } [InverseProperty("UserProfile")] public virtual Cartable Cartable { get; set; } }
بررسی رابطهی One-to-One
تشکیل رابطهی One-to-One که در آن برخلاف رابطهی One-to-Zero-or-One، وجود هر دو سر رابطه اجباری هستند، در SQL Server میسر نیست (زیرا زمانیکه یک چنین رابطهای تشکیل شود، نمیتوان رکوردی را Insert کرد! چون زمانیکه هنوز یک سر رابطه ثبت نشدهاست، چگونه میتوان Id آنرا در سر دیگری به اجبار ثبت کرد؟!). SQL Server این رابطه را نیز به صورت همان One-to-Zero-or-One تشکیل میدهد. تنظیمات آن نیز با قبل تفاوتی ندارد. در این حالت اجباری بودن و یا نبودن یک سر رابطه همانند نکات قسمت «تعیین اجباری بودن یا نبودن ستونها در EF Core» در مطلب «شروع به کار با EF Core 1.0 - قسمت 6 - تعیین نوعهای داده و ویژگیهای آنها» است و این تنظیمات در اینجا صرفا از دیدگاه EF Core مفهوم دارند.
جهت تکمیل بحث، روش تشکیل رابطهی One-to-One بدون استفاده از روش به اشتراک گذاری کلید اصلی (one-to-one association with shared primary key) به صورت زیر است:
همانطور که مشاهده میکنید، در اینجا هر بلاگ حداکثر یک تصویر را میتواند داشته باشد. علت آن نیز به ذکر MyBlogForeignKey بر میگردد که یک کلید خارجی نال نپذیر است.
public class MyBlog { public int MyBlogId { get; set; } public string Url { get; set; } public MyBlogImage MyBlogImage { get; set; } } public class MyBlogImage { public int MyBlogImageId { get; set; } public byte[] Image { get; set; } public string Caption { get; set; } public int MyBlogForeignKey { get; set; } public MyBlog MyBlog { get; set; } }
modelBuilder.Entity<MyBlog>() .HasOne(p => p.MyBlogImage) .WithOne(i => i.MyBlog) .HasForeignKey<MyBlogImage>(b => b.MyBlogForeignKey);
اشتراکها
Bootstrap 5.1.2 منتشر شد
اشتراکها
Bootstrap 5.1.1 منتشر شد
داشتن Breadcrumbs یکی از گزینههای مفید بهبود کاربری هر سایتی است و در برنامههای Angular با کوئری گرفتن از سیستم مسیریابی آن میتوان به سادگی آنها را تولید کرد.
ایجاد ساختاری برای نگهداری آرایهی breadcrumbs
کامپوننت نمایش breadcrumbs را در مسیر src\app\core\bread-crumb ایجاد میکنیم. یعنی قصد داریم آنرا به CoreModule برنامه اضافه کنیم؛ از این جهت که کامپوننت آن، تکمیل کنندهی app.component است و هر کامپوننتی که تنها در این کامپوننت ویژه بکار رود، محل تعریف آن در CoreModule خواهد بود.
به همین جهت کامپوننت bread-crumb را به صورت ذیل ایجاد میکنیم:
که تعاریف آن در فایل src\app\core\core.module.ts در قسمت exports و declarations درج خواهد شد:
و سپس اینترفیس bread-crumb.ts را در مسیر src\app\core\bread-crumb\bread-crumb.ts به نحو زیر تعریف میکنیم:
برچسب و url، دو حداقل پیشفرض نمایش قطعات منوی breadcrumbs هستند.
تکمیل کامپوننت Breadcrumb
کدهای کامل کامپوننت Breadcrumb را در ذیل مشاهده میکنید:
توضیحات:
در اینجا در ابتدای کار، مشترک رخداد NavigationEnd سیستم مسیریابی خواهیم شد:
هر زمانیکه رخداد مرور صفحهی جاری به پایان رسید، بر اساس مسیر ریشهی آن، متد buildBreadCrumbs فراخوانی میشود. این متد، یک متد بازگشتی است. از این جهت که مسیر جاری میتواند حاصل مرور یک مسیر والد و سپس چندین مسیر تو در توی فرزند و والد آن باشد. برای نمونه ممکن است مانند تصویر ابتدای بحث، مسیریابی شما به صورت ذیل تعریف شده باشد:
در اینجا چندین مسیر والد و فرزند تو در تو را مشاهده میکنید.
بر اساس قراردادی که در کامپوننت نمایش bread-crumbs درنظر گرفتهایم، عنوان هر مسیر در خاصیت data آن با کلید breadcrumb درج میشود:
مقدار این خاصیت را به صورت ذیل در متد buildBreadCrumbs میخوانیم:
برای بار اول فراخوانی متد بازگشتی buildBreadCrumbs، مقدار url خالی است. به همین جهت برچسب home را به آن نسبت خواهیم داد. در بار بعدی، اطلاعات path و data مسیر فعال شده را از شیء route.routeConfig استخراج کرده و توسط آن یک bread-crumb جدید را ایجاد میکنیم:
سپس اگر این مسیر فرزندی را داشته باشد، مقدار خاصیت route.firstChild آن نال نبوده و میتوان متد بازگشتی را در همینجا با فراخوانی متد جاری تشکیل داد:
علت استفادهی از روش [breadcrumb, breadcrumbs...] بجای استفاده از متد push آرایه، مربوط است به
در مورد ViewEncapsulation در مطلب «بررسی استراتژیهای تشخیص تغییرات در برنامههای Angular» بیشتر بحث کردهایم و تنظیم آن به None، کارآیی برنامه را با کاهش کار سیستم ردیابی تغییرات Angular، بهبود میبخشد.
تکمیل قالب نمایش Breadcrumbs
پس از تکمیل BreadCrumbComponent، اکنون قالب آن به صورت ذیل تعریف میشود:
در اینجا بر روی آرایهی تشکیل شدهی توسط متد buildBreadCrumbs، یک حلقه ایجاد شده و عناصر آن نمایش داده میشوند. چون خاصیت عمومی breadcrumbs به صورت یک Observable تعریف شدهاست، در اینجا استفاده از async pipe را نیز در این حلقه مشاهده میکنید:
نمایش نهایی BreadCrumbs
در پایان کافی است به فایل app.component.html مراجعه کرده و selector کامپوننت نمایش bread crumbs را در آن درج کنیم:
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
ایجاد ساختاری برای نگهداری آرایهی breadcrumbs
کامپوننت نمایش breadcrumbs را در مسیر src\app\core\bread-crumb ایجاد میکنیم. یعنی قصد داریم آنرا به CoreModule برنامه اضافه کنیم؛ از این جهت که کامپوننت آن، تکمیل کنندهی app.component است و هر کامپوننتی که تنها در این کامپوننت ویژه بکار رود، محل تعریف آن در CoreModule خواهد بود.
به همین جهت کامپوننت bread-crumb را به صورت ذیل ایجاد میکنیم:
ng g c core/bread-crumb
import { BreadCrumbComponent } from "./bread-crumb/bread-crumb.component"; @NgModule({ imports: [CommonModule, RouterModule], exports: [ // components that are used in app.component.ts will be listed here. BreadCrumbComponent ], declarations: [ // components that are used in app.component.ts will be listed here. BreadCrumbComponent ] }) export class CoreModule {}
export interface BreadCrumb { label: string; url: string; };
تکمیل کامپوننت Breadcrumb
کدهای کامل کامپوننت Breadcrumb را در ذیل مشاهده میکنید:
import { Component, OnInit, ViewEncapsulation } from "@angular/core"; import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; import { BreadCrumb } from "./bread-crumb"; import { Observable } from "rxjs/Observable"; @Component({ selector: "app-bread-crumb", templateUrl: "./bread-crumb.component.html", styleUrls: ["./bread-crumb.component.css"], encapsulation: ViewEncapsulation.None }) export class BreadCrumbComponent implements OnInit { breadcrumbs$: Observable<BreadCrumb[]>; constructor(private activatedRoute: ActivatedRoute, private router: Router) { } ngOnInit() { this.breadcrumbs$ = this.router.events .filter(event => event instanceof NavigationEnd) .distinctUntilChanged() .map(event => event ? this.buildBreadCrumbs(this.activatedRoute.root) : []); } buildBreadCrumbs(route: ActivatedRoute, url: string = "", breadcrumbs: Array<BreadCrumb> = []): Array<BreadCrumb> { const routeDataBreadCrumbKey = "breadcrumb"; const routeConfig = route.routeConfig; const path = routeConfig && routeConfig.path !== undefined ? routeConfig.path : ""; let label = path; if (url === "") { label = "Home"; } else if (routeConfig && routeConfig.data !== undefined) { label = routeConfig.data[routeDataBreadCrumbKey]; } const nextUrl = `${url}${path}/`; const breadcrumb: BreadCrumb = { label: label, url: nextUrl }; console.log("breadcrumb", { path: path, label: label, url: nextUrl, route: route }); const newBreadcrumbs = [...breadcrumbs, breadcrumb]; if (route.firstChild) { return this.buildBreadCrumbs(route.firstChild, nextUrl, newBreadcrumbs); } return newBreadcrumbs; } }
در اینجا در ابتدای کار، مشترک رخداد NavigationEnd سیستم مسیریابی خواهیم شد:
ngOnInit() { this.breadcrumbs$ = this.router.events .filter(event => event instanceof NavigationEnd) .distinctUntilChanged() .map(event => event ? this.buildBreadCrumbs(this.activatedRoute.root) : []); }
const routes: Routes = [ { path: "breadCrumbTest", data: { breadcrumb: "Parent1" }, children: [ { path: "", component: Parent1Component }, { path: "Parent1Child1", data: { breadcrumb: "Parent1-Child1" }, children: [ { path: "", component: Parent1Child1Component }, { path: "Parent1Child1Child1", component: Parent1Child1Child1Component, data: { breadcrumb: "Parent1-Child1 Child1" } } ] } ] } ];
بر اساس قراردادی که در کامپوننت نمایش bread-crumbs درنظر گرفتهایم، عنوان هر مسیر در خاصیت data آن با کلید breadcrumb درج میشود:
data: { breadcrumb: "Parent1-Child1 Child1" }
buildBreadCrumbs(route: ActivatedRoute, url: string = "", breadcrumbs: Array<BreadCrumb> = []): Array<BreadCrumb> { const routeDataBreadCrumbKey = "breadcrumb"; const routeConfig = route.routeConfig; const path = routeConfig && routeConfig.path !== undefined ? routeConfig.path : ""; let label = path; if (url === "") { label = "Home"; } else if (routeConfig && routeConfig.data !== undefined) { label = routeConfig.data[routeDataBreadCrumbKey]; }
const nextUrl = `${url}${path}/`; const breadcrumb: BreadCrumb = { label: label, url: nextUrl };
const newBreadcrumbs = [...breadcrumbs, breadcrumb]; if (route.firstChild) { return this.buildBreadCrumbs(route.firstChild, nextUrl, newBreadcrumbs); } return newBreadcrumbs;
encapsulation: ViewEncapsulation.None
تکمیل قالب نمایش Breadcrumbs
پس از تکمیل BreadCrumbComponent، اکنون قالب آن به صورت ذیل تعریف میشود:
<ol class="breadcrumb"> <li *ngFor="let breadcrumb of breadcrumbs$ | async" class="breadcrumb-item"> <a [routerLink]="[breadcrumb.url]">{{ breadcrumb.label }}</a> </li> </ol>
breadcrumbs$: Observable<BreadCrumb[]>;
نمایش نهایی BreadCrumbs
در پایان کافی است به فایل app.component.html مراجعه کرده و selector کامپوننت نمایش bread crumbs را در آن درج کنیم:
<div class="container"> <app-bread-crumb></app-bread-crumb> <router-outlet></router-outlet> </div>
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
در نگاه اول این ترکیب ممکن است کمی گیجکننده باشد ولی بعد از مدتی کار با React به قدرت آن پی خواهید برد. در واقع درج کدهای JSX درون کامپوننتها یک مزیت محسوب میشود زیرا پیمایش بین کدها را راحتتر و همچنین نوشتن و دیباگ کامپوننتها را سادهتر خواهد کرد. از آنجائیکه تمام قابلیتها درون یک فایل تعریف میشوند دیگر نیاز نخواهد بود زمان زیادی را برای سوئیچ کردن بین فایلهای جدا (تمپلیت و...) هدر دهید. همچنین جداسازی فایلهای JSX از کامپوننت به معنای پیروی از اصل Separation Of Concern نیست چون مجموع فایلها در نهایت مختص به یک کامپوننت هستند (به عنوان مثال نمیتوان تمپلیت را برای یک کامپوننت دیگر استفاده کرد و...)
ضمناً برای اینکه حجم کدهایتان بعد از مدتی زیاد نشود بهتر است کامپوننتهایی که جزئیات زیادی دارند را به Child Componentهای کوچکتری تقسیم کنید (مانند مثال طراحی گرید این سری).
نظرات مطالب
آموزش سیلورلایت 4 - قسمتهای 21 تا 27
در صورت امکان اگر توضیحانی و نمونه های از wcf ria service ونحوه کار با mssql روی سرور و برقرای ارتباط با silverlight درسایت قرار بدید ممنون می شم
Here are some of the reasons why nullable reference types are less than ideal:
- Invoking a member on a null value will issue a System.NullReferenceException exception, and every invocation that results in a System.NullReferenceException in production code is a bug. Unfortunately, however, with nullable reference types we “fall in” to doing the wrong thing rather than the right thing. The “fall in” action is to invoke a reference type without checking for null.
- There’s an inconsistency between reference types and value types (following the introduction of Nullable<T>) in that value types are nullable when decorated with “?” (for example, int? number); otherwise, they default to non-nullable. In contrast, reference types are nullable by default. This is “normal” to those of us who have been programming in C# for a long time, but if we could do it all over, we’d want the default for reference types to be non-nullable and the addition of a “?” to be an explicit way to allow nulls.
- It’s not possible to run static flow analysis to check all paths regarding whether a value will be null before dereferencing it, or not. Consider, for example, if there were unmanaged code invocations, multi-threading, or null assignment/replacement based on runtime conditions. (Not to mention whether analysis would include checking of all library APIs that are invoked.)
- There’s no reasonable syntax to indicate that a reference type value of null is invalid for a particular declaration.
- There’s no way to decorate parameters to not allow null.