From ebf100a5b4dcc575affe35be1390e0f5410573ed Mon Sep 17 00:00:00 2001 From: Mehrdad Shirvani Date: Fri, 28 Mar 2025 13:40:37 +0330 Subject: [PATCH] vault backup: 2025-03-28 13:40:37 --- .../Session03/Session 03 Document.md | 282 ++++++------------ 1 file changed, 90 insertions(+), 192 deletions(-) diff --git a/02_ProjectOrientedSessions/Session03/Session 03 Document.md b/02_ProjectOrientedSessions/Session03/Session 03 Document.md index 1b06e86..315dd15 100644 --- a/02_ProjectOrientedSessions/Session03/Session 03 Document.md +++ b/02_ProjectOrientedSessions/Session03/Session 03 Document.md @@ -1,4 +1,4 @@ -# Introduction to Repository Pattern +# Introduction to Repository Pattern (Chat GPT): The **Repository Pattern** in ASP.NET Core is a design pattern used to separate business logic from data access logic by providing an abstraction layer over database operations. This pattern improves maintainability, testability, and flexibility in applications by encapsulating database operations in dedicated repository classes. --- @@ -7,23 +7,18 @@ The **Repository Pattern** in ASP.NET Core is a design pattern used to separate ### **Pros:** 1. **Abstraction from ORM (Entity Framework Core)** - - Prevents direct dependency on EF Core, making it easier to swap out the data access layer in the future. 2. **Better Code Organization** - - Separates concerns by keeping data logic in repositories and business logic in services/controllers. 3. **Improved Testability** - - Makes it easier to mock repositories in unit tests. 4. **Encapsulation of Queries** - - Common queries can be abstracted, reducing repetition. 5. **Centralized Data Access Logic** - - Ensures a single location for handling CRUD operations. @@ -31,16 +26,16 @@ The **Repository Pattern** in ASP.NET Core is a design pattern used to separate ## **Comparison: Repository Pattern vs. Direct DbSet Operations** -|Feature|Using Repository Pattern|Using DbSet Directly in Controllers| -|---|---|---| -|**Separation of Concerns**|✅ Maintains separation|❌ Business and data access logic mixed| -|**Testability**|✅ Easy to mock and test|❌ Harder to mock DbContext| -|**Code Reusability**|✅ Common operations are encapsulated|❌ Repetitive DbSet calls| -|**Flexibility**|✅ Can switch database providers easily|❌ Tightly coupled to EF Core| +| Feature | Using Repository Pattern | Using DbSet Directly in Controllers | +| -------------------------- | -------------------------------------- | -------------------------------------- | +| **Separation of Concerns** | ✅ Maintains separation | ❌ Business and data access logic mixed | +| **Testability** | ✅ Easy to mock and test | ❌ Harder to mock DbContext | +| **Code Reusability** | ✅ Common operations are encapsulated | ❌ Repetitive DbSet calls | +| **Flexibility** | ✅ Can switch database providers easily | ❌ Tightly coupled to EF Core | --- -## **Implementation of the Repository Pattern with Unit of Work** +## **Implementation of the Repository Pattern** We will create: @@ -48,192 +43,95 @@ We will create: 2. **Repository** (Generic repository implementation) -3. **IUnitOfWork** (Handles transaction management) - -4. **UnitOfWork** (Implementation for database commit operations) - -5. **Entity-specific repositories** (e.g., `ICustomerRepository` and `CustomerRepository`) +3. **Entity-specific repositories** (e.g., `ICustomerRepository` and `CustomerRepository`) + + +# Introduction to Unit of Work Pattern (Chat GPT): + +## **What is the Unit of Work Pattern?** + +The **Unit of Work (UoW)** pattern is a **centralized mechanism** to manage **database transactions** and ensure that multiple repository operations are treated as a single unit of execution. It acts as a wrapper around multiple repositories to **coordinate their changes and commit them in one go**. + +--- + +## **Advantages of Unit of Work** + +### **1. Single Transaction for Multiple Operations** + +- If you're performing **multiple database operations** across different repositories, **Unit of Work ensures atomicity**. +- If one operation fails, everything is **rolled back** (when using explicit transactions). + +### **2. Better Performance** + +- **Without UoW:** Every repository would call `SaveChangesAsync()` separately, causing multiple round trips to the database. +- **With UoW:** All changes are saved **at once**, reducing the number of database calls. +### **3. Maintains Consistency** +- When multiple repositories modify related entities, **UoW ensures that all changes are either committed or discarded together**. +### **4. Improves Testability** +- Unit of Work allows you to **mock database changes** and write unit tests efficiently without worrying about inconsistent data states. +### **5. Prevents Partial Updates** +- If multiple repositories handle different entities in the same operation, calling `SaveChangesAsync()` in individual repositories could lead to **partial updates** if one operation succeeds and another fails. + +--- + +## **Why Should `SaveChanges()` NOT Be in the Repository?** + +### **1. Each Repository Should Not Control Transactions** + +If each repository calls `SaveChangesAsync()`, **you lose control over transactions**. + +#### **Example Problem (Without UoW)** + +Imagine you have two repositories: `CustomerRepository` and `OrderRepository`. +If you try to **add a customer** and **add an order** separately, each calling `SaveChangesAsync()`: + +```csharp +var customer = new Customer { Name = "John Doe" }; +await _customerRepository.AddAsync(customer); +await _customerRepository.SaveChangesAsync(); // ❌ First database call + +var order = new Order { CustomerId = customer.Id, TotalAmount = 100 }; +await _orderRepository.AddAsync(order); +await _orderRepository.SaveChangesAsync(); // ❌ Second database call +``` + +**What happens if the second `SaveChangesAsync()` fails?** + +- The customer has already been saved, but the order is missing. +- **Your database is left in an inconsistent state!** + +### **2. Database Round Trips (Performance Issue)** + +If each repository calls `SaveChangesAsync()`, you end up with **multiple database calls** instead of batching them into a single transaction. + +```csharp +await _customerRepository.SaveChangesAsync(); // ❌ DB call +await _orderRepository.SaveChangesAsync(); // ❌ Another DB call +``` + +Using **Unit of Work**, all changes can be saved in one go: + +```csharp +await _unitOfWork.SaveChangesAsync(); // ✅ One database call +``` + +This **reduces network latency** and improves **database performance**. +### **3. Promotes Separation of Concerns** +- **Repositories should focus on CRUD operations** (data retrieval and manipulation). +- **Unit of Work should manage transactions**. +- This makes the code **cleaner and easier to maintain**. --- -### **Step 1: Create a Generic Repository Interface** - -```csharp -public interface IRepository where T : class -{ - Task GetByIdAsync(int id); - Task> GetAllAsync(); - Task AddAsync(T entity); - void Update(T entity); - void Delete(T entity); -} -``` +## **Key Takeaways** +✔ **Unit of Work ensures all database operations are part of a single transaction**. +✔ **Repositories should NOT call `SaveChangesAsync()` to avoid multiple transactions**. +✔ **EF Core tracks changes, so calling `SaveChangesAsync()` once is enough**. +✔ **Using UoW improves performance, consistency, and maintainability**. --- -### **Step 2: Implement the Generic Repository** - -```csharp -public class Repository : IRepository where T : class -{ - protected readonly DbContext _context; - protected readonly DbSet _dbSet; - - public Repository(DbContext context) - { - _context = context; - _dbSet = _context.Set(); - } - - public async Task GetByIdAsync(int id) - { - return await _dbSet.FindAsync(id); - } - - public async Task> GetAllAsync() - { - return await _dbSet.ToListAsync(); - } - - public async Task AddAsync(T entity) - { - await _dbSet.AddAsync(entity); - } - - public void Update(T entity) - { - _dbSet.Update(entity); - } - - public void Delete(T entity) - { - _dbSet.Remove(entity); - } -} -``` - ---- - -### **Step 3: Create an Entity-Specific Repository Interface** - -```csharp -public interface ICustomerRepository : IRepository -{ - Task> GetCustomersWithOrdersAsync(); -} -``` - ---- - -### **Step 4: Implement the Entity-Specific Repository** - -```csharp -public class CustomerRepository : Repository, ICustomerRepository -{ - public CustomerRepository(DbContext context) : base(context) - { - } - - public async Task> GetCustomersWithOrdersAsync() - { - return await _dbSet.Include(c => c.Orders).ToListAsync(); - } -} -``` - ---- - -### **Step 5: Create the Unit of Work Interface** - -```csharp -public interface IUnitOfWork : IDisposable -{ - ICustomerRepository Customers { get; } - Task SaveChangesAsync(); -} -``` - ---- - -### **Step 6: Implement the Unit of Work** - -```csharp -public class UnitOfWork : IUnitOfWork -{ - private readonly DbContext _context; - private CustomerRepository _customerRepository; - - public UnitOfWork(DbContext context) - { - _context = context; - } - - public ICustomerRepository Customers => - _customerRepository ??= new CustomerRepository(_context); - - public async Task SaveChangesAsync() - { - return await _context.SaveChangesAsync(); - } - - public void Dispose() - { - _context.Dispose(); - } -} -``` - ---- - -### **Step 7: Register Dependencies in `Program.cs`** - -```csharp -builder.Services.AddScoped(); -builder.Services.AddScoped(); -``` - ---- - -### **Step 8: Use in a Service or Controller** - -```csharp -public class CustomerService -{ - private readonly IUnitOfWork _unitOfWork; - - public CustomerService(IUnitOfWork unitOfWork) - { - _unitOfWork = unitOfWork; - } - - public async Task> GetAllCustomersAsync() - { - return await _unitOfWork.Customers.GetAllAsync(); - } - - public async Task AddCustomerAsync(Customer customer) - { - await _unitOfWork.Customers.AddAsync(customer); - await _unitOfWork.SaveChangesAsync(); - } -} -``` - ---- - -## **Conclusion** - -- The **Repository Pattern** with **Unit of Work** ensures clean architecture, better maintainability, and testability. - -- It abstracts **DbSet** operations and allows for easier database switching. - -- The **Unit of Work** manages transactional consistency by coordinating multiple repositories. - - -Would you like modifications, such as adding specifications or pagination? 🚀 - -# Introduction to Unit of Work Pattern # 1. Implementing Repository Pattern ## Branching - [ ] Create the feature/repositories branch based on develop