سرویس‌های حرفه‌ای برای روابط یک به یک در EF Core

سرویس‌های حرفه‌ای برای روابط یک به یک در EF Core

در آموزش سرویس روابط یک به یک در EF Core، یاد می‌گیریم که چگونه یک Service برای مدیریت روابط یک به یک در EF Core ایجاد کنیم. در قسمت قبلی مدل‌های داده را طراحی کردیم. و در این قسمت یک Service Interface و Implementation را برای CRUD Operations ایجاد خواهیم کرد.

عملیات CRUD و سرویس روابط یک به یک در EF Core

برای پیاده سازی یک رابطه یک به یک در قالب یک سرویس بهتر است از دو سرویس مجزا استفاده کنیم. با توجه به پیاده سازی قسمت قبلی ما یک سرویس برای Student و یکی برای StudentCard ایجاد میکنیم.

روابط یک به یک در EF Core

روابط یک به یک در EF Core

ساخت سرویس CRUD برای Student

برای ساخت سرویس روابط یک به یک در EF Core به دو سرویس نیاز داریم. در این بخش سرویس Student را ایجاد میکنیم. برای ساخت آن به یک Interface و Service نیاز داریم.

  • ایجاد Interface دانشجو – Student
public interface IStudentService
{
    public Task<List<StudentViewModel>> GetAllStudentsAsync();
    public Task<StudentViewModel> GetStudentByIdAsync(int Id);
    public Task<bool> CreateStudentAsync(StudentViewModel studentVm);
    public Task<bool> UpdateStudentAsync(StudentViewModel studentVm);
    public Task<bool> DeleteStudentAsync(int Id);
}
  • ایجاد Service دانشجو – Student
public class StudentService : IStudentService
{
    private readonly MyDbContext _context;
    private readonly IMapper _mapper;
    /// <summary>
    /// سازنده سرویس دانشجو
    /// </summary>
    /// <param name="context">کانتکست دیتابیس</param>
    /// <param name="mapper">مپر</param>
    public StudentService(MyDbContext context, IMapper mapper)
    {
        _mapper = mapper;
        _context = context;
    }
    /// <summary>
    /// دریافت لیست همه دانشجویان
    /// </summary>
    /// <returns>لیست مدل نمایشی دانشجویان</returns>
    public async Task<List<StudentViewModel>> GetAllStudentsAsync()
    {
        return await _context.students
            .Include(s => s.StudentCard)
            .ProjectTo<StudentViewModel>(_mapper.ConfigurationProvider)
            .AsNoTracking()
            .ToListAsync();
    }
    /// <summary>
    /// دریافت دانشجو بر اساس شناسه
    /// </summary>
    /// <param name="Id">شناسه دانشجو</param>
    /// <returns>مدل نمایشی دانشجو</returns>
    public async Task<StudentViewModel> GetStudentByIdAsync(int Id)
    {
        var student = await _context.students
            .ProjectTo<StudentViewModel>(_mapper.ConfigurationProvider)
            .AsNoTracking()
            .FirstOrDefaultAsync(a => a.Id == Id);
        if (student == null) throw new KeyNotFoundException($"Student with id : {Id} not found");
        return student;
    }
    /// <summary>
    /// ایجاد دانشجوی جدید
    /// </summary>
    /// <param name="studentVm">مدل نمایشی دانشجو</param>
    /// <returns>وضعیت موفقیت عملیات</returns>
    public async Task<bool> CreateStudentAsync(StudentViewModel studentVm)
    {
        if (studentVm == null || studentVm.Id != 0)
            throw new ArgumentException("Invalid student data.");
        var studentEntity = _mapper.Map<Student>(studentVm);
        _context.Add(studentEntity);
        await _context.SaveChangesAsync();
        return true;
    }
    /// <summary>
    /// به‌روزرسانی اطلاعات دانشجو
    /// </summary>
    /// <param name="studentVm">مدل نمایشی دانشجو</param>
    /// <returns>وضعیت موفقیت عملیات</returns>
    public async Task<bool> UpdateStudentAsync(StudentViewModel studentVm)
    {
        if (studentVm == null || studentVm.Id == 0)
            throw new ArgumentException(nameof(studentVm), "The Student Model Cannot Be Null");
        var studentEntity = await _context.students.FindAsync(studentVm.Id);
        if (studentEntity == null)
            throw new KeyNotFoundException($"Student With Id : {studentVm.Id} Not Found.");
        _mapper.Map(studentVm, studentEntity);
        await _context.SaveChangesAsync();
        return true;
    }
    /// <summary>
    /// حذف دانشجو بر اساس شناسه
    /// </summary>
    /// <param name="Id">شناسه دانشجو</param>
    /// <returns>وضعیت موفقیت عملیات</returns>
    public async Task<bool> DeleteStudentAsync(int Id)
    {
        var studentEntity = await _context.students.FindAsync(Id);
        if (studentEntity == null)
            throw new KeyNotFoundException($"Student With id : {Id} Not Found");
        _context.Remove(studentEntity);
        await _context.SaveChangesAsync();
        return true;
    }
}

ساخت سرویس CRUD برای StudentCard

در این بخش از ساخت سرویس روابط یک به یک در EF Core به سرویس StudentCard میپردازیم.

  • ایجاد Interface کارت دانشجویی – StudentCard
public interface IStudentCardService
{
    public Task<StudentCardViewModel> GetCardByIdAsync(int id);
    public Task<bool> CreateCardAsync(StudentCardViewModel studentCardVm);
    public Task<bool> UpdateCardAsync(StudentCardViewModel studentCardVm);
    public Task<bool> DeleteCardAsync(int Id);
}
  • ایجاد Service کارت دانشجویی – StudentCard
public class StudentCardService : IStudentCardService
{
    private readonly MyDbContext _context;
    private readonly IMapper _mapper;
    /// <summary>
    /// سازنده سرویس کارت دانشجو
    /// </summary>
    /// <param name="context">کانتکست دیتابیس</param>
    /// <param name="mapper">مپر</param>
    public StudentCardService(MyDbContext context, IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }
    /// <summary>
    /// دریافت کارت دانشجویی با شناسه
    /// </summary>
    /// <param name="id">شناسه کارت دانشجویی</param>
    /// <returns>مدل نمای کارت دانشجویی</returns>
    public async Task<StudentCardViewModel> GetCardByIdAsync(int id)
    {
        var studentCardVm = await _context.studentCards
            .AsNoTracking()
            .ProjectTo<StudentCardViewModel>(_mapper.ConfigurationProvider)
            .FirstOrDefaultAsync(s => s.Id == id);
        if (studentCardVm == null)
            throw new KeyNotFoundException($"StudentCard With Id : {id} Not Found.");
        return studentCardVm;
    }
    /// <summary>
    /// ایجاد کارت دانشجویی جدید
    /// </summary>
    /// <param name="studentCardVm">مدل نمای کارت دانشجویی</param>
    /// <returns>وضعیت موفقیت عملیات</returns>
    public async Task<bool> CreateCardAsync(StudentCardViewModel studentCardVm)
    {
        if (studentCardVm == null || studentCardVm.Id != 0)
            throw new ArgumentException("Invalid student card data.");
        if (!await _context.students.AnyAsync(s => s.Id == studentCardVm.StudentId))
            throw new KeyNotFoundException($"Student With Id : {studentCardVm.Id} Not Found.");
        var studentEntity = _mapper.Map<StudentCard>(studentCardVm);
        _context.Add(studentEntity);
        await _context.SaveChangesAsync();
        return true;
    }
    /// <summary>
    /// به‌روزرسانی کارت دانشجویی
    /// </summary>
    /// <param name="studentCardVm">مدل نمای کارت دانشجویی</param>
    /// <returns>وضعیت موفقیت عملیات</returns>
    public async Task<bool> UpdateCardAsync(StudentCardViewModel studentCardVm)
    {
        if (studentCardVm == null || studentCardVm.Id == 0)
            throw new ArgumentException(nameof(studentCardVm), "The StudentCard Model Cannot Be Null");
        var studentCardEntity = await _context.studentCards
            .FindAsync(studentCardVm.Id);
        if (studentCardEntity == null)
            throw new KeyNotFoundException($"StudentCard With Id : {studentCardVm.StudentId} Not Found.");
        _mapper.Map(studentCardVm, studentCardEntity);
        await _context.SaveChangesAsync();
        return true;
    }
    /// <summary>
    /// حذف کارت دانشجویی
    /// </summary>
    /// <param name="Id">شناسه کارت دانشجویی</param>
    /// <returns>وضعیت موفقیت عملیات</returns>
    public async Task<bool> DeleteCardAsync(int Id)
    {
        var studentCardEntity = await _context.studentCards.FindAsync(Id);
        if (studentCardEntity == null)
            throw new KeyNotFoundException($"Student Card With id : {Id} Not Found");
        _context.Remove(studentCardEntity);
        await _context.SaveChangesAsync();
        return true;
    }
}

جلوگیری ار بروزرسانی همراه با مقدار دهی اولیه یک فیلد

  • تنظیم مقدار پیش‌فرض هنگام ایجاد رکورد

HasDefaultValueSql(“GETDATE()”) مقدار پیش‌فرض را به GETDATE() در SQL Server تنظیم می‌کند. به این معنی که وقتی یک StudentCard جدید ایجاد شود، مقدار IssueDate به‌طور خودکار مقدار تاریخ و زمان فعلی را دریافت می‌کند.

ValueGeneratedOnAdd(); مشخص می‌کند که این مقدار در هنگام درج (INSERT) به‌صورت خودکار مقداردهی شود و نیازی به مقداردهی دستی در کد برنامه نباشد

modelBuilder.Entity<StudentCard>()
    .Property(sc => sc.IssueDate)
    .HasDefaultValueSql("GETDATE()")
    .ValueGeneratedOnAdd();
  • جلوگیری از تغییر هنگام ذخیره رکورد (UPDATE)

SetBeforeSaveBehavior(PropertySaveBehavior.Ignore) این قابلیت به EF Core می‌گوید که قبل از ذخیره (SaveChanges()) هر تغییری روی IssueDate را نادیده بگیرد. به این معنی که اگر در جایی از برنامه بخواهید مقدار IssueDate را هنگام Update تغییر دهید، EF Core این مقدار را در پایگاه داده ذخیره نخواهد کرد و مقدار قبلی حفظ می‌شود.

modelBuilder.Entity<StudentCard>()
    .Property(sc => sc.IssueDate)
    .Metadata.SetBeforeSaveBehavior(Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Ignore);
  • جلوگیری از تغییر بعد از ذخیره رکورد (UPDATE)

SetAfterSaveBehavior(PropertySaveBehavior.Ignore) این قابلیت به EF Core می‌گوید که بعد از ذخیره داده‌ها (SaveChanges()) مقدار IssueDate را تغییر ندهد.

modelBuilder.Entity<StudentCard>()
    .Property(sc => sc.IssueDate)
    .Metadata.SetAfterSaveBehavior(Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Ignore);
  • SetBeforeSaveBehavior.Ignore مقدار قبل از ذخیره شدن در UPDATE نادیده گرفته می‌شود.
  • SetAfterSaveBehavior.Ignore مقدار پس از ذخیره شدن دیگر به‌روزرسانی نمی‌شود.
  • Metadata در EF Core یعنی اطلاعاتی که درباره‌ی ویژگی‌های جدول‌ها و فیلدها ذخیره می‌شود. مشخص می‌کند .که یک مقدار پیش‌فرض داشته باشد. تغییر کند یا نه. و نوع داده آن چیست. EF Core از این اطلاعات برای کار با پایگاه داده استفاده می‌کند.

مشاهده در آپارات یا یوتیوب

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

تماشای ویدیوی سرویس‌های حرفه‌ای برای روابط یک به یک در EF Core در آپارات و یوتیوب

اشتراک‌گذاری با یک کلیک

با یک کلیک، محتوای این صفحه را در شبکه‌های اجتماعی، ایمیل یا پیام‌رسان‌های مورد علاقه‌تان به‌سرعت به اشتراک بگذارید.

اشتراک‌گذاری صفحه سرویس‌های حرفه‌ای برای روابط یک به یک در EF Core در شبکه‌های اجتماعی

نظرات ارزشمند شما

نظرات شما برای ما ارزشمند است و آنها را میخوانیم و پاسخ میدهیم.

تصویر مرتضی فاضل

مثل همیشه عالی

تصویر محمدرضا احدیان

ممتون از شما دوست گرامی ❤️❤️❤️

تصویر alamir

سلام
چرا در اینجا از
context.update(studentM)
استفاده نکردیم؟

تصویر عرفانی

سلام بر استاد عزیز، ضمن تشکر از زحمات شما، ممنون میشم در مورد Generic Repository هم توضیح بدهید.

تصویر محمدرضا

باسلام و تشکر از آموزش خوبتون.
می خواستم بدونم در سرویس StudentCard چه جوری مقدار فیلد Id از Student در فیلد StudentId ُاز SudentCard ذخیره می شود. توی متد Create که برای سرویس StudentCard نوشتید من ندیدم که مقدار فیلد Id از Student در فیلد StudentId قرار بگیرد. لطفاً می شود در این مورد کمی توضیح بفرمایید.
با تشکر فراوان

تصویر محمدرضا احدیان

لطفا تماس بگیرید متوجه منظورتان نشدم.

تصویر محمدرضا احدیان

سلام بله حتما به آن میپردازیم.

فرم افزودن دیدگاه

با ارسال نظرات خود ما را در ایجاد محتوای بهتر کمک کنید.

با خدمات تهران آی تی آشنا شوید

تهران آی تی با افتخار خدمات زیر را ارائه می‌دهد.