کنید دنبال

طراحی پایگاه داده Codefirst

برای شروع متن خود را بنویسید. همچنین میتوانید با زدن دکمه Enter و سپس از طریق آیکون + عناصر مختلفی را به بلاگ اضافه نمایید.

در این مقاله قصد دارم تا به ایجاد یک دیتابیس نمونه در پایگاه داده sql Server با روش codefirst و با استفاده از migration ها بپردازیم. هدف اصلی از ایجاد این پایگاه داده، پیاده سازی الگوی Generic Repository در مقالات بعدی است؛ لذا تصمیم گرفتم تا مراحل طراحی این پایگاه داده را به صورتcode first انجام داده و به صورت یک مقاله آموزشی به انتشار آن بپردازم.

پیش نیاز های این مقاله

1)     آشنایی با پایگاه داده های رابطه ای

2)     آشنایی با زبان برنامه نویسی سیشارپ

3)     نصب پایگاه داده Sql Server و آماده سازی آن جهت اتصال از طریق connection string

(جهت یادگیری مراحل نصب Sql server به این لینک مراجعه نمایید.)

4)     ترجیحا آشنایی با محیط ویژوال استدیو

اکنون برای آن دسته از عزیزانی که با کلمه مایگریشن آشنایی ندارند به سوال زیر پاسخ میدهیم.

Migration چیست؟

از مایگریشن ها برای ایجاد پایگاه داده بر اساس کدهایی که توسعه داده ایم، استفاده میکنیم. همانطور که از عنوان Code First مشخص است؛ در ابتدا entity ها (در واقع مدل متناظر با جداول ما در پایگاه داده هستند) و روابط آنها و سایر پیکربندی ها را پیاده سازی میکنیم و سپس با استفاده از مایگریشن، دیتابیس اصلی خود را روی سرور RDBMS که در اینجا sql server و به صورت لوکال سرور(روی رایانه شما) است ایجاد میکنیم.

پایگاه داده‌ای که قصد داریم بسازیم مربوط به یک سیستم فروشگاهی و شامل جداول زیر است.(این پایگاه داده نمونه و جهت یادگیری است و ممکن است از دقت بیزینسی  جهت پیاده سازی دقیق برخوردار نباشد.)

1)    Categories: شامل اطلاعات دسته بندی محصولات

2)    Products: شامل اطلاعات محصولات

3)    Users: شامل اطلاعات کاربران

4)    Payments: شامل اطلاعات پرداخت ها

5)    Baskets: شامل اطلاعات سبد خرید مربوط به پرداخت های کاربران

دیاگرام پایگاه داده

دیاگرام پایگاه داده نهایی و روابط آن به صورت زیر خواهد بود.

مراحل پیاده سازی

1)    ابتدا یک پروژه از نوع Asp.Net Core Web App با نامی دلخواه(در اینجا CodeFirst) ایجاد نمایید.(در این مثال از این نوع پروژه استفاده شده است، اما این اجباری نیست و به نیاز شما بستگی خواهد داشت.)

2)    در پروژه ایجاد شده framework های زیر را از طریق کنسول یا ویزارد NuGet اضافه نمایید.

Install-Package Microsoft.EntityFrameworkCore -Version 6.0.2

Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 6.0.2

Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.2

فریم ورک های فوق مربوط به entity framework جهت کار با پایگاه داده ‌ها از جمله sql server و همچنین امکان انجام مایگریشن است.

3)    سپس در مسیر پروژه ایجاد شده، فولدری با نام Entities جهت قرار دادن کلاسهای entity اضافه کنید.

4)    سپس کلاسهای انتیتی زیر را به پروژه اضافه میکنیم.

·       این انتیتی ها مربوط به جداول پایگاه داده هستند که در ابتدای مقاله نام برده شد.

·       هر یک از انتیتی ها شامل کلید Id به عنوان PrimaryKey و همچنین  ForeignKey(در صورت لزوم) است.

·        سایر فیلدهای انتیتی صرفا به عنوان نمونه قرار داده شده و از آنجا که در محتوی آموزشی ما نقش چندانی ندارند بسیاری از فیلدهایی که در پیاده سازی عملیاتی ممکن است لازم باشد در نظر گرفته نشده است.

·       کلاس EntityBase به خودی خود یک جدول در پایگاه داده نیست اما در سطح کد، شامل فیلدهای مشترکی است که در سایر انتیتی ها باید وجود داشته باشد. از جمله این فیلدها میتوان به تاریخ ایجاد یک رکورد(CreatedDate)، فیلد مربوط به حذف یک رکورد(IsDeleted) و فیلد مربوط به تاریخ ویرایش یک رکورد(LastModifiedDate) اشاره کرد.  قاعدتا سایر انتیتی ها باید این فیلدها را از EntityBase به ارث ببرند.

namespace
CodeFirst.Entities

{

    public class EntityBase

    {

        public int Id { get; set; }

        public DateTime CreatedDate { set; get; }

        public bool IsDeleted { set; get; }

        public DateTime LastModifiedDate { get;  set; }

    }

    public class Basket:EntityBase

    {

        public int ProductId { get; set; }

        public int PaymentId { get; set; }

        public Product Product { get; set; }

        public Payment Payment { get; set; }

    }

    public class Category : EntityBase

    {

       
[StringLength(50)]

        public string Title { get; set; }

        public IEnumerable
Products { set; get; }

    }

    public class Payment:EntityBase

    {

        public double TotalPrice { get; set; }

        public Guid UserId { get; set; }

        public User User { get; set; }

        public IEnumerable
Baskets { set; get; }

    }

    public class Product : EntityBase

    {

        public string Title { get; set; }

        public double Price { get; set; }

        public int CategoryId { get; set; }

        public Category Category { get; set; }

        public IEnumerable
Baskets { set; get; }

    }

    public class User : EntityBase

    {

        public Guid Id { get; set; }

       
[StringLength(50)]

        public string Username { get; set; }

        public string PasswordHash { get; set; }

        public IEnumerable
Payments { set; get; }

    }

}

نکته مهم: در طراحی انتیتی ها، برای ایجاد روابط یک به چند(one to many)  همانند رابطه دسته بندی و محصول(یک دسته بندی شامل چند محصول است.) باید در کلاس طرف one (Category) لیستی از کلاسهای طرف  many(Product) قرار داده و همچنین در طرف کلاس many (در اینجا Product) یک ابجکت از جنس کلاس طرف one (در اینجا Category) قرار دهید  که در کلاسهای فوق پیاده سازی شده است.

به زبان ساده تر، از آنجا که یک کتگوری شامل چند محصول است لیستی از محصولات داخل کلاس خود دارد و از آنجا که یک محصول قطعه متعلق به یک کتگوری است، یک آبجکت کتگوری در کلاس خود دارد.

5)    پس از پیاده سازی کامل انتیتی ها، نوبت به ایجاد کلاسContext  است. این کلاس، از DbContext که یک کلاس built int در انتیتی فریم ورک است، ارث بری میکند. این کلاس در واقع نقطه اتصال ما به پایگاه داده در زمان اجرای یک کوئری یا انجام مایگریشن ها است بنابراین از اهمیت خاصی برخوردار میباشد. به طور کلی در این کلاس موارد زیر قرار میگیرد:

·       DbSet نگاشت انتیتی به جداول پایگاه داده است. به طور مثال به کد زیر توجه کنید.

public DbSet<User> Users { get; set; }

قطعه کد فوق مشخص میکند که باید در پایگاه داده جدولی به نام Users وجود داشته باشد که فیلدهای آن مطابق با انتیتی User است.

·       بازنویسی(override) متد OnModelCreating که در این متد پیکربندی های خاصی از جداول پایگاه داده، رکوردها و فیلدهای آن مشخص میشود. پیکربندی های مختلفی در طول پروژه نیاز میشود که در متد میتوان مشخص نمود.(به طور مثال default value، computed column و موارد این چنینی...)

·       بازنویسی متد SaveChangeAsync

این متد زمانی که یک رکورد با استفاده از انتیتی فریم ورک در دیتابیس insert یا update میشود فراخوانی میشود و از طریق آن میتوان برخی عملیات کاربردی و مفید را پیاده سازی کرد. به طور مثال در این پروژه، ما بر اساس اینکه یک رکورد در حال insert است یا update، فیلدهای CreatedDate یا LastModifiedDate را برای رکورد مقداردهی میکنیم.

در مجموع، پیاده سازی کلاس Context با اجزای ذکر شده به شکل زیر خواهد بود.

namespace CodeFirst.DBContext

{

    public class Context : DbContext

    {

        public Context(DbContextOptions options) : base(options)

        {

        }

        public DbSet Users { get; set; }

        public DbSet Products { get; set; }

        public DbSet Categories { get; set; }

        public DbSet Baskets { get; set; }

        public DbSet Payments { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)

        {

           
modelBuilder.Entity(entity =>

            {

                entity.HasOne(d =>
d.Payment)

                    .WithMany(p =>
p.Baskets)

                    .HasForeignKey(d => d.PaymentId);

                entity.HasOne(d =>
d.Product)

                    .WithMany(p =>
p.Baskets)

                    .HasForeignKey(d =>
d.ProductId);

            });

           
modelBuilder.Entity().HasOne(d => d.User)

                    .WithMany(p =>
p.Payments)

                    .HasForeignKey(d =>
d.UserId);

           
modelBuilder.Entity().HasOne(d => d.Category)

                    .WithMany(p =>
p.Products)

                    .HasForeignKey(d =>
d.CategoryId);

        }

        public override Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())

        {

            foreach (var entry in ChangeTracker.Entries())

            {

                switch (entry.State)

                {

                    case EntityState.Added:

                       
entry.Entity.CreatedDate = DateTime.Now;

                        break;

                    case EntityState.Modified:

                        entry.Entity.LastModifiedDate =
DateTime.Now;

                        break;

                }

            }

            return base.SaveChangesAsync(cancellationToken);

        }

    }

}

در متد OnModelCreating، باید روابط و کلید خارجی موجود در هر یک از روابط دقیقا مشخص شود. به طور مثال قطعه کد زیر یک رابطه مابین جداول Product و Category را مشخص میکند.

modelBuilder.Entity().HasOne(d
=> d.Category)

.WithMany(p => p.Products)

.HasForeignKey(d => d.CategoryId);

6)     پس از ایجاد Context باید آن را در کلاس  Program(Startup در نسخه 5 به قبل Core) جهت Inject شدن در سایر کلاسهای پروژه و مشخص کردن ConnectionString پیکربندی کنیم. رشته اتصال دیتابیس را در فایل App Setting قرار میدهیم. این فایل باید به شکل زیر باشد:

{

  "Logging": {

    "LogLevel": {

      "Default": "Information",

      "Microsoft.AspNetCore": "Warning"

    }

  },

  "ConnectionStrings": {

    "SellerConnectionString": "Server=.;Database=Seller;User
Id=sa;Password=123456;"

  },

  "AllowedHosts": "*"

}

در اینجا رشته اتصال به دیتابیس لوکال متصل است و برای اتصال از یوزر sa با پسورد 123456 استفاده میکند. شما میتوانید مطابق پایگاه داده مد نظر خود این رشته را تغییر دهید. همچنین مشخص شده است که نام پایگاه داده هدف ما Seller خواهد بود(یعنی مایگریشن یک پایگاه داده به این نام را به صورت خودکار برای ما خواهد ساخت.)

سپس در کلاس Program پیکربندی Context را مطابق قطعه کد زیر انجام میدهیم.

builder.Services.AddDbContext(options
=>

              
options.UseSqlServer(builder.Configuration.GetConnectionString("SellerConnectionString")));

در کد فوق مشخص شده است که کلاس Context، باید به پایگاه داده مربوط به رشته اتصالی که در AppSetting با عنوان SellerConnectionString مشخص شده است متصل شود.

7)    در حال حاضر پایگاه داده، جداول و هر آنچه که برای ایجاد پایگاه داده نمونه خود لازم داشتیم، مشخص شده است. هم اکنون نوبت به انجام عملیات مایگریشن میرسد.(برای مایگریشن حتما فریم ورک های ابتدای مقاله را نصب کنید.)

مراحل مایگریشن

1)    ابتدا پنجره  PackageManagerConsole را جهت نوشتن کامندهای مربوط به ماگریشن به در ویژوال استدیو باز کنید.(معمولا به صورت دیفالت قابل مشاهده است.)

میتوانید از طریق مسیر زیر، پنجره کنسول را اضافه کنید.

View/Other Windows/Package Manager Console

2)    ماگریشن اولیه را از طریق کامند زیر ایجاد کنید.

add-migration initial_mig

با اجرای کامند فوق، به طور خودکار پوشه ای بسیار مهم به نام Migrations ایجاد میشود که از این پس تمامی ماگریشن های شما در طول توسعه در این فولدر اضافه میشوند. بنابراین یک کلاس هم نام با عنوان مایگریشن در این فولدر ایجاد میشود که در آن بر اساس انیتی ها و Context، کدهایی جهت ایجاد دیتابیس در sql server ایجاد میشود. کلاسهای مایگریشن را به هیچ وجه حذف نکنید. (علت را در انتهای مقاله بیان میکنیم.)

توجه داشته باشید که هر ماگریشن یک عنوان دلخواه خواهد داشت. به طور مثال در کامند فوق، نام ماگریشن را initial-mig گذاشته ایم. معمولا در طول توسعه پروژه، بار ها و بارها از کامند add-migration استفاده خواهید کرد. عنوانی که برای مایگریشن در نظر میگیرید، باید حتما یکتا باشد و نباید نام تکراری استفاده کنید.

3)    همه چیز برای ایجاد پایگاه داده آماده است. بنابراین با کامند زیر گام نهایی را برداشته و پایگاه داده ساخته میشود.

update-database

4)    اکنون اگر با نرم افزار ssms، پایگاه داده های روی سرور را مشاهده کنید، خواهید دید که یک پایگاه داده به نام Seller ایجاد شده است و در آن تمامی جداول مد نظر ما با ویژگی ها و روابط مشخص شده ایجاد شده است.

5)    پایگاه داده ایجاد شده و برای ادامه مراحل توسعه پروژه آماده است.

6)     از این پس قطعا شما نیاز خواهید داشت تا اصلاحاتی را در دیتابیس ایجاد کنید. به طور مثال فیلدی به یک انتیتی اضافه کنید و یا دیتاتایپ یک فیلد را تغییر دهید و یا حتی جداول جدیدی بسازید. تغییرات زیادی از این قبیل مورد نیاز خواهد بود که پس از ایجاد اولیه پایگاه داده اتفاق می افتد. در این مواقع نیز شما همانند موارد فوق عمل میکنید. ابتدا کامند add-migration را اجرا کنید و سپس کامند update-database را جهت اعمال اصلاحات در پایگاه داده اجرا کنید.(این کامندها هم برای ایجاد در اولین مرتبه و هم برای اعمال اصلاحات استفاده میشوند و به صورت هوشمند تغییرات در انتیتی ها و دیتابیس ها را میشناسند.)

ملاحظات و نکات

·       ملاک شناسنایی تغییرات در هر بار اجرای کامند add-migration، کلاسهایی است که در اجرای کامندهای قبلی در فولدر Migrations ایجاد شده است، بنابراین حذف کردن یک کلاس از این فولدر میتواند مشکلات زیادی ایجاد کند.

·       در موارد خاصی که ماگریشن یک تغییر خاص را به درستی هندل نمیکند، میتوانید در کلاس ایجاد شده در فولدر Migrations، تغییر مورد نظر را یافته و اصلاحات جزئی انجام دهید. اما این کار مستلزم داشتن تجربه کافی در توسعه Code First است و باید با احتیاط عمل کرد.

·       در صورتی که یک ماگریشن اشتباه ایجاد کردید، میتوانید کلاس ماگریشن ایجاد شده را قبل از اجرای کوئری update-database حذف کنید و مجددا ماگریشن صحیح را ایجاد کنید. اما بعد از اجرای کوئری update-database و اعمال تغییرات در پایگاه داده از حذف مایگریشن خودداری کنید.

·       در مواردی که شما قصد دارید تا با معماری مشخصی به پیاده سازی کد بپردازید، ممکن است لایه ای که شامل کلاس Context است، با لایه اپلیکیشن (لایه ای که AppSetting و کلاس program یا startup در آن است) متفاوت باشد. لذا در این گونه موارد، لایه مربوط به اپلیکیشن را از طریق Solution Exploarer به عنوان پروژه دیفالت جهت اجرا مشخص کنید. و در پنجره کنسول، پروژه شامل Context را به عنوان دیفالت انتخاب کنید.

در این مقاله به ایجاد یک پایگاه داده به صورت CodeFirst پرداختیم. قطعا این تمامی مطالب مربوط به CodeFirst نیست و موارد بسیاری وجود دارد که با کسب تجربه در آنها به مهارت خواهید رسید.

مشاهده سورس کد پروژه در گیت هاب(کلیک کنید)

شما همچنین می توانید این مقاله را در وب سایت ویرگول مطالعه نمایید:

مشاهده در ویرگول 

نظر خود را بنویسید.

Theme Skin
Select your color