در آموزش ساخت سرویس CRUD در EF Core قصد داریم. یک سرویس ساختاریافته برای مدیریت دادهها در پروژههای خود بسازیم. عملیات CRUD را با حداقل کدنویسی و حداکثر بازدهی در EF Core پیادهسازی کنیم. از ابزارهای Scaffolding و LINQ و AutoMapper برای تسریع توسعه و مدیریت دادهها استفاده کنیم. و کدهای خود را طوری بنویسیم که قابلیت تستپذیری و انعطافپذیری بیشتری داشته باشد.
سرویس بخشی از برنامه است که وظیفه انجام یک کار مشخص یا ارائه یک قابلیت خاص را بر عهده دارد. سرویسها معمولاً منطق اصلی برنامه، مانند پردازش دادهها، ارتباط با پایگاه داده یا اجرای قوانین کسبوکار را مدیریت میکنند و به بخشهای دیگر برنامه (مثل کنترلرها یا رابط کاربری) کمک میکنند تا وظایف خود را سادهتر انجام دهند. هدف اصلی سرویسها، سازماندهی بهتر کد، کاهش تکرار و افزایش قابلیت نگهداری است.
در این آموزش فرض ما این است که میخواهیم معلم ها را در سایت خود ثبت نام کنیم. و روی آن عملیات CRUD انجام دهیم. مثلا لیست معلمان را مشاهده کنیم. قابلیت افزودن، حدف و وبرایش معلم ها را نیز داشته باشیم.
public class Teacher
{
public int Id { get; set; }
public required string Name { get; set; }
public required string Family { get; set; }
public required string Email { get; set; }
}
public class TeacherViewModel
{
public int Id { get; set; }
public required string Name { get; set; }
public required string Family { get; set; }
public required string Email { get; set; }
}
public DbSet<Teacher> Teachers { get; set; }
Add-Migration
Update-Database
CreateMap<Teacher,TeacherViewModel>().ReverseMap();
اینترفیس (Interface) در برنامهنویسی، قراردادی است که مجموعهای از متدها و خواص را تعریف میکند، بدون اینکه پیادهسازی آنها را مشخص کند. اینترفیس به کلاسها یا ساختارها اجازه میدهد از این قرارداد پیروی کنند و متدها یا خواص مشخصشده را طبق نیاز خود پیادهسازی کنند.
public interface ITeacherService
{
Task<List<TeacherViewModel>> GetAllTeacherAsync();
Task<TeacherViewModel?> GetTeacherByIdAsync(int id);
Task<bool> ExistsTeacherAsync(int id);
Task CreateTeacherAsync(TeacherViewModel model);
Task UpdateTeacherAsync(TeacherViewModel model);
Task<bool> DeleteTeacherAsync(int id);
}
public class TeacherService : ITeacherService
{
private readonly MyDbContext _context;
private readonly IMapper _mapper;
private readonly ILogger<TeacherService> _logger;
public TeacherService(MyDbContext context, IMapper mapper, ILogger<TeacherService> logger)
{
_context = context;
_mapper = mapper;
_logger = logger;
}
/////////////////////////////////////////////////////////////
public async Task<List<TeacherViewModel>> GetAllTeacherAsync()
{
try
{
return await _context.Teachers
.ProjectTo<TeacherViewModel>(_mapper.ConfigurationProvider)
.AsNoTracking()
.ToListAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error In GetAllTeacherAsync");
throw new InvalidOperationException("Error In GetAllTeacherAsync", ex);
}
}
/////////////////////////////////////////////////////////////
public async Task<TeacherViewModel?> GetTeacherByIdAsync(int id)
{
try
{
var TeacherVm = await _context.Teachers
.Where(x => x.Id == id)
.AsNoTracking()
.ProjectTo<TeacherViewModel>(_mapper.ConfigurationProvider)
.FirstOrDefaultAsync();
if (TeacherVm == null)
{
throw new KeyNotFoundException($"Teacher with ID {id} not found");
}
return TeacherVm;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error In GetTeacherByIdAsync");
throw new InvalidOperationException("Error In GetTeacherByIdAsync", ex);
}
}
/////////////////////////////////////////////////////////////
public async Task<bool> ExistsTeacherAsync(int id)
{
try
{
return await _context.Teachers
.AsNoTracking()
.AnyAsync(a => a.Id == id);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error In ExistsTeacherAsync");
throw new InvalidOperationException("Error In ExistsTeacherAsync", ex);
}
}
/////////////////////////////////////////////////////////////
public async Task CreateTeacherAsync(TeacherViewModel model)
{
try
{
if (model == null)
{
throw new ArgumentNullException(nameof(model), "The Teacher Model Cannot Be Null.");
}
var TeacherEntity = _mapper.Map<Teacher>(model);
_context.Add(TeacherEntity);
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error In CreateTeacherAsync");
throw new InvalidOperationException("Error In CreateTeacherAsync", ex);
}
}
/////////////////////////////////////////////////////////////
public async Task UpdateTeacherAsync(TeacherViewModel model)
{
try
{
if (model == null)
{
throw new ArgumentNullException(nameof(model), "The Teacher Model Cannot Be Null.");
}
var TeacherEntity = await _context.Teachers.FindAsync(model.Id);
if (TeacherEntity == null)
{
throw new KeyNotFoundException($"Teacher with ID {model.Id} not found.");
}
_mapper.Map(model, TeacherEntity);
_context.Update<Teacher>(TeacherEntity);
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error In UpdateTeacherAsync");
throw new InvalidOperationException("Error In UpdateTeacherAsync", ex);
}
}
/////////////////////////////////////////////////////////////
public async Task<bool> DeleteTeacherAsync(int id)
{
try
{
var TeacherEntity = await _context.Teachers.FindAsync(id);
if (TeacherEntity == null)
{
throw new KeyNotFoundException($"Teacher with ID {id} not found");
}
_context.Remove(TeacherEntity);
await _context.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error In DeleteTeacherAsync");
throw new InvalidOperationException("Error In DeleteTeacherAsync", ex);
}
}
builder.Services.AddScoped<ITeacherService,TeacherService>();
کد های کنترلر
public class TeachersController : Controller
{
private readonly ITeacherService _teacherService;
public TeachersController(ITeacherService teacherService)
{
_teacherService = teacherService;
}
public async Task Index()
{
return View(await _teacherService.GetAllTeacherAsync());
}
public async Task Details(int? id)
{
if (id == null)
{
return NotFound();
}
var teacher =await _teacherService.GetTeacherByIdAsync(id.Value);
return View(teacher);
}
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task Create([Bind("Id,Name,Family,Email")] TeacherViewModel TeacherVm)
{
if (ModelState.IsValid)
{
await _teacherService.CreateTeacherAsync(TeacherVm);
return RedirectToAction(nameof(Index));
}
return View(TeacherVm);
}
public async Task Edit(int? id)
{
if (id == null) return NotFound();
var teacher =await _teacherService.GetTeacherByIdAsync(id.Value);
return View(teacher);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task Edit(int id, [Bind("Id,Name,Family,Email")] TeacherViewModel TeacherVm)
{
if (id != TeacherVm.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
await _teacherService.UpdateTeacherAsync(TeacherVm);
return RedirectToAction(nameof(Index));
}
return View(TeacherVm);
}
public async Task Delete(int? id)
{
if (id == null) return NotFound();
var TeacherVm =await _teacherService.GetTeacherByIdAsync(id.Value);
return View(TeacherVm);
}
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task DeleteConfirmed(int id)
{
bool IsSuccess = await _teacherService.DeleteTeacherAsync(id);
if (IsSuccess) {
return RedirectToAction(nameof(Index));
}
return View();
}
}
استفاده از سرویسها در برنامهنویسی، بهویژه در پروژههایی که از معماریهایی مانند ASP.NET Core و Entity Framework Core استفاده میکنند، دلایل مهمی دارد. در ادامه به دلایل اصلی استفاده از سرویسها اشاره میکنم:
سرویسها به شما کمک میکنند که منطق کسبوکار (Business Logic) را از لایههای دیگر (مانند کنترلرها یا رابط کاربری) جدا کنید. این جداسازی باعث میشود کدهای شما تمیزتر، قابل نگهداریتر و سازمانیافتهتر باشند.
اگر منطق CRUD شما در یک سرویس متمرکز باشد، میتوانید آن را در بخشهای مختلف پروژه مجدد استفاده کنید. مثلاً یک سرویس ProductService میتواند هم در Web API و هم در برنامههای دسکتاپ یا موبایل استفاده شود.
زمانی که عملیات CRUD و منطق کسبوکار در یک سرویس جداگانه پیادهسازی شده باشد، میتوانید با استفاده از ابزارهای Mocking و Unit Testing آن را بهراحتی تست کنید. این کار برای توسعه پروژههای بزرگ بسیار ضروری است.
سرویسها از طریق Dependency Injection (DI) مدیریت میشوند. این روش باعث میشود وابستگیها به صورت انعطافپذیر و بدون نیاز به وابستگی مستقیم به کلاسهای خاص تعریف شوند. این ویژگی باعث کاهش Coupling (وابستگیهای متقابل) بین کلاسها میشود.
وقتی که منطق کسبوکار در یک سرویس متمرکز است، تغییرات یا افزودن قابلیتهای جدید بسیار سادهتر خواهد بود. بهجای جستجو در کل پروژه، فقط باید سرویس مربوطه را تغییر دهید.
سرویسها امکان اضافه کردن لایههای امنیتی را فراهم میکنند. مثلاً میتوانید در سرویس بررسی کنید که کاربر مجاز به انجام عملیات خاصی هست یا نه. این کار امنیت کد شما را افزایش میدهد.
سلام و درود کارتون عالیه
ویدیو جدید رو کی قرار میدین تو سایت؟