vault backup: 2025-03-18 12:29:42

This commit is contained in:
2025-03-18 12:29:42 +03:30
parent 8fc1b192b6
commit ba83cb7849
3 changed files with 303 additions and 184 deletions
@@ -0,0 +1,303 @@
## Branching
- [ ] Create the develop branch
- [ ] Create the feature/domain-entities branch based on develop
## `IEntity.cs`
- [ ] Create the IEntity **interface**
> Location: Domain Project > Framework > Interfaces
```C#
public interface IEntity<TKey>
{
public TKey Id { get; set; }
}
```
## `Entity.cs`
- [ ] Create the Entity **class**
> Location: Domain Project > Framework > Base
```C#
public class Entity<TKey> : IEntity<TKey>
{
public TKey Id{ get; set; }
}
```
### Why do we need IEntity and Entity? (Chat GPT):
> this approach is **valid and commonly used** in **Domain-Driven Design (DDD)** and **Clean Architecture**. It provides **consistency**, **reusability**, and **common functionality** across all entities.
## Create Entities
> Location: Domain Project > Aggregates > (RelatedFolder)
### They all (with the exception of join tables) should inherit Entity, and you should specify the datatype of the Id
### Use the latest version of ERD to specify the properties
### You can use this project as a reference:
https://github.com/MehrdadShirvani/AlibabaClone-Backend/tree/develop/AlibabaClone.Domain/Aggregates
### One Example:
```C#
public class Account : Entity<long>
{
public required string PhoneNumber { get; set; }
public required string Password { set; get; }
public string? Email { get; set; }
public long? PersonId { get; set; }
}
```
## Add Navigation Properties
### What is a navigation property?
#### **🔹 Navigation Properties in Entity Framework Core: Everything You Need to Know**
---
#### **📌 What Are Navigation Properties?**
Navigation properties in Entity Framework Core (EF Core) **represent relationships between entities**. They allow you to **navigate** (follow) the relationships between different tables using **C# objects** instead of writing SQL joins manually.
For example, if you have a **Ticket** entity related to a **Buyer**, the navigation property allows you to access the buyer from a ticket without writing a separate SQL query.
---
#### **🔹 Types of Navigation Properties**
Navigation properties can be of two types:
| Relationship Type | Description |
| ---------------------------------- | ----------------------------------------------------------------------------- |
| **Reference Navigation Property** | Represents a **single entity** related to another (one-to-one or many-to-one) |
| **Collection Navigation Property** | Represents **a list of related entities** (one-to-many or many-to-many) |
---
#### **🔹 How to Define Navigation Properties?**
##### **🔹 One-to-Many Example**
A **Buyer** can have **multiple Tickets**, but each **Ticket** belongs to one **Buyer**.
```csharp
public class Buyer
{
public int Id { get; set; }
public string Name { get; set; }
// Navigation Property (One Buyer → Many Tickets)
public virtual ICollection<Ticket> Tickets { get; set; } = new List<Ticket>();
}
public class Ticket
{
public int Id { get; set; }
public int BuyerId { get; set; }
// Navigation Property (Many Tickets → One Buyer)
public virtual Buyer Buyer { get; set; }
}
```
##### **🔹 One-to-One Example**
A **Ticket** can have only **one Transaction**, and a **Transaction** belongs to exactly **one Ticket**.
```csharp
public class Ticket
{
public int Id { get; set; }
// One-to-One Navigation Property
public virtual Transaction Transaction { get; set; }
}
public class Transaction
{
public int Id { get; set; }
public int TicketId { get; set; }
// One-to-One Navigation Property
public virtual Ticket Ticket { get; set; }
}
```
##### **🔹 Many-to-Many Example**
A **Buyer** can buy **many Tickets**, and each **Ticket** can be bought by **many Buyers** (if resale is allowed).
```csharp
public class Buyer
{
public int Id { get; set; }
public string Name { get; set; }
// Many-to-Many Navigation Property
public virtual ICollection<Ticket> Tickets { get; set; } = new List<Ticket>();
}
public class Ticket
{
public int Id { get; set; }
// Many-to-Many Navigation Property
public virtual ICollection<Buyer> Buyers { get; set; } = new List<Buyer>();
}
```
### Add the needed navigation properties inside entities
- [ ] Figure out what navigation properties are needed based on the ERD, and use this project as a reference:
https://github.com/MehrdadShirvani/AlibabaClone-Backend/tree/develop/AlibabaClone.Domain/Aggregates
- [ ] Don't forget to mark all the navigation properties as `virtual`
## Add a NuGet package to Infrastructure Project
- [ ] Add `Microsoft.EntityFrameworkCore.Proxies` to Infrastructure Project
## Create a pull request and merge the current branch with develop
# Side Notes
## **Choosing the right datatype for integer values, specially IDs (Chat GPT):**
### **1️⃣ Integer Data Types (int, short, long) in C# and SQL Server**
| **C# Type** | **SQL Server Type** | **Size** | **Range** |
| ----------- | ------------------- | -------- | ------------------------------------------------------- |
| `byte` | `TINYINT` | 1 byte | 0 to 255 |
| `short` | `SMALLINT` | 2 bytes | -32,768 to 32,767 |
| `int` | `INT` | 4 bytes | -2,147,483,648 to 2,147,483,647 |
| `long` | `BIGINT` | 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
❗ **Important Notes:**
- The **ranges are the same in C# and SQL Server** because both use the same underlying storage.
- `SMALLINT` and `TINYINT` **save space**, but be careful about hitting the limit.
- `BIGINT` is needed only if you expect **billions** of records.
---
### **2️⃣ What to Use for User ID, Ticket ID, Gender ID? (Some examples)**
| **Field** | **Recommended C# Type** | **SQL Server Type** | **Why?** |
| ----------- | ----------------------- | ----------------------- | ----------------------------------------------------------- |
| `UserId` | `int` or `long` | `INT` or `BIGINT` | `INT` is usually enough unless expecting billions of users. |
| `TicketId` | `int` or `long` | `INT` or `BIGINT` | Use `BIGINT` if expecting massive ticket volumes. |
| `GenderId` | `byte` or `short` | `TINYINT` or `SMALLINT` | Gender options are limited, so `TINYINT` is sufficient. |
| `CompanyId` | `int` | `INT` | Companies are limited, `INT` is fine. |
| `VehicleId` | `int` | `INT` | Use `INT`, as vehicle count is manageable. |
| `Price` | `decimal(18,2)` | `DECIMAL(18,2)` | Avoid `float`/`double` due to rounding issues. |
---
### **3️⃣ Should I Use GUIDs for User IDs or Ticket IDs?**
- **Use `GUID` (`UNIQUEIDENTIFIER`) for IDs only if:**
- Data is distributed across multiple databases.
- Security is critical (e.g., preventing sequential guessing of IDs).
- Otherwise, **stick with `int` or `long`** for performance.
📌 **Example in C# (EF Core Model):**
```csharp
public class Ticket
{
public int TicketId { get; set; } // Primary key
public int UserId { get; set; } // Foreign key
public decimal Price { get; set; } // Use decimal for money
public DateTime PurchaseDate { get; set; }
}
```
📌 **Fluent API (SQL Mapping)**
```csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Ticket>()
.Property(t => t.Price)
.HasColumnType("DECIMAL(18,2)");
}
```
## **`required` keyword (Chat GPT) :
C# **IntelliSense** suggests adding `required` to string properties because of **nullable reference types (NRT)** introduced in **C# 8.0+**.
### **1️⃣ What Does `required` Do?**
- `required` **forces initialization** of the property when creating an object.
- It is **not a data annotation** (like `[Required]` in EF Core), but a **C# keyword** that affects **compile-time checks**.
📌 **Example Without `required`**
```csharp
public class User
{
public string Name { get; set; } // Warning: "Non-nullable property 'Name' is uninitialized"
}
```
🔴 **Problem**: The compiler warns that `Name` is not initialized.
✅ **Fix**: Add `required` or initialize the property.
📌 **Example With `required`**
```csharp
public class User
{
public required string Name { get; set; } // No warning
}
```
✅ **Effect**: You **must** provide `Name` when creating a `User` object.
```csharp
var user = new User { Name = "Mehrdad" }; // ✅ Works
var invalidUser = new User(); // ❌ Compilation Error: Name is required
```
## **A note about strings in C# (Chat GPT) :**
**`string` is nullable in C#**, but in **nullable reference types (C# 8+), `string` is treated as non-nullable unless explicitly marked `string?`**.
- **`string`** → Default behavior (non-nullable by default in nullable context).
- **`string?`** → Explicitly nullable.
## **Why virtual navigation properties? (Chat GPT):**
- If you mark a navigation property as `virtual`, EF Core **creates a proxy class** at runtime that overrides the property and loads related data **only when accessed**.
- This is called **Lazy Loading**, meaning data is not fetched until needed.
- If you dont mark it as `virtual`, you **must** load relationships using `.Include()` (Eager Loading).
### **What Type Should Navigation Properties Be?**
| **Scenario** | **Recommended Type** | **Why?** |
| --------------------------------------------------------------------- | ----------------------------- | --------------------------------------------------------------------- |
| **Single reference** (e.g., `Ticket → Transportation`) | `virtual Transportation` | Represents a **one-to-one** or **many-to-one** relationship. |
| **Collection of related entities** (e.g., `Transportation → Tickets`) | `virtual ICollection<Ticket>` | Best for **one-to-many** relationships, supports lazy loading. |
| **Alternative for collections** | `virtual List<Ticket>` | Works the same, but **EF prefers `ICollection<T>`**. |
| **Using `IEnumerable<T>`** | ❌ **Avoid** | EF **does not recognize** `IEnumerable<T>` for navigation properties. |
### **One Scenario to look after if using lazy loading:**
#### **What is the N+1 Query Problem?**
The **N+1 query problem** happens when EF Core **makes too many separate database queries** instead of loading data efficiently.
##### **Example Scenario**
Lets say you have **100 tickets**, and each ticket has a related **Transportation** entity.
You run this code:
```c#
var tickets = context.Tickets.ToList(); // Loads all tickets foreach (var ticket in tickets)
{
Console.WriteLine(ticket.Transportation.Name); // Lazy loads Transportation for each ticket
}
```
##### **What Happens?**
1. **1 Query:** EF Core first loads all `Tickets`.
2. **N Queries:** Then, for each **Ticket**, EF Core makes a separate query to fetch `Transportation` (so 100 additional queries).
3. **Total Queries:** **1 + 100 = 101 queries!** 🚨 **Bad performance!**
---
@@ -1,188 +1,4 @@
# 1. Create the develop branch
# 2. Entities
## Create the feature/domain-entities branch
## Create the IEntity interface and Entity class
## IEntity
> Location: Domain Project > Framework > Interfaces > IEntity.cs
```C#
public interface IEntity<TKey>
{
public TKey Id { get; protected set; }
}
```
## Entity
> Location: Domain Project > Framework > Base > Entity.cs
```C#
public class Entity<TKey> : IEntity<TKey>
{
public TKey Id{ get; protected set; }
}
```
## Why should we do this? (Chat GPT):
> this approach is **valid and commonly used** in **Domain-Driven Design (DDD)** and **Clean Architecture**. It provides **consistency**, **reusability**, and **common functionality** across all entities.
## Create the entities
## Create the entities in Domain > Aggregates > (RelatedFolder)
## They all should inherit Entity
## **Choosing the right datatype for integer values, specially IDs (Chat GPT):**
### **1️⃣ Integer Data Types (int, short, long) in C# and SQL Server**
| **C# Type** | **SQL Server Type** | **Size** | **Range** |
| ----------- | ------------------- | -------- | ------------------------------------------------------- |
| `byte` | `TINYINT` | 1 byte | 0 to 255 |
| `short` | `SMALLINT` | 2 bytes | -32,768 to 32,767 |
| `int` | `INT` | 4 bytes | -2,147,483,648 to 2,147,483,647 |
| `long` | `BIGINT` | 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
❗ **Important Notes:**
- The **ranges are the same in C# and SQL Server** because both use the same underlying storage.
- `SMALLINT` and `TINYINT` **save space**, but be careful about hitting the limit.
- `BIGINT` is needed only if you expect **billions** of records.
---
### **2️⃣ What to Use for User ID, Ticket ID, Gender ID? (Some examples)**
| **Field** | **Recommended C# Type** | **SQL Server Type** | **Why?** |
| ----------- | ----------------------- | ----------------------- | ----------------------------------------------------------- |
| `UserId` | `int` or `long` | `INT` or `BIGINT` | `INT` is usually enough unless expecting billions of users. |
| `TicketId` | `int` or `long` | `INT` or `BIGINT` | Use `BIGINT` if expecting massive ticket volumes. |
| `GenderId` | `byte` or `short` | `TINYINT` or `SMALLINT` | Gender options are limited, so `TINYINT` is sufficient. |
| `CompanyId` | `int` | `INT` | Companies are limited, `INT` is fine. |
| `VehicleId` | `int` | `INT` | Use `INT`, as vehicle count is manageable. |
| `Price` | `decimal(18,2)` | `DECIMAL(18,2)` | Avoid `float`/`double` due to rounding issues. |
---
### **3️⃣ Should I Use GUIDs for User IDs or Ticket IDs?**
- **Use `GUID` (`UNIQUEIDENTIFIER`) for IDs only if:**
- Data is distributed across multiple databases.
- Security is critical (e.g., preventing sequential guessing of IDs).
- Otherwise, **stick with `int` or `long`** for performance.
📌 **Example in C# (EF Core Model):**
```csharp
public class Ticket
{
public int TicketId { get; set; } // Primary key
public int UserId { get; set; } // Foreign key
public decimal Price { get; set; } // Use decimal for money
public DateTime PurchaseDate { get; set; }
}
```
📌 **Fluent API (SQL Mapping)**
```csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Ticket>()
.Property(t => t.Price)
.HasColumnType("DECIMAL(18,2)");
}
```
## **`required` keyword (Chat GPT) :
C# **IntelliSense** suggests adding `required` to string properties because of **nullable reference types (NRT)** introduced in **C# 8.0+**.
### **1️⃣ What Does `required` Do?**
- `required` **forces initialization** of the property when creating an object.
- It is **not a data annotation** (like `[Required]` in EF Core), but a **C# keyword** that affects **compile-time checks**.
📌 **Example Without `required`**
```csharp
public class User
{
public string Name { get; set; } // Warning: "Non-nullable property 'Name' is uninitialized"
}
```
🔴 **Problem**: The compiler warns that `Name` is not initialized.
**Fix**: Add `required` or initialize the property.
📌 **Example With `required`**
```csharp
public class User
{
public required string Name { get; set; } // No warning
}
```
**Effect**: You **must** provide `Name` when creating a `User` object.
```csharp
var user = new User { Name = "Mehrdad" }; // ✅ Works
var invalidUser = new User(); // ❌ Compilation Error: Name is required
```
## **A note about strings in C# (Chat GPT) :**
**`string` is nullable in C#**, but in **nullable reference types (C# 8+), `string` is treated as non-nullable unless explicitly marked `string?`**.
- **`string`** → Default behavior (non-nullable by default in nullable context).
- **`string?`** → Explicitly nullable.
## Add Navigation Properties
## Create `virtual` navigation properties
### **Why virtual? (Chat GPT):**
- If you mark a navigation property as `virtual`, EF Core **creates a proxy class** at runtime that overrides the property and loads related data **only when accessed**.
- This is called **Lazy Loading**, meaning data is not fetched until needed.
- If you dont mark it as `virtual`, you **must** load relationships using `.Include()` (Eager Loading).
### **What Type Should Navigation Properties Be?**
| **Scenario** | **Recommended Type** | **Why?** |
| --------------------------------------------------------------------- | ----------------------------- | --------------------------------------------------------------------- |
| **Single reference** (e.g., `Ticket → Transportation`) | `virtual Transportation` | Represents a **one-to-one** or **many-to-one** relationship. |
| **Collection of related entities** (e.g., `Transportation → Tickets`) | `virtual ICollection<Ticket>` | Best for **one-to-many** relationships, supports lazy loading. |
| **Alternative for collections** | `virtual List<Ticket>` | Works the same, but **EF prefers `ICollection<T>`**. |
| **Using `IEnumerable<T>`** | ❌ **Avoid** | EF **does not recognize** `IEnumerable<T>` for navigation properties. |
### **One Scenario to look after if using lazy loading:**
#### **What is the N+1 Query Problem?**
The **N+1 query problem** happens when EF Core **makes too many separate database queries** instead of loading data efficiently.
##### **Example Scenario**
Lets say you have **100 tickets**, and each ticket has a related **Transportation** entity.
You run this code:
```c#
var tickets = context.Tickets.ToList(); // Loads all tickets foreach (var ticket in tickets)
{
Console.WriteLine(ticket.Transportation.Name); // Lazy loads Transportation for each ticket
}
```
##### **What Happens?**
1. **1 Query:** EF Core first loads all `Tickets`.
2. **N Queries:** Then, for each **Ticket**, EF Core makes a separate query to fetch `Transportation` (so 100 additional queries).
3. **Total Queries:** **1 + 100 = 101 queries!** 🚨 **Bad performance!**
---
## Add `Microsoft.EntityFrameworkCore.Proxies` to Infrastructure Project
## Create a PR and merge the current branch with develop
# 6. Configurations
## Read the documentation:
https://learn.microsoft.com/en-us/ef/core/modeling/
Binary file not shown.