# EF Core — DbContext, DbSet ו-Migrations📖 שיעור

## מה זה?

בשיעור הקודם תכננו את הסכמה וכתבנו `CREATE TABLE` ידני. אבל בקוד C# בפועל, נרצה לעבוד עם אובייקטים (`Student`, `Grade`) לא עם SQL גולמי בכל מקום. EF Core (Entity Framework Core) הוא **ORM** (Object-Relational Mapper) — ממפה בין מחלקות C# לטבלאות SQL אוטומטית, ו-Migrations בונות/מעדכנות את מבנה ה-DB **מתוך הקוד עצמו** ("Code First"), בלי לכתוב `CREATE TABLE` ידני בנפרד.

השיעור הזה שונה מהשיעורים הקודמים בדבר אחד חשוב: הוא לא נגמר בקטע קוד לדוגמה. הוא מתחיל מ"אין כלום", ומסתיים ב-API אמיתי שרץ על המחשב שלכם ומדבר עם מסד נתונים אמיתי — כולל להרים את מסד הנתונים עצמו עם Docker (שכבר למדתם ביחידה הקודמת), ולבדוק את הכול בפועל עם שני endpoints. כל שלב בונה על השלב שלפניו — כדאי לעבור אותם לפי הסדר, לא לדלג.

## מילות מפתח שחשוב לזכור

• ORM (Object-Relational Mapper) — שכבה שממפה אוטומטית בין אובייקטים ב-C# לשורות בטבלאות SQL

• NuGet Package — ספריית קוד חיצונית שמוסיפים לפרויקט .NET (המקביל ל-`npm install` ב-Node) — EF Core הוא לא חלק מובנה ב-.NET, אלא Package כזה

• `DbContext` — המחלקה המרכזית של EF Core; מייצגת "חיבור" לוגי למסד הנתונים כולו

• `DbSet<T>` — Property בתוך `DbContext` שמייצג טבלה בודדת (`DbSet<Student>` = טבלת Students)

• Connection String — מחרוזת טקסט שאומרת לתוכנית איך להגיע למסד הנתונים: איפה הוא (כתובת+פורט), שמו, ואיך מתחברים אליו (משתמש+סיסמה)

• Migration — קובץ שמתעד שינוי במבנה ה-DB (הוספת עמודה, טבלה חדשה), שאפשר להחיל בפועל על ה-DB

## בונים מאפס — Docker, DbContext, Migrations, ושני Endpoints שרצים באמת

### שלב 1: מוסיפים את EF Core לפרויקט — NuGet Packages

עד עכשיו כל מה שהשתמשנו בו הגיע מובנה בתוך .NET — לא היינו צריכים "להתקין" שום דבר נוסף. EF Core שונה: הוא **ספרייה חיצונית**, שצריך להוסיף במפורש לפרויקט לפני שאפשר להשתמש בה:

```bash
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
```

`dotnet add package <שם>` מוריד ספרייה (NuGet Package) ומוסיף אותה לפרויקט — בדיוק כמו `npm install` בעולם ה-JavaScript. הראשונה נותנת ל-EF Core את היכולת לדבר עם SQL Server ספציפית; השנייה נדרשת כדי שהכלים של Migrations (שנגיע אליהם בשלב 6) יעבדו בכלל.

חוץ מזה, צריך גם **כלי שורת-פקודה** גלובלי, בנפרד מהפרויקט עצמו — לא קוד שהתוכנית שלכם משתמשת בו, אלא פקודה שמריצים מהטרמינל:

```bash
dotnet tool install --global dotnet-ef
```

מתקינים את זה **פעם אחת** על המחשב (לא לכל פרויקט מחדש) — זה מה שנותן לכם את הפקודה `dotnet ef` שנשתמש בה בשלב 6.

### שלב 2: מרימים מסד נתונים אמיתי — SQL Server בתוך Docker

עד עכשיו לא היה לנו שום מסד נתונים אמיתי להתחבר אליו. פותרים את זה עם בדיוק אותו Docker שכבר תרגלתם:

```bash
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=YourStrong@Passw0rd" -p 1433:1433 --name sql-server -d mcr.microsoft.com/mssql/server:2022-latest
```

חלק מהדגלים כבר מוכרים לכם מיחידת ה-Docker, וחלק חדשים:

- `mcr.microsoft.com/mssql/server:2022-latest` — ה-**Image** להרצה (בדיוק כמו `nginx` או `hello-world` שכבר הרצתם) — הפעם, Image רשמי של מיקרוסופט שמכיל SQL Server מוכן לשימוש.
- `-p 1433:1433` — מיפוי פורטים, בדיוק כמו `-p 8080:80` שכבר תרגלתם: `1433` הוא הפורט הסטנדרטי שבו SQL Server מאזין. אחרי השורה הזו, `localhost:1433` על המחשב שלכם מוביל ל-SQL Server **בתוך** הקונטיינר.
- `-e "ACCEPT_EULA=Y"` ו-`-e "MSSQL_SA_PASSWORD=..."` — **חדש**: `-e` מעביר **משתנה סביבה** (Environment Variable) לתוך הקונטיינר — דרך נפוצה מאוד להעביר הגדרות ל-Image בלי לערוך קבצים. ה-Image הזה "יודע לקרוא" שני משתנים ספציפיים: `ACCEPT_EULA=Y` (מאשר את תנאי הרישוי של SQL Server) ו-`MSSQL_SA_PASSWORD` (קובע את הסיסמה של המשתמש הראשי, `sa`, שנשתמש בו כדי להתחבר בעוד רגע). הסיסמה חייבת להיות "חזקה" (אותיות גדולות/קטנות/ספרות/סימן) — אחרת ה-Image מסרב לעלות.
- `--name sql-server` — **חדש**: קובע שם קבוע לקונטיינר, במקום שתצטרכו להעתיק כל פעם את ה-ID המסורבל מ-`docker ps`. מעכשיו אפשר לכתוב `docker logs sql-server` או `docker stop sql-server` ישירות בשם.
- `-d` — **חדש**: (detached) מריץ את הקונטיינר **ברקע**. בלעדיו (כמו בדוגמאות הקודמות שלכם ביחידת Docker), הטרמינל "נתפס" ומציג את הפלט החי של הקונטיינר עד שעוצרים אותו — לא מעשי כשרוצים להמשיך לעבוד באותו טרמינל בזמן שהמסד רץ.

מוודאים שזה עלה בהצלחה, עם כלים שכבר מכירים מיחידת ה-Docker:

```bash
docker ps                # sql-server אמור להופיע ברשימת הקונטיינרים הרצים
docker logs sql-server   # מחפשים שורה שמכילה "SQL Server is now ready for client connections"
```

מזה והלאה, ה-DB **פשוט רץ** ברקע — בדיוק כמו כל שירות אחר שהרמתם בקונטיינר.

### שלב 3: מגדירים את מחלקות הנתונים (Entities)

```csharp
public class Student
{
    public int Id { get; set; }          // EF Core automatically recognizes "Id" as a Primary Key
    public string Name { get; set; }
    public int Age { get; set; }
    public List<Grade> Grades { get; set; } = new();   // relationship to Grade — a Navigation Property
}

public class Grade
{
    public int Id { get; set; }
    public int StudentId { get; set; }   // Foreign Key
    public int Score { get; set; }
}
```

שימו לב: `Student` כאן היא `class` רגילה עם `{ get; set; }` על כל שדה — **לא** `record`, כמו ששימש בשיעורי ה-CRUD המוקדמים. הסיבה: `record` נועד לערך קבוע שנוצר פעם אחת; EF Core צריך למלא ולעדכן שדות **אחרי** שהאובייקט כבר קיים (למשל, למלא את ה-`Id` בעצמו אחרי ה-Insert) — לשם כך צריך Properties שאפשר לשנות (`set`), כמו שכבר הכרתם ביחידת ה-OOP.

`List<Grade> Grades` נקראת **Navigation Property** — זו הדרך של EF Core "לדעת" שיש קשר בין `Student` ל-`Grade` (בדיוק כמו ה-Foreign Key `StudentId` בעיצוב מהשיעור הקודם), בלי שתצטרכו לכתוב JOIN ידני כדי לגשת לציונים של תלמיד. נעמיק בזה בשיעור העתידי על Queries; כרגע מספיק להגדיר את זה, לא להשתמש בזה.

### שלב 4: בונים את ה-DbContext

```csharp
public class SchoolDbContext : DbContext
{
    public SchoolDbContext(DbContextOptions<SchoolDbContext> options) : base(options) { }

    public DbSet<Student> Students { get; set; }
    public DbSet<Grade> Grades { get; set; }
}
```

`DbContext` הוא מחלקת-בסיס שמגיעה מתוך חבילת EF Core שהתקנו בשלב 1 — `SchoolDbContext` **יורש** ממנה (בדיוק כמו ירושה שכבר הכרתם ביחידת ה-OOP), ומקבל בזכות זה את כל היכולות לדבר עם מסד הנתונים. שתי השורות `public DbSet<Student> Students { get; set; }` ו-`public DbSet<Grade> Grades { get; set; }` הן ה"תרגום" בין מחלקה לטבלה: כל `DbSet<T>` מייצג טבלה בודדת. **חדש** לעומת הגרסה שאולי ראיתם במקומות אחרים: הקונסטרוקטור `public SchoolDbContext(DbContextOptions<SchoolDbContext> options) : base(options)` מקבל את הגדרות החיבור (איזה DB, איך מתחברים) **מבחוץ**, דרך פרמטר — בדיוק אותו רעיון של Constructor Injection שכבר הכרתם משיעור ה-DI. לא כותבים את מחרוזת החיבור בתוך ה-`DbContext` עצמו; מי שיזין אותה הוא `Program.cs`, בשלב הבא.

### שלב 5: מחברים הכול ב-Program.cs — בדיוק כמו כל שירות אחר עם DI

```csharp
var builder = WebApplication.CreateBuilder(args);

var connectionString =
    "Server=localhost,1433;Database=SchoolDb;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True;";

builder.Services.AddDbContext<SchoolDbContext>(options =>
    options.UseSqlServer(connectionString));

builder.Services.AddControllers();

var app = builder.Build();
app.MapControllers();
app.Run();
```

בואו נפרק את מחרוזת החיבור, מקטע-מקטע:

- `Server=localhost,1433` — לאיזה כתובת ופורט מתחברים. שימו לב: SQL Server משתמש בפסיק (`,`) לפני מספר הפורט, לא בנקודתיים (`:`) — טעות נפוצה. `localhost` ו-`1433` הם בדיוק הכתובת והפורט שפתחנו ב-`-p 1433:1433` בשלב 2.
- `Database=SchoolDb` — שם מסד הנתונים. **לא צריך שיהיה קיים מראש** — Migrations (שלב 6) ייצרו אותו בעצמם אם הוא לא קיים.
- `User Id=sa;Password=YourStrong@Passw0rd` — פרטי ההתחברות: המשתמש `sa` והסיסמה בדיוק כפי שקבענו ל-Docker ב-`MSSQL_SA_PASSWORD` בשלב 2. חייבים להיות זהים.
- `TrustServerCertificate=True` — ה-Image של SQL Server מגיע עם תעודת אבטחה "עצמית" (לא חתומה רשמית) — בלי השורה הזו, ההתחברות נכשלת עם שגיאת אבטחה. בפרויקט production אמיתי משתמשים בתעודה חתומה אמיתית; לפיתוח מקומי זה בסדר גמור.

`builder.Services.AddDbContext<SchoolDbContext>(...)` — הכרתם את `AddScoped`/`AddControllers` בשיעורים קודמים; `AddDbContext` הוא בדיוק אותו רעיון (למעשה, הוא רושם את ה-`DbContext` בתור `Scoped` מאחורי הקלעים), רק גרסה מיוחדת שמיועדת ל-`DbContext` — היא גם יודעת להעביר את ה-`options` (עם מחרוזת החיבור) לקונסטרוקטור שבנינו בשלב 4. מהרגע הזה, כל Controller שיבקש `SchoolDbContext` בקונסטרוקטור שלו יקבל אותו אוטומטית מ-ASP.NET — בדיוק כמו כל תלות אחרת שכבר הזרקתם.

### שלב 6: יוצרים את הטבלאות בפועל — Migrations

עד עכשיו הכול "בטיוטה" — יש מחלקות ויש חיבור, אבל שום טבלה עדיין לא נוצרה בפועל ב-DB. שתי הפקודות שסוגרות את הפער:

```bash
dotnet ef migrations add InitialCreate
```

הפקודה הזו **לא נוגעת ב-DB בכלל** — היא רק משווה בין המחלקות (`Student`, `Grade`) לבין מה שקיים כרגע (כלום, בפעם הראשונה), ויוצרת קובץ C# חדש בתיקיית `Migrations/` שמתעד את השינוי הנדרש ("צור טבלת Students עם העמודות האלה, צור טבלת Grades..."). `InitialCreate` הוא רק שם — תיעוד קריא-לאדם למה השתנה, בדיוק כמו שם ל-commit ב-Git.

```bash
dotnet ef database update
```

זו הפקודה שבאמת **מתחברת ל-DB** (דרך מחרוזת החיבור מ-Program.cs) ומריצה את השינויים המתועדים ב-Migration — יוצרת את מסד הנתונים `SchoolDb` אם הוא לא קיים, ובתוכו את הטבלאות `Students` ו-`Grades`. זה בדיוק אותו אפקט כמו להריץ `CREATE TABLE` ידני מהשיעור הקודם — רק שהוא נגזר אוטומטית מהמחלקות שלכם, לא נכתב בנפרד.

**טעות נפוצה:** להריץ רק `migrations add` ולשכוח `database update`, ואז לתהות למה הטבלה "לא קיימת" כשמריצים את התוכנית (שגיאה כמו `Invalid object name 'Students'`). `migrations add` בלבד רק **מתכנן** את השינוי בקובץ — הוא לא נוגע ב-DB בפועל. צריך את שתי הפקודות, בסדר הזה.

### שלב 7: משתמשים בזה — Controller עם שני Endpoints אמיתיים

עכשיו, כדי לוודא שכל השרשרת (Docker → DbContext → Migrations) באמת עובדת, בונים Controller פשוט שמדבר ישירות עם `SchoolDbContext`:

```csharp
[ApiController]
[Route("api/[controller]")]
public class StudentsController : ControllerBase
{
    private readonly SchoolDbContext _context;

    public StudentsController(SchoolDbContext context)   // injected automatically, thanks to AddDbContext in step 5
    {
        _context = context;
    }

    [HttpGet]
    public IActionResult GetAll()
    {
        return Ok(_context.Students.ToList());
    }

    [HttpPost]
    public IActionResult Create([FromBody] Student newStudent)
    {
        _context.Students.Add(newStudent);
        _context.SaveChanges();   // only here does it actually get written to the DB
        return CreatedAtAction(nameof(GetAll), newStudent);
    }
}
```

שום דבר כאן לא חדש בפני עצמו — `[ApiController]`, `[Route]`, `[HttpGet]`/`[HttpPost]`, Constructor Injection — כל אלה כבר הכרתם. השינוי היחיד: במקום `IRepository<Student>` או `List<Student>` בזיכרון, ה-Controller מקבל `SchoolDbContext` ישירות, ומדבר עם `_context.Students` בדיוק כמו `List<T>` — רק שמאחורי הקלעים, `.ToList()` שולח שאילתת `SELECT` אמיתית ל-SQL Server, ו-`.Add(...)` + `SaveChanges()` שולחים `INSERT` אמיתי.

(שימו לב: ה-Controller הזה מדבר עם ה-`DbContext` ישירות, לא דרך `IRepository`/`Service` שבנינו ביחידה הקודמת — זו דרך מכוונת פשוטה כדי לוודא **קודם** שכל השרשרת הטכנית עובדת מקצה לקצה. בשיעור הבא נחבר את `SchoolDbContext` הזה בדיוק לתוך ה-`IRepository<Student>` שכבר בנינו, כך ש-`StudentsController` יחזור להיות דק ולא ידבר עם ה-DB ישירות.)

### שלב 8: מריצים ובודקים בפועל

```bash
dotnet run
```

השרת עולה (כמו בכל שיעור עד עכשיו), ומדפיס את הכתובת שהוא מאזין לה. עכשיו הבדיקה האמיתית — לא רק "זה מתקמפל", אלא "זה באמת שומר ל-DB":

```bash
# 1. Create a student
curl -X POST http://localhost:5123/api/students -H "Content-Type: application/json" -d "{\"name\":\"Dana\",\"age\":25}"

# 2. Read all students back
curl http://localhost:5123/api/students
```

הבקשה הראשונה (`POST`) צריכה להחזיר `201 Created` עם התלמיד החדש. הבקשה השנייה (`GET`) צריכה להחזיר רשימה שמכילה אותו — **ההוכחה** שהמידע נשמר בפועל: אם עוצרים את `dotnet run` (Ctrl+C) ומריצים אותו שוב, ואז שולחים שוב `GET`, דנה עדיין שם. בניגוד ל-`List<T>` בזיכרון מהשיעורים המוקדמים (שהיה "שוכח" הכול עם כל הפעלה מחדש), הנתונים כאן חיים בתוך הקונטיינר של SQL Server, לא בתוך התוכנית שלכם.

## הסבר עיקרי

`DbSet<T>` "נראה" כמו `List<T>` אבל הוא לא — `context.Students.Add(...)` נראה זהה לפעולת `List<T>.Add`, אבל מאחורי הקלעים EF Core בונה שאילתת SQL בהתאם. ההבדל הקריטי: השינוי לא נשמר בפועל ל-DB עד קריאה מפורשת ל-`SaveChanges()` — אפשר להוסיף/לעדכן/למחוק כמה פעולות ברצף, וכולן נשמרות **ביחד** בקריאה אחת, לא אחת-אחת.

Code First הופך את EF Core למקור-האמת היחיד — במקום לתחזק גם `CREATE TABLE` SQL וגם מחלקות C# בנפרד (וסיכון שהם "יתפצלו" ולא יתאימו יותר), עם Code First המחלקות **הן** מקור האמת — Migrations נגזרות מהן אוטומטית. שינוי מבני (הוספת עמודה) = שינוי במחלקה + `migrations add` חדש, לא עריכת SQL ידנית.

Navigation Property (`Student.Grades`) — זו הדרך של EF Core "לדעת" על קשרים בלי לכתוב JOIN ידני כל פעם — `Student` מכיל `List<Grade>` שמייצג את כל הציונים שלו, ו-EF Core מטפל בקישור (דרך ה-`StudentId` FK) מאחורי הקלעים. זה יאפשר בשיעור העתידי על Queries לשלוף תלמיד **עם** כל ציוניו בשאילתה אחת פשוטה, בלי SQL JOIN ידני.

## יתרונות

EF Core חוסך כתיבת SQL ידני לרוב הפעולות הנפוצות (CRUD בסיסי); Migrations מתעדות כל שינוי מבני ב-DB לאורך זמן, וניתנות לביטול; Code First שומר על מחלקות C# כמקור אמת יחיד, במקום סנכרון ידני בין קוד ל-SQL; Docker נותן מסד נתונים אמיתי וזהה לכל מפתח בצוות, בלי התקנה ידנית של SQL Server על כל מחשב.

## חסרונות

EF Core מוסיף שכבת עקיפין — שאילתות מורכבות מאוד לפעמים קשות לכתוב/לייעל דרכו לעומת SQL ישיר; `SaveChanges()` שנשכח משאיר שינויים "תקועים" בזיכרון בלי להיכתב בפועל ל-DB; שכחת `docker run` (מהשלב הראשון) לפני הרצת התוכנית גורמת לשגיאת חיבור מיידית.

## נקודות חשובות

• EF Core הוא NuGet Package חיצוני (לא מובנה ב-.NET) — דורש `dotnet add package` לפני שימוש, ו-`dotnet tool install --global dotnet-ef` לכלי ה-Migrations

• `DbContext` = חיבור לוגי ל-DB, מקבל את מחרוזת החיבור דרך הקונסטרוקטור (`DbContextOptions`); `DbSet<T>` = טבלה בודדת בתוכו

• `AddDbContext` ב-`Program.cs` הוא בדיוק `AddScoped`, מותאם ל-`DbContext` — כולל העברת מחרוזת החיבור

• Code First = מחלקות C# קודם; `migrations add` מתכנן שינוי, `database update` מריץ אותו בפועל על ה-DB

• `SaveChanges()` הוא הרגע היחיד שבו שינויים נכתבים בפועל ל-DB

## טעויות נפוצות

• לשכוח להריץ `docker run` (או שהקונטיינר נעצר) לפני `dotnet run` — שגיאת חיבור מיידית; `docker ps` תמיד הבדיקה הראשונה

• להריץ `migrations add` בלי `database update` אחריו — הטבלה עדיין לא קיימת בפועל ב-DB (ראו הסבר מלא בשלב 6)

• לשכוח `SaveChanges()` ולתהות למה נתונים "לא נשמרו" ל-DB בפועל

• לבלבל בין `DbSet<T>` (טבלה) ל-`List<T>` רגילה — נראים דומים בתחביר, אך `DbSet` מתורגם לשאילתות SQL אמיתיות

## סיכום

בנינו שרשרת מלאה מאפס: התקנת EF Core כ-NuGet Package, הרמת SQL Server אמיתי בתוך Docker (`-e` למשתני סביבה, `--name`+`-d` לניהול נוח), מחלקות `Student`/`Grade` כ-Entities, `DbContext` עם `DbSet<T>` לכל טבלה שמקבל את מחרוזת החיבור דרך הקונסטרוקטור, רישום ב-`Program.cs` עם `AddDbContext` (בדיוק כמו כל שירות אחר ב-DI), יצירת הטבלאות בפועל עם `migrations add` + `database update`, ולבסוף Controller עם שני endpoints (`GET`/`POST`) שהוכיחו שהכול עובד ביחד — כולל שהנתונים שורדים הפעלה מחדש של השרת, בניגוד ל-`List<T>` בזיכרון.

## דוקומנטציה רשמית

[Microsoft Learn — EF Core overview](https://learn.microsoft.com/ef/core/)

[Microsoft Learn — Migrations](https://learn.microsoft.com/ef/core/managing-schemas/migrations/)

[Microsoft Learn — SQL Server container images](https://learn.microsoft.com/sql/linux/quickstart-install-connect-docker)

---

## תרגילים

### תרגיל 1 — DbContext ראשון

**המשימה:** צרו מחלקת `Product` פשוטה (`Id`, `Name`, `Price`), `DbContext` עם `DbSet<Product>`, והריצו `migrations add` + `database update` ליצירת הטבלה על מסד הנתונים ב-Docker שהרמתם בשלב 2.

**בדיקה:** הטבלה `Products` נוצרת בפועל ב-DB, עם עמודות תואמות למחלקה.

### תרגיל 2 — CRUD בסיסי עם SaveChanges

**המשימה:** הוסיפו 3 מוצרים עם `context.Products.Add`, קראו אותם חזרה עם `context.Products.ToList()`, ועדכנו את המחיר של אחד מהם.

**בדיקה:** לפני `SaveChanges()` הראשון, הטבלה ריקה ב-DB; אחריו, שלושת המוצרים קיימים בפועל; עדכון המחיר משתקף בשליפה חוזרת מה-DB (לא רק בזיכרון).

---

## פרויקט מסכם

**המשימה:** ממשו את סכמת ה-DB שתכננתם בשיעור הקודם (בלוג: Authors/Posts/Comments) עם EF Core Code First, מול אותו מסד נתונים ב-Docker.

**דרישות:**
1. שלוש מחלקות תואמות (`Author`, `Post`, `Comment`) עם Navigation Properties מתאימות (`Author.Posts`, `Post.Comments`)
2. `BlogDbContext` עם שלושה `DbSet<T>`, ו-Migration ראשונה שיוצרת את כל הטבלאות
3. Controller עם `GET`/`POST` שמוכיח שהכול עובד מקצה לקצה, כמו בשלבים 7-8 למעלה

**בדיקה:** בדיקה ב-DB (או בשליפה חוזרת) שכל הרשומות נוצרו עם FK נכונים בין הטבלאות, תואם בדיוק את התכנון מהשיעור הקודם.

## מה בפרק הבא

בפרק הבא — **SqlRepository**: ה-Controller ששלב 7 בנה מדבר עם `SchoolDbContext` ישירות — נוח להוכחה מהירה, אבל סותר את כל תבנית ה-DAL שבנינו ביחידת ASP.NET Core. בשיעור הבא נחבר את ה-`DbContext` הזה בדיוק לתוך `IRepository<Student>` הקיים, כך שה-Controller יחזור להיות דק ולא ידבר עם ה-DB בכלל — בלי לגעת ב-Service וב-Controller עצמם.
