Webframeworkk/ASP.NET Core/Dependency Inversion: Unterschied zwischen den Versionen
imported>Import Version 189 |
Keine Bearbeitungszusammenfassung |
||
| Zeile 1: | Zeile 1: | ||
''Hinweis: Diese Inhalte wurden mit Unterstützung von Künstlicher Intelligenz erstellt und redaktionell überprüft (Transparenzhinweis gemäß Art. 50 EU AI Act).'' | ''Hinweis: Diese Inhalte wurden mit Unterstützung von Künstlicher Intelligenz erstellt und redaktionell überprüft (Transparenzhinweis gemäß Art. 50 EU AI Act).'' | ||
---- | ---- | ||
== 1. Einführung: Was sind Services? == | == 1. Einführung: Was sind Services? == | ||
| Zeile 7: | Zeile 8: | ||
=== Hauptaufgaben von Services === | === Hauptaufgaben von Services === | ||
* '''Kapselung der Geschäftslogik''': Trennung komplexer Operationen von der Präsentationsschicht (Controller/Views). | * '''Kapselung der Geschäftslogik''': Trennung komplexer Operationen von der Präsentationsschicht (Controller/Views). | ||
* '''Wiederverwendbarkeit''': Ein Service kann von mehreren Controllern genutzt werden (DRY-Prinzip). | * '''Wiederverwendbarkeit''': Ein Service kann von mehreren Controllern genutzt werden (DRY-Prinzip). | ||
| Zeile 14: | Zeile 14: | ||
* '''Integration''': Interaktion mit externen Systemen. | * '''Integration''': Interaktion mit externen Systemen. | ||
---- | |||
== 2. Theoretische Grundlagen: DIP, IoC und DI == | == 2. Theoretische Grundlagen: DIP, IoC und DI == | ||
| Zeile 22: | Zeile 21: | ||
=== Dependency Inversion Principle (DIP) === | === Dependency Inversion Principle (DIP) === | ||
DIP besagt, dass High-Level-Module nicht von Low-Level-Modulen abhängen sollten. Beide sollten von '''Abstraktionen''' (Interfaces) abhängen. | DIP besagt, dass High-Level-Module nicht von Low-Level-Modulen abhängen sollten. Beide sollten von '''Abstraktionen''' (Interfaces) abhängen. | ||
* '''Ziel''': Lose Kopplung, Flexibilität. | * '''Ziel''': Lose Kopplung, Flexibilität. | ||
''Beispiel (Lichtschalter-Analogie):'' | ''Beispiel (Lichtschalter-Analogie):'' | ||
* ''Ohne DIP'': Ein Lichtschalter ist fest mit einer speziellen Glühbirne verdrahtet. Wenn man die Birne wechseln will, muss man den Schalter ändern. | * ''Ohne DIP'': Ein Lichtschalter ist fest mit einer speziellen Glühbirne verdrahtet. Wenn man die Birne wechseln will, muss man den Schalter ändern. | ||
* ''Mit DIP'': Es gibt eine Steckdose (Interface). Der Schalter funktioniert mit allem, was in die Steckdose passt (Glühbirne, Ventilator). | * ''Mit DIP'': Es gibt eine Steckdose (Interface). Der Schalter funktioniert mit allem, was in die Steckdose passt (Glühbirne, Ventilator). | ||
=== Inversion of Control (IoC) === | === Inversion of Control (IoC) === | ||
IoC bedeutet, die Kontrolle über die Objekterstellung und -verwaltung von der Anwendung an ein Framework oder einen Container abzugeben. | IoC bedeutet, die Kontrolle über die Objekterstellung und -verwaltung von der Anwendung an ein Framework oder einen Container abzugeben. | ||
* '''Motto''': "Don't call us, we will call you." | |||
* '''Motto''': | |||
=== Dependency Injection (DI) === | === Dependency Injection (DI) === | ||
DI ist ein Entwurfsmuster und eine konkrete Umsetzung von IoC. Abhängigkeiten werden einer Klasse von außen (meist durch einen DI-Container) zur Verfügung gestellt ("injiziert"), anstatt dass die Klasse sie selbst erstellt. | |||
---- | |||
== 3. Service-Lebenszyklen (Lifetimes) == | == 3. Service-Lebenszyklen (Lifetimes) == | ||
| Zeile 50: | Zeile 42: | ||
=== 1. Transient (Flüchtig) === | === 1. Transient (Flüchtig) === | ||
* '''Erstellung''': Jedes Mal neu, wenn der Service angefordert wird. | * '''Erstellung''': Jedes Mal neu, wenn der Service angefordert wird. | ||
* '''Nutzung''': Für leichte, zustandslose Services. | * '''Nutzung''': Für leichte, zustandslose Services. | ||
| Zeile 56: | Zeile 47: | ||
=== 2. Scoped (Bereichsbezogen) === | === 2. Scoped (Bereichsbezogen) === | ||
* '''Erstellung''': Einmal pro Request (z.B. HTTP-Anfrage). | * '''Erstellung''': Einmal pro Request (z.B. HTTP-Anfrage). | ||
* '''Nutzung''': Der Standard für Web-Anwendungen (z.B. Datenbank-Kontexte, User-Daten). Alle Komponenten innerhalb eines Requests teilen sich dieselbe Instanz. | * '''Nutzung''': Der Standard für Web-Anwendungen (z.B. Datenbank-Kontexte, User-Daten). Alle Komponenten innerhalb eines Requests teilen sich dieselbe Instanz. | ||
| Zeile 62: | Zeile 52: | ||
=== 3. Singleton (Einzelstück) === | === 3. Singleton (Einzelstück) === | ||
* '''Erstellung''': Einmalig für die gesamte Laufzeit der Anwendung (beim ersten Abruf). | * '''Erstellung''': Einmalig für die gesamte Laufzeit der Anwendung (beim ersten Abruf). | ||
* '''Nutzung''': Für globale Zustände, Caches oder Konfigurationen. | * '''Nutzung''': Für globale Zustände, Caches oder Konfigurationen. | ||
* '''Registrierung''': <code>builder.Services.AddSingleton<IMyService, MyService>();</code> | * '''Registrierung''': <code>builder.Services.AddSingleton<IMyService, MyService>();</code> | ||
---- | |||
== 4. Implementierung in ASP.NET Core == | == 4. Implementierung in ASP.NET Core == | ||
| Zeile 76: | Zeile 64: | ||
=== Schritt 1: Interface und Implementierung definieren === | === Schritt 1: Interface und Implementierung definieren === | ||
<syntaxhighlight lang="csharp">// ServiceContracts (Interface) | <syntaxhighlight lang="csharp"> | ||
// ServiceContracts (Interface) | |||
namespace ServiceContracts | namespace ServiceContracts | ||
{ | { | ||
| Zeile 102: | Zeile 91: | ||
} | } | ||
} | } | ||
}</syntaxhighlight> | } | ||
</syntaxhighlight> | |||
=== Schritt 2: Registrierung im DI-Container (Program.cs) === | === Schritt 2: Registrierung im DI-Container (Program.cs) === | ||
<syntaxhighlight lang="csharp">var builder = WebApplication.CreateBuilder(args); | <syntaxhighlight lang="csharp"> | ||
var builder = WebApplication.CreateBuilder(args); | |||
// Registrierung des Services als Transient (Beispiel) | // Registrierung des Services als Transient (Beispiel) | ||
| Zeile 111: | Zeile 103: | ||
// Weitere Dienste... | // Weitere Dienste... | ||
builder.Services.AddControllersWithViews();</syntaxhighlight> | builder.Services.AddControllersWithViews(); | ||
</syntaxhighlight> | |||
=== Schritt 3: Injektion im Controller (Constructor Injection) === | === Schritt 3: Injektion im Controller (Constructor Injection) === | ||
Das ist die empfohlene Methode. Der DI-Container löst die Abhängigkeit automatisch auf. | Das ist die empfohlene Methode. Der DI-Container löst die Abhängigkeit automatisch auf. | ||
<syntaxhighlight lang="csharp">public class HomeController : Controller | <syntaxhighlight lang="csharp"> | ||
public class HomeController : Controller | |||
{ | { | ||
private readonly ICitiesService _citiesService; | private readonly ICitiesService _citiesService; | ||
| Zeile 133: | Zeile 128: | ||
return View(cities); | return View(cities); | ||
} | } | ||
}</syntaxhighlight> | } | ||
</syntaxhighlight> | |||
---- | |||
== 5. Arten der Dependency Injection == | == 5. Arten der Dependency Injection == | ||
| Zeile 145: | Zeile 141: | ||
#* Stellt sicher, dass die Klasse validiert instanziiert wird. | #* Stellt sicher, dass die Klasse validiert instanziiert wird. | ||
# '''Property Injection''': | # '''Property Injection''': | ||
#* Nutzung des <code>[FromServices]</code> Attributs an Properties. | #* Nutzung des <code>[FromServices]</code> Attributs an Properties. | ||
#* Sinnvoll für optionale Abhängigkeiten (weniger gebräuchlich). | #* Sinnvoll für optionale Abhängigkeiten (weniger gebräuchlich). | ||
# '''Method Injection''': | # '''Method Injection''': | ||
#* Übergabe als Parameter an eine Methode. | #* Übergabe als Parameter an eine Methode. | ||
# '''Action Method Injection''': | # '''Action Method Injection''': | ||
#* Direkt in der Controller-Action mit <code>[FromServices]</code>. | #* Direkt in der Controller-Action mit <code>[FromServices]</code>. | ||
#* Nützlich, wenn der Service nur in einer einzigen Action benötigt wird. | #* Nützlich, wenn der Service nur in einer einzigen Action benötigt wird. | ||
<syntaxhighlight lang="csharp">public IActionResult Index([FromServices] IUserService userService) | <syntaxhighlight lang="csharp"> | ||
public IActionResult Index([FromServices] IUserService userService) | |||
{ | { | ||
// ... | // ... | ||
}</syntaxhighlight> | } | ||
</syntaxhighlight> | |||
---- | |||
== 6. Best Practices == | == 6. Best Practices == | ||
| Zeile 174: | Zeile 169: | ||
* '''Kein globaler State''': Vermeiden Sie statische Klassen für User-Daten; nutzen Sie stattdessen Scoped Services oder <code>HttpContext.Items</code>. | * '''Kein globaler State''': Vermeiden Sie statische Klassen für User-Daten; nutzen Sie stattdessen Scoped Services oder <code>HttpContext.Items</code>. | ||
---- | |||
== 7. Autofac (Alternative) == | == 7. Autofac (Alternative) == | ||
| Zeile 186: | Zeile 180: | ||
# In <code>Program.cs</code> konfigurieren: | # In <code>Program.cs</code> konfigurieren: | ||
<syntaxhighlight lang="csharp">builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); | <syntaxhighlight lang="csharp"> | ||
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); | |||
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => | builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => | ||
| Zeile 194: | Zeile 189: | ||
.As<ICitiesService>() | .As<ICitiesService>() | ||
.InstancePerLifetimeScope(); // Entspricht Scoped | .InstancePerLifetimeScope(); // Entspricht Scoped | ||
});</syntaxhighlight> | }); | ||
</syntaxhighlight> | |||
=== Mapping der Lebenszyklen === | === Mapping der Lebenszyklen === | ||
| Zeile 201: | Zeile 198: | ||
* <code>SingleInstance()</code> = Singleton | * <code>SingleInstance()</code> = Singleton | ||
---- | |||
== Zusammenfassung == | == Zusammenfassung == | ||
Aktuelle Version vom 18. Februar 2026, 21:27 Uhr
Hinweis: Diese Inhalte wurden mit Unterstützung von Künstlicher Intelligenz erstellt und redaktionell überprüft (Transparenzhinweis gemäß Art. 50 EU AI Act).
1. Einführung: Was sind Services?
In ASP.NET Core MVC sind Services Klassen, die für die Implementierung der Kern-Geschäftslogik Ihrer Anwendung verantwortlich sind. Sie sind darauf ausgelegt, wiederverwendbar, eigenständig und unabhängig von spezifischen Controllern oder Views zu sein.
Hauptaufgaben von Services
- Kapselung der Geschäftslogik: Trennung komplexer Operationen von der Präsentationsschicht (Controller/Views).
- Wiederverwendbarkeit: Ein Service kann von mehreren Controllern genutzt werden (DRY-Prinzip).
- Testbarkeit: Services können leicht isoliert getestet werden (Unit Tests).
- Datenzugriff: Kommunikation mit Datenbanken oder APIs.
- Integration: Interaktion mit externen Systemen.
2. Theoretische Grundlagen: DIP, IoC und DI
Um Dependency Injection zu verstehen, müssen wir drei grundlegende Prinzipien betrachten:
Dependency Inversion Principle (DIP)
DIP besagt, dass High-Level-Module nicht von Low-Level-Modulen abhängen sollten. Beide sollten von Abstraktionen (Interfaces) abhängen.
- Ziel: Lose Kopplung, Flexibilität.
Beispiel (Lichtschalter-Analogie):
- Ohne DIP: Ein Lichtschalter ist fest mit einer speziellen Glühbirne verdrahtet. Wenn man die Birne wechseln will, muss man den Schalter ändern.
- Mit DIP: Es gibt eine Steckdose (Interface). Der Schalter funktioniert mit allem, was in die Steckdose passt (Glühbirne, Ventilator).
Inversion of Control (IoC)
IoC bedeutet, die Kontrolle über die Objekterstellung und -verwaltung von der Anwendung an ein Framework oder einen Container abzugeben.
- Motto: "Don't call us, we will call you."
Dependency Injection (DI)
DI ist ein Entwurfsmuster und eine konkrete Umsetzung von IoC. Abhängigkeiten werden einer Klasse von außen (meist durch einen DI-Container) zur Verfügung gestellt ("injiziert"), anstatt dass die Klasse sie selbst erstellt.
3. Service-Lebenszyklen (Lifetimes)
In ASP.NET Core definieren Sie bei der Registrierung eines Services dessen Lebensdauer. Es gibt drei Hauptoptionen:
1. Transient (Flüchtig)
- Erstellung: Jedes Mal neu, wenn der Service angefordert wird.
- Nutzung: Für leichte, zustandslose Services.
- Registrierung:
builder.Services.AddTransient<IMyService, MyService>();
2. Scoped (Bereichsbezogen)
- Erstellung: Einmal pro Request (z.B. HTTP-Anfrage).
- Nutzung: Der Standard für Web-Anwendungen (z.B. Datenbank-Kontexte, User-Daten). Alle Komponenten innerhalb eines Requests teilen sich dieselbe Instanz.
- Registrierung:
builder.Services.AddScoped<IMyService, MyService>();
3. Singleton (Einzelstück)
- Erstellung: Einmalig für die gesamte Laufzeit der Anwendung (beim ersten Abruf).
- Nutzung: Für globale Zustände, Caches oder Konfigurationen.
- Registrierung:
builder.Services.AddSingleton<IMyService, MyService>();
4. Implementierung in ASP.NET Core
Hier sehen wir uns an, wie man Services definiert, registriert und injiziert.
Schritt 1: Interface und Implementierung definieren
// ServiceContracts (Interface)
namespace ServiceContracts
{
public interface ICitiesService
{
List<string> GetCities();
}
}
// Services (Implementierung)
namespace Services
{
public class CitiesService : ICitiesService
{
private List<string> _cities;
public CitiesService()
{
_cities = new List<string>() { "London", "Paris", "New York", "Tokyo", "Rome" };
}
public List<string> GetCities()
{
return _cities;
}
}
}
Schritt 2: Registrierung im DI-Container (Program.cs)
var builder = WebApplication.CreateBuilder(args);
// Registrierung des Services als Transient (Beispiel)
builder.Services.AddTransient<ICitiesService, CitiesService>();
// Weitere Dienste...
builder.Services.AddControllersWithViews();
Schritt 3: Injektion im Controller (Constructor Injection)
Das ist die empfohlene Methode. Der DI-Container löst die Abhängigkeit automatisch auf.
public class HomeController : Controller
{
private readonly ICitiesService _citiesService;
// Der Service wird über den Konstruktor injiziert
public HomeController(ICitiesService citiesService)
{
_citiesService = citiesService;
}
[Route("/")]
public IActionResult Index()
{
// Verwendung des Services
List<string> cities = _citiesService.GetCities();
return View(cities);
}
}
5. Arten der Dependency Injection
ASP.NET Core unterstützt verschiedene Arten der Injektion:
- Constructor Injection (Empfohlen):
- Abhängigkeiten werden über den Konstruktor übergeben.
- Stellt sicher, dass die Klasse validiert instanziiert wird.
- Property Injection:
- Nutzung des
[FromServices]Attributs an Properties. - Sinnvoll für optionale Abhängigkeiten (weniger gebräuchlich).
- Nutzung des
- Method Injection:
- Übergabe als Parameter an eine Methode.
- Action Method Injection:
- Direkt in der Controller-Action mit
[FromServices]. - Nützlich, wenn der Service nur in einer einzigen Action benötigt wird.
- Direkt in der Controller-Action mit
public IActionResult Index([FromServices] IUserService userService)
{
// ...
}
6. Best Practices
- Interfaces nutzen: Abhängigkeiten sollten immer auf Interfaces basieren, nicht auf konkreten Klassen.
- Constructor Injection bevorzugen: Es ist die sauberste Form der DI.
- Service Locator vermeiden: Rufen Sie nicht manuell
GetService()auf (außer in speziellen Factory-Szenarien). - Lebenszyklen beachten: Vermeiden Sie Captive Dependencies (z.B. einen Scoped Service in einen Singleton injizieren – das führt zu Fehlern).
- Kein globaler State: Vermeiden Sie statische Klassen für User-Daten; nutzen Sie stattdessen Scoped Services oder
HttpContext.Items.
7. Autofac (Alternative)
Obwohl ASP.NET Core einen eingebauten Container hat, bietet Autofac erweiterte Funktionen (z.B. Property Injection, Decorators, erweiterte Scopes).
Integration von Autofac
- NuGet-Paket
Autofac.Extensions.DependencyInjectioninstallieren. - In
Program.cskonfigurieren:
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
// Registrierung mit Autofac-Syntax
containerBuilder.RegisterType<CitiesService>()
.As<ICitiesService>()
.InstancePerLifetimeScope(); // Entspricht Scoped
});
Mapping der Lebenszyklen
InstancePerDependency()= TransientInstancePerLifetimeScope()= ScopedSingleInstance()= Singleton
Zusammenfassung
DI ist ein mächtiges Werkzeug, um lose gekoppelte, testbare und wartbare Software zu schreiben. Durch das Verständnis der Prinzipien (DIP, IoC) und der korrekten Anwendung der Lebenszyklen (Transient, Scoped, Singleton) können Sie robuste ASP.NET Core Anwendungen entwickeln.
Hinweis: Diese Inhalte wurden mit Unterstützung von Künstlicher Intelligenz erstellt und redaktionell überprüft (Transparenzhinweis gemäß Art. 50 EU AI Act).