در آموزش سرویس روابط یک به یک در EF Core، یاد میگیریم که چگونه یک Service برای مدیریت روابط یک به یک در EF Core ایجاد کنیم. در قسمت قبلی مدلهای داده را طراحی کردیم. و در این قسمت یک Service Interface و Implementation را برای CRUD Operations ایجاد خواهیم کرد.
برای پیاده سازی یک رابطه یک به یک در قالب یک سرویس بهتر است از دو سرویس مجزا استفاده کنیم. با توجه به پیاده سازی قسمت قبلی ما یک سرویس برای Student و یکی برای StudentCard ایجاد میکنیم.
برای ساخت سرویس روابط یک به یک در EF Core به دو سرویس نیاز داریم. در این بخش سرویس Student را ایجاد میکنیم. برای ساخت آن به یک Interface و Service نیاز داریم.
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);
}
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;
}
}
در این بخش از ساخت سرویس روابط یک به یک در EF Core به سرویس 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);
}
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();
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);
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 از این اطلاعات برای کار با پایگاه داده استفاده میکند.
مثل همیشه عالی