diff --git a/02_ProjectOrientedSessions/Session02.md b/02_ProjectOrientedSessions/Session02.md deleted file mode 100644 index fe5c924..0000000 --- a/02_ProjectOrientedSessions/Session02.md +++ /dev/null @@ -1,572 +0,0 @@ - -# 6. Configurations -## Read the documentation: -https://learn.microsoft.com/en-us/ef/core/modeling/ - -## Create the feature/entity-configurations branch based on develop - -## **1. Where Should You Place Configuration Files?** - -βœ… **Best Practice:** Place all configuration files in the **Infrastructure** layer. -πŸ“‚ Suggested Folder: `Infrastructure/Configurations` - -Create configuration classes with this format: `[Entity]Configutaion.cs` -The class should implement the `IEntityTypeConfiguration<[Entity]>` - - -### **Reason:** - -- The **Domain layer** should be **clean** (only entities, no database-related logic). -- The **Infrastructure layer** handles **database interactions**, so configurations belong here. - ---- - -## **2. How to Define Keys (Primary Keys & Identity)** - -You **don’t** need to explicitly define the **primary key (PK)** if you follow EF Core conventions (`Id` or `EntityNameId`). However, if you want to be explicit: - -```csharp -public class TicketConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(t => t.Id); // Explicitly defining PK (optional) - - builder.Property(t => t.Id) - .ValueGeneratedOnAdd(); // Sets Identity (auto-increment) - } -} -``` - -> πŸ›‘ **NOTE:** If you’re using a GUID as the ID, you might need `.ValueGeneratedNever()` instead. - ---- - -## **3. How to Define Foreign Keys?** - -Use `HasOne()` and `WithMany()` for **one-to-many** relationships. - -```csharp -public class TicketConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(t => t.Id); - - // Foreign Key - Ticket to Transportation - builder.HasOne(t => t.Transportation) - .WithMany(tr => tr.Tickets) - .HasForeignKey(t => t.TransportationId) - .OnDelete(DeleteBehavior.Restrict); // Optional: No cascade delete - } -} -``` - ---- - -## **4. How to Introduce Navigation Properties with Different Names?** - -If your navigation property **doesn’t match** the entity name, you should explicitly specify it using `HasOne()` and `WithMany()`. - -### **Example: Ticket has a Buyer (which is an Account)** - -```csharp -builder.HasOne(t => t.Buyer) // Navigation property (Ticket β†’ Account) - .WithMany(a => a.TicketsBought) // Corresponding collection in Account - .HasForeignKey(t => t.BuyerId); -``` - -> **Tip:** If your navigation property names don't match table names, always define them explicitly in the Fluent API. - ---- - -## **5. How to Configure Column Types? (nvarchar, date, etc.)** - -You can **manually specify column types** using `.HasColumnType()`. - -### **All Strings Should Be `nvarchar` with Specific Lengths** - -```csharp -builder.Property(t => t.TicketNumber) - .IsRequired() - .HasMaxLength(20) // Limits nvarchar length - .HasColumnType("nvarchar(20)"); -``` - -### **Store Some DateTime Fields as SQL `DATE` Instead of `DATETIME2`** - -```csharp -builder.Property(t => t.PurchaseDate) - .HasColumnType("date"); // Instead of default "datetime2" -``` - -> πŸš€ **Best Practice:** Always set **string lengths** to avoid `nvarchar(MAX)`, which hurts performance. - ---- - -## **6. How to Define Constraints? (Not Null, Length, etc.)** - -Use `.IsRequired()` for **NOT NULL** and `.HasMaxLength()` for length constraints. - -### **Example: Ticket Number Must Be Unique & Required** - -```csharp -builder.Property(t => t.TicketNumber) - .IsRequired() // NOT NULL - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - -builder.HasIndex(t => t.TicketNumber) - .IsUnique(); // Unique constraint -``` - ---- - -## **Final Configuration File Example (TicketConfiguration.cs)** - -Here’s a **complete** example of a configuration file: - -```csharp -public class TicketConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(t => t.Id); // Primary Key - builder.Property(t => t.Id).ValueGeneratedOnAdd(); // Auto-increment - - // Foreign Key Relationships - builder.HasOne(t => t.Transportation) - .WithMany(tr => tr.Tickets) - .HasForeignKey(t => t.TransportationId) - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(t => t.Buyer) - .WithMany(a => a.TicketsBought) - .HasForeignKey(t => t.BuyerId); - - // Column Types & Constraints - builder.Property(t => t.TicketNumber) - .IsRequired() - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - builder.HasIndex(t => t.TicketNumber) - .IsUnique(); // Unique constraint - - builder.Property(t => t.PurchaseDate) - .HasColumnType("date"); // Store as DATE instead of DATETIME2 - } -} -``` - ---- - -## **Summary & Best Practices** - -βœ… **Store Configuration Files in:** `Infrastructure/Configurations` -βœ… **Define Foreign Keys:** Use `HasOne()` and `WithMany()` -βœ… **Explicitly Define Navigation Properties** if the names differ -βœ… **Column Types:** Use `.HasColumnType()` for `nvarchar`, `date`, etc. -βœ… **Constraints:** Use `.IsRequired()`, `.HasMaxLength()`, `.IsUnique()` - ---- - -Now you’re all set to configure your entities properly! πŸš€ Let me know if you have more questions! 😊 - -## Some special cases - -### **1. Join Tables with Multiple IDs** - -In many-to-many relationships, a join table is created to link two entities. This join table typically contains foreign keys referencing the primary keys of the two entities involved in the relationship. - -#### **Example of a Join Table** - -Suppose we have two entities, `Student` and `Course`, and we want to create a many-to-many relationship between them. We'll create a join table called `StudentCourses`. - -#### **Entities** - -```csharp -public class Student : Entity -{ - public required string Name { get; set; } - public virtual ICollection StudentCourses { get; set; } -} - -public class Course : Entity -{ - public required string Title { get; set; } - public virtual ICollection StudentCourses { get; set; } -} - -public class StudentCourse -{ - public long StudentId { get; set; } - public virtual Student Student { get; set; } - - public long CourseId { get; set; } - public virtual Course Course { get; set; } -} -``` - -#### **Configuration for Join Table** - -You would configure the join table using the Fluent API: - -```csharp -public class StudentCourseConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - // Composite Primary Key - builder.HasKey(sc => new { sc.StudentId, sc.CourseId }); - - // Foreign Key Relationships - builder.HasOne(sc => sc.Student) - .WithMany(s => s.StudentCourses) - .HasForeignKey(sc => sc.StudentId); - - builder.HasOne(sc => sc.Course) - .WithMany(c => c.StudentCourses) - .HasForeignKey(sc => sc.CourseId); - } -} -``` - -#### **OnModelCreating Method in DbContext** - -In your `DbContext`, you would include: - -```csharp -protected override void OnModelCreating(ModelBuilder modelBuilder) -{ - modelBuilder.ApplyConfiguration(new StudentCourseConfiguration()); -} -``` - -#### **Key Points for Join Tables** - -- **Composite Primary Key**: The join table uses a composite key made up of both foreign keys. -- **Navigation Properties**: This enables navigation from `Student` to `Course` and vice versa. - ---- - -### **2. Using GUIDs That Should Be Auto-Generated** - -GUIDs (Globally Unique Identifiers) can be used as primary keys in your entities. In EF Core, you can configure them to auto-generate when a new entity is created. - -#### **Example Entity Using GUID** - -```csharp -public class SomeEntity -{ - public Guid Id { get; set; } = Guid.NewGuid(); // Auto-generate GUID - public string Name { get; set; } -} -``` - -#### **Configuration for GUID** - -When configuring an entity with a GUID as the primary key, you don’t need a specific setup in the configuration, but you can enforce that the `Id` is generated on addition. - -```csharp -builder.Property(e => e.Id) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("NEWSEQUENTIALID()"); // Optionally use NEWID() for random GUID -``` - -#### **How It Works** - -- **`Guid.NewGuid()`** generates a new GUID when a new entity instance is created. -- **Database**: If you use `NEWSEQUENTIALID()` in SQL Server, it generates sequential GUIDs, which can improve indexing performance. - -#### **Example Configuration in DbContext** - -Here's how you might define an entity with GUIDs in your `DbContext`: - -```csharp -public class ApplicationDbContext : DbContext -{ - public DbSet SomeEntities { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity(builder => - { - builder.HasKey(e => e.Id); - builder.Property(e => e.Id) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("NEWSEQUENTIALID()"); - }); - } -} -``` - ---- - - -## Create a PR and merge the current branch with develop -# Summary -### πŸ“Œ **Implementing Database and EF Core in Clean Architecture** - -In **Clean Architecture**, everything should be **modular**, **loosely coupled**, and **separated into layers**. The database-related concerns (entities, repositories, unit of work, configurations, migrations, etc.) belong **primarily** to the **Infrastructure** and **Domain layers**. - ---- - -## πŸ› **Layer Breakdown for Database Implementation** - -|**Layer**|**Responsibility**|**Examples**| -|---|---|---| -|**Domain**|Business rules & entity models|Entities (POCOs), Value Objects, Interfaces| -|**Application**|Business logic & use cases|Services, DTOs, CQRS Handlers| -|**Infrastructure**|Data access, repositories, database configurations|EF Core, Repository, Unit of Work, Configurations| -|**Presentation**|UI/API layer, controllers, endpoints|ASP.NET Core Controllers, Razor Pages, Blazor| - ---- - -## πŸ›  **1️⃣ Defining the Domain Layer (Entities and Interfaces)** - -The **Domain Layer** should contain **only business logic and entity definitions**β€”it should not reference Entity Framework or infrastructure concerns. - -### πŸ“Œ **Entities (Domain/Entities)** - -```csharp -namespace Domain.Entities -{ - public class Ticket - { - public int Id { get; set; } - public string CustomerName { get; set; } = string.Empty; - public DateTime PurchaseDate { get; set; } - public decimal Price { get; set; } - - // Navigation Property - public int CompanyId { get; set; } - public virtual Company Company { get; set; } = null!; - } -} -``` - -```csharp -namespace Domain.Entities -{ - public class Company - { - public int Id { get; set; } - public string Name { get; set; } = string.Empty; - - // Navigation Property - public virtual ICollection Tickets { get; set; } = new List(); - } -} -``` - -### πŸ“Œ **Repository Interfaces (Domain/Repositories)** - -```csharp -namespace Domain.Repositories -{ - public interface IRepository where T : class - { - Task GetByIdAsync(int id); - Task> GetAllAsync(); - Task AddAsync(T entity); - void Remove(T entity); - } -} -``` - -```csharp -namespace Domain.Repositories -{ - public interface IUnitOfWork - { - Task CompleteAsync(); - } -} -``` - ---- - -## πŸ— **2️⃣ Implementing Infrastructure Layer (EF Core, Repositories, Configurations, Migrations)** - -### πŸ“Œ **Database Context (Infrastructure/Persistence/AppDbContext.cs)** - -```csharp -using Domain.Entities; -using Microsoft.EntityFrameworkCore; -using System.Reflection; - -namespace Infrastructure.Persistence -{ - public class AppDbContext : DbContext - { - public AppDbContext(DbContextOptions options) : base(options) { } - - public DbSet Tickets { get; set; } - public DbSet Companies { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); - base.OnModelCreating(modelBuilder); - } - } -} -``` - -### πŸ“Œ **Entity Configurations (Infrastructure/Persistence/Configurations)** - -EF Core Fluent API is best kept in separate configuration classes. - -```csharp -using Domain.Entities; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Infrastructure.Persistence.Configurations -{ - public class TicketConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(t => t.Id); - builder.Property(t => t.CustomerName) - .IsRequired() - .HasMaxLength(100); - builder.Property(t => t.Price) - .HasColumnType("decimal(18,2)"); - - builder.HasOne(t => t.Company) - .WithMany(c => c.Tickets) - .HasForeignKey(t => t.CompanyId); - } - } -} -``` - ---- - -### πŸ“Œ **Repository Pattern (Infrastructure/Persistence/Repositories)** - -```csharp -using Domain.Repositories; -using Microsoft.EntityFrameworkCore; - -namespace Infrastructure.Persistence.Repositories -{ - public class Repository : IRepository where T : class - { - protected readonly AppDbContext _context; - protected readonly DbSet _dbSet; - - public Repository(AppDbContext context) - { - _context = context; - _dbSet = context.Set(); - } - - public async Task GetByIdAsync(int id) => await _dbSet.FindAsync(id); - public async Task> GetAllAsync() => await _dbSet.ToListAsync(); - public async Task AddAsync(T entity) => await _dbSet.AddAsync(entity); - public void Remove(T entity) => _dbSet.Remove(entity); - } -} -``` - -### πŸ“Œ **Unit of Work Implementation** - -```csharp -using Domain.Repositories; -using System.Threading.Tasks; - -namespace Infrastructure.Persistence -{ - public class UnitOfWork : IUnitOfWork - { - private readonly AppDbContext _context; - - public UnitOfWork(AppDbContext context) - { - _context = context; - } - - public async Task CompleteAsync() - { - return await _context.SaveChangesAsync(); - } - } -} -``` - ---- - -## 🌍 **3️⃣ Configuring the Database in ASP.NET Core** - -### πŸ“Œ **Connection String (appsettings.json)** - -```json -{ - "ConnectionStrings": { - "DefaultConnection": "Server=YOUR_SERVER;Database=YourDb;Trusted_Connection=True;MultipleActiveResultSets=true;" - } -} -``` - -### πŸ“Œ **Registering EF Core in `Program.cs`** - -```csharp -using Infrastructure.Persistence; -using Microsoft.EntityFrameworkCore; - -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddDbContext(options => - options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); - -// Register repositories and Unit of Work -builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); -builder.Services.AddScoped(); - -var app = builder.Build(); -app.Run(); -``` - ---- - -## πŸš€ **4️⃣ Migrations and Database Setup** - -### βœ… **Creating Migrations** - -```bash -dotnet ef migrations add InitialCreate --project Infrastructure --startup-project Presentation -``` - -### βœ… **Applying Migrations** - -```bash -dotnet ef database update --project Infrastructure --startup-project Presentation -``` - ---- - -## 🎯 **Summary** - -|**Component**|**Layer**|**Purpose**| -|---|---|---| -|**Entities**|Domain|Business models| -|**Repository Interfaces**|Domain|Data access abstraction| -|**AppDbContext & Configurations**|Infrastructure|EF Core setup & Fluent API| -|**Repositories & UoW**|Infrastructure|Data persistence implementation| -|**Controllers & Services**|Presentation & Application|API logic| -|**Connection String**|AppSettings.json|Database configuration| - ---- - -## πŸ† **Final Thoughts** - -βœ… **This follows Clean Architecture principles** -βœ… **Keeps domain logic independent of EF Core** -βœ… **Repositories and Unit of Work separate infrastructure concerns** -βœ… **DB Context and migrations handled in the right place** - -Would you like to add CQRS or MediatR to this setup? πŸš€ \ No newline at end of file diff --git a/02_ProjectOrientedSessions/Session02/Session 02 Document.md b/02_ProjectOrientedSessions/Session02/Session 02 Document.md new file mode 100644 index 0000000..0289ca6 --- /dev/null +++ b/02_ProjectOrientedSessions/Session02/Session 02 Document.md @@ -0,0 +1,461 @@ + +# 1. Configurations +## Preparation +- [ ] Read the documentation: +https://learn.microsoft.com/en-us/ef/core/modeling/ + +## Branching +- [ ] Create the feature/entity-configurations branch based on develop + +## Where Should You Place Configuration Files? + +βœ… **Best Practice:** Place all configuration files in the **Infrastructure** layer. +πŸ“‚ Suggested Folder: `Infrastructure/Configurations` +### **Reason:** + +- The **Domain layer** should be **clean** (only entities, no database-related logic). +- The **Infrastructure layer** handles **database interactions**, so configurations belong here. + + +## Create configuration classes +- [ ] Create the classes with this format: `[Entity]Configutaion.cs` +- [ ] The class should implement the `IEntityTypeConfiguration<[Entity]>` +## Examples and Details + + +### **Use this as a reference:** +https://github.com/MehrdadShirvani/AlibabaClone-Backend/tree/develop/AlibabaClone.Infrastructure/Configurations +--- + +### **How to Define Keys (Primary Keys & Identity)** + +You **don’t** need to explicitly define the **primary key (PK)** if you follow EF Core conventions (`Id` or `EntityNameId`). However, if you want to be explicit: + +```csharp +public class TicketConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(t => t.Id); // Explicitly defining PK (optional) + + builder.Property(t => t.Id) + .ValueGeneratedOnAdd(); // Sets Identity (auto-increment) + } +} +``` + +> πŸ›‘ **NOTE:** If you’re using a GUID as the ID, you might need `.ValueGeneratedNever()` instead. + +--- + +### **How to Define Foreign Keys?** + +Use `HasOne()` and `WithMany()` for **one-to-many** relationships. + +```csharp +public class TicketConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(t => t.Id); + + // Foreign Key - Ticket to Transportation + builder.HasOne(t => t.Transportation) + .WithMany(tr => tr.Tickets) + .HasForeignKey(t => t.TransportationId) + .OnDelete(DeleteBehavior.Restrict); // Optional: No cascade delete + } +} +``` + +--- + +### **How to Introduce Navigation Properties with Different Names?** + +If your navigation property **doesn’t match** the entity name, you should explicitly specify it using `HasOne()` and `WithMany()`. + +### **Example: Ticket has a Buyer (which is an Account)** + +```csharp +builder.HasOne(t => t.Buyer) // Navigation property (Ticket β†’ Account) + .WithMany(a => a.TicketsBought) // Corresponding collection in Account + .HasForeignKey(t => t.BuyerId); +``` + +> **Tip:** If your navigation property names don't match table names, always define them explicitly in the Fluent API. + +--- + +### **How to Configure Column Types? (nvarchar, date, etc.)** + +You can **manually specify column types** using `.HasColumnType()`. + +### **All Strings Should Be `nvarchar` with Specific Lengths** + +```csharp +builder.Property(t => t.TicketNumber) + .IsRequired() + .HasMaxLength(20) // Limits nvarchar length + .HasColumnType("nvarchar(20)"); +``` + +### **Store Some DateTime Fields as SQL `DATE` Instead of `DATETIME2`** + +```csharp +builder.Property(t => t.PurchaseDate) + .HasColumnType("date"); // Instead of default "datetime2" +``` + +> πŸš€ **Best Practice:** Always set **string lengths** to avoid `nvarchar(MAX)`, which hurts performance. + +--- + +### **How to Define Constraints? (Not Null, Length, etc.)** + +Use `.IsRequired()` for **NOT NULL** and `.HasMaxLength()` for length constraints. + +### **Example: Ticket Number Must Be Unique & Required** + +```csharp +builder.Property(t => t.TicketNumber) + .IsRequired() // NOT NULL + .HasMaxLength(20); + +builder.HasIndex(t => t.TicketNumber) + .IsUnique(); // Unique constraint +``` + +--- + +### **Final Configuration File Example (TicketConfiguration.cs)** + +Here’s a **complete** example of a configuration file: + +```csharp +public class TicketConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(t => t.Id); + + builder.Property(t => t.TransportationId) + .IsRequired(); + + builder.Property(t => t.SeatId) + .IsRequired(); + + builder.Property(t => t.BuyerId) + .IsRequired(); + + builder.Property(t => t.TravelerId) + .IsRequired(); + + builder.Property(t => t.CreatedAt) + .IsRequired(); + + builder.Property(t => t.CompanionId) + .IsRequired(false); + + builder.Property(t => t.TicketStatusId) + .IsRequired(); + + builder.Property(t => t.SerialNumber) + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false); + + builder.Property(t => t.Description) + .HasMaxLength(200) + .IsUnicode(false); + + // Relationships + builder.HasOne(t => t.Transportation) + .WithMany(t => t.Tickets) + .HasForeignKey(t => t.TransportationId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(t => t.Seat) + .WithMany(s => s.Tickets) + .HasForeignKey(t => t.SeatId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(t => t.Buyer) + .WithMany(a => a.BoughtTickets) + .HasForeignKey(t => t.BuyerId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(t => t.Traveler) + .WithMany(p => p.TraveledTickets) + .HasForeignKey(t => t.TravelerId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(t => t.Companion) + .WithMany() + .HasForeignKey(t => t.CompanionId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(t => t.TicketStatus) + .WithMany() + .HasForeignKey(t => t.TicketStatusId) + .OnDelete(DeleteBehavior.Restrict); + } +} +``` + +--- + +### **Summary & Best Practices** + +βœ… **Store Configuration Files in:** `Infrastructure/Configurations` +βœ… **Define Foreign Keys:** Use `HasOne()` and `WithMany()` +βœ… **Explicitly Define Navigation Properties** if the names differ +βœ… **Column Types:** Use `.HasColumnType()` for `nvarchar`, `date`, etc. +βœ… **Constraints:** Use `.IsRequired()`, `.HasMaxLength()`, `.IsUnique()` + +--- + + + +### **1. Join Tables with Multiple IDs** + +In many-to-many relationships, a join table is created to link two entities. This join table typically contains foreign keys referencing the primary keys of the two entities involved in the relationship. + +#### **Example of a Join Table** + +Suppose we have two entities, `Student` and `Course`, and we want to create a many-to-many relationship between them. We'll create a join table called `StudentCourses`. + +#### **Entities** + +```csharp +public class Student : Entity +{ + public required string Name { get; set; } + public virtual ICollection StudentCourses { get; set; } +} + +public class Course : Entity +{ + public required string Title { get; set; } + public virtual ICollection StudentCourses { get; set; } +} + +public class StudentCourse +{ + public long StudentId { get; set; } + public virtual Student Student { get; set; } + + public long CourseId { get; set; } + public virtual Course Course { get; set; } +} +``` + +#### **Configuration for Join Table** + +You would configure the join table using the Fluent API: + +```csharp +public class StudentCourseConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + // Composite Primary Key + builder.HasKey(sc => new { sc.StudentId, sc.CourseId }); + + // Foreign Key Relationships + builder.HasOne(sc => sc.Student) + .WithMany(s => s.StudentCourses) + .HasForeignKey(sc => sc.StudentId); + + builder.HasOne(sc => sc.Course) + .WithMany(c => c.StudentCourses) + .HasForeignKey(sc => sc.CourseId); + } +} +``` + +#### **Key Points for Join Tables** + +- **Composite Primary Key**: The join table uses a composite key made up of both foreign keys. +- **Navigation Properties**: This enables navigation from `Student` to `Course` and vice versa. + +--- + +### **2. Using GUIDs That Should Be Auto-Generated** + +GUIDs (Globally Unique Identifiers) can be used as primary keys in your entities. In EF Core, you can configure them to auto-generate when a new entity is created. + +#### **Example Entity Using GUID** + +```csharp +public class SomeEntity +{ + public Guid Id { get; set; } = Guid.NewGuid(); // Auto-generate GUID + public string Name { get; set; } +} +``` + +#### **Configuration for GUID** + +When configuring an entity with a GUID as the primary key, you don’t need a specific setup in the configuration, but you can enforce that the `Id` is generated on addition. + +```csharp +builder.Property(e => e.Id) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("NEWSEQUENTIALID()"); // Optionally use NEWID() for random GUID +``` + +#### **How It Works** + +- **`Guid.NewGuid()`** generates a new GUID when a new entity instance is created. +- **Database**: If you use `NEWSEQUENTIALID()` in SQL Server, it generates sequential GUIDs, which can improve indexing performance. + +#### **Example Configuration in DbContext** + +Here's how you might define an entity with GUIDs in your `DbContext`: + +```csharp +public class ApplicationDbContext : DbContext +{ + public DbSet SomeEntities { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(builder => + { + builder.HasKey(e => e.Id); + builder.Property(e => e.Id) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("NEWSEQUENTIALID()"); + }); + } +} +``` + +--- + + +## Merge +- [ ] Create a PR and merge the current branch with develop + +# 2. Application DBContext and ConnectionString Configurations + +## Preparation +- [ ] Read the documentation: +https://learn.microsoft.com/en-us/ef/core/modeling/ + +## Branching +- [ ] Create the feature/setup-dbContext branch based on develop + +## Database Context +- [ ] Create ApplicationDBContext + - [ ] Location: Infrastructure/ApplicationDbContext.cs + - [ ] Inherits DbContext + - [ ] Create the constructor like the code below + - [ ] Add the Needed DbSets + - [ ] Override `OnModelCreating` and `OnConfiguring` as below + +```csharp +using AlibabaClone.Domain.Aggregates.AccountAggregates; + +using Microsoft.EntityFrameworkCore; + +namespace AlibabaClone.Infrastructure +{ + public class ApplicationDBContext : DbContext + { + public ApplicationDBContext(DbContextOptions options) : base(options) + { + + } + + public DbSet Accounts { get; set; } + public DbSet AccountRoles { get; set; } + public DbSet Genders{ get; set; } + public DbSet People { get; set; } + public DbSet Roles { get; set; } + + //... Add other DbSets as well + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDBContext).Assembly); + base.OnModelCreating(modelBuilder); + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseLazyLoadingProxies(); + } + } +} + +``` + + + +--- + +## Configuring the Database in ASP.NET Core + +### πŸ“Œ **Connection String ** +- [ ] Modify `appsettings.json` and add the following. (They might have a type or something... search the web to make sure) + +- [ ] Adjust the ConnectionString to meet your needs +### Option 1: +```json +{ + "ConnectionStrings": { + "DefaultConnection": "Server=YOUR_SERVER;Database=YourDb;User Id=USERNAME;Password=PASSWORD;Trusted_Connection=True;" + } +} +``` +### Option 2: +```json +{ + "ConnectionStrings": { + "DefaultConnection": "Server=YOUR_SERVER;Database=YourDb;IntegratedSecurity=TRUE;Trusted_Connection=True;" + } +} +``` +- [ ] Put this `appsettings.json` in **`gitignore`** if you think is needed +### **Registering EF Core in `Program.cs`** +- [ ] Modify `Program.cs` + +```csharp +using Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddDbContext(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); + +// Register repositories and Unit of Work +builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); +builder.Services.AddScoped(); + +var app = builder.Build(); +app.Run(); +``` +## Merge +- [ ] Create a PR and merge the current branch with develop +--- + +# Migrations and Database Setup +## Branching +- [ ] Create the feature/migrations branch based on develop + +## Using Package Manager Console +- [ ] Make sure to set the project to Infrastructure +- [ ] Make sure you have installed Microsoft.EntityFrameworkCore.Tools +- [ ] Run the following command +``` +Add-Migrations InitialCreate +``` +- [ ] In case of scuccues: +``` +Update-Database +``` +## Merge +- [ ] Create a PR and merge the current branch with develop +--- diff --git a/02_ProjectOrientedSessions/Session03/Session 01 Document.md b/02_ProjectOrientedSessions/Session03/Session 01 Document.md new file mode 100644 index 0000000..5dfc701 --- /dev/null +++ b/02_ProjectOrientedSessions/Session03/Session 01 Document.md @@ -0,0 +1,77 @@ + +### πŸ“Œ **Repository Interfaces (Domain/Repositories)** + +```csharp +namespace Domain.Repositories +{ + public interface IRepository where T : class + { + Task GetByIdAsync(int id); + Task> GetAllAsync(); + Task AddAsync(T entity); + void Remove(T entity); + } +} +``` + +```csharp +namespace Domain.Repositories +{ + public interface IUnitOfWork + { + Task CompleteAsync(); + } +} +``` + +### πŸ“Œ **Repository Pattern (Infrastructure/Persistence/Repositories)** + +```csharp +using Domain.Repositories; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Persistence.Repositories +{ + public class Repository : IRepository where T : class + { + protected readonly AppDbContext _context; + protected readonly DbSet _dbSet; + + public Repository(AppDbContext context) + { + _context = context; + _dbSet = context.Set(); + } + + public async Task GetByIdAsync(int id) => await _dbSet.FindAsync(id); + public async Task> GetAllAsync() => await _dbSet.ToListAsync(); + public async Task AddAsync(T entity) => await _dbSet.AddAsync(entity); + public void Remove(T entity) => _dbSet.Remove(entity); + } +} +``` + +### πŸ“Œ **Unit of Work Implementation** + +```csharp +using Domain.Repositories; +using System.Threading.Tasks; + +namespace Infrastructure.Persistence +{ + public class UnitOfWork : IUnitOfWork + { + private readonly AppDbContext _context; + + public UnitOfWork(AppDbContext context) + { + _context = context; + } + + public async Task CompleteAsync() + { + return await _context.SaveChangesAsync(); + } + } +} +```