EMZETT.
Login

Kurz: ImplicitUsings fügt häufige using-Zeilen automatisch ein, Nullable aktiviert die Null-Analyse.

Teil des Kurses C#

Kapitel 15 von 16 im Kurs C#. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Das dotnet-Werkzeug

BefehlZweck
dotnet new console -o NameKonsolenprogramm anlegen
dotnet new classlib, webapi, xunitBibliothek, Web-API, Tests
dotnet new sln + dotnet sln add ...Projektmappe
dotnet runübersetzen und starten
dotnet buildübersetzen
dotnet testTests ausführen
dotnet publish -c ReleaseAuslieferungsfähige Dateien
dotnet add package NameNuGet-Paket hinzufügen
dotnet format, dotnet watch runformatieren, Neustart bei Änderung

Projektdatei

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
  </ItemGroup>
</Project>

ImplicitUsings fügt häufige using-Zeilen automatisch ein, Nullable aktiviert die Null-Analyse.

Namensräume

namespace Kurs.Modelle;                                  // dateiweiter Namensraum
 
public class Artikel
{
    public required string Name { get; init; }
    public decimal Preis { get; init; }
}
using Kurs.Modelle;
var a = new Artikel { Name = "Stift", Preis = 1.5m };

Eine Datei hat meist eine Klasse und heißt wie die Klasse. Der Namensraum folgt der Ordnerstruktur.

Tests mit xUnit

dotnet new xunit -o Kurs.Tests
dotnet add Kurs.Tests reference Kurs
dotnet test
using Xunit;
 
public class RechnerTests
{
    [Fact]
    public void Addiere_ZweiZahlen_LiefertSumme()
    {
        Assert.Equal(5, Rechner.Addiere(2, 3));
    }
 
    [Theory]
    [InlineData(2, 3, 5)]
    [InlineData(0, 0, 0)]
    [InlineData(-1, 1, 0)]
    public void Addiere_VieleFaelle(int a, int b, int erwartet)
    {
        Assert.Equal(erwartet, Rechner.Addiere(a, b));
    }
 
    [Fact]
    public void Teile_DurchNull_WirftFehler()
    {
        Assert.Throws<DivideByZeroException>(() => Rechner.Teile(1, 0));
    }
}

Ein Test hat drei Teile (Arrange – Act – Assert): vorbereiten, ausführen, prüfen. Alternativen zu xUnit: NUnit und MSTest. Zum Ersetzen von Abhängigkeiten gibt es Moq oder NSubstitute.

Das Prinzip ohne Bibliothek:

int fehler = 0;
void Pruefe(string name, object erwartet, object ist)
{
    bool ok = erwartet.Equals(ist);
    if (!ok) fehler++;
    Console.WriteLine($"{(ok ? "OK    " : "FEHLER")} {name}");
}
 
Pruefe("addiere", 5, Rechner.Addiere(2, 3));
Pruefe("addiere negativ", -1, Rechner.Addiere(2, -3));
Pruefe("palindrom", true, Rechner.IstPalindrom("Anna"));
Pruefe("kein palindrom", false, Rechner.IstPalindrom("Hallo"));
Console.WriteLine($"{fehler} Fehler");
 
static class Rechner
{
    public static int Addiere(int a, int b) => a + b;
    public static bool IstPalindrom(string s)
    {
        var t = new string(s.ToLower().Where(char.IsLetterOrDigit).ToArray());
        return t.SequenceEqual(t.Reverse());
    }
}

Ausgabe:

OK     addiere
OK     addiere negativ
OK     palindrom
OK     kein palindrom
0 Fehler

Eine Web-API in wenigen Zeilen

ASP.NET Core (Minimal API) zeigt die Stärke von C# im Webbereich:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IArtikelDienst, ArtikelDienst>();
var app = builder.Build();
 
app.MapGet("/artikel", (IArtikelDienst d) => d.Alle());
app.MapGet("/artikel/{id:int}", (int id, IArtikelDienst d) =>
    d.Finde(id) is { } a ? Results.Ok(a) : Results.NotFound());
app.MapPost("/artikel", (Artikel a, IArtikelDienst d) => Results.Created($"/artikel/{d.Neu(a).Id}", a));
 
app.Run();

Start mit dotnet run. Die Antworten werden automatisch als JSON geliefert.

Datenbanken

WerkzeugZweck
Entity Framework CoreORM: Klassen ↔ Tabellen, Migrationen, LINQ-Abfragen
Dapperschlanker SQL-Helfer
ADO.NETgrundlegende Datenbankzugriffe
public class ShopContext : DbContext
{
    public DbSet<Artikel> Artikel => Set<Artikel>();
    protected override void OnConfiguring(DbContextOptionsBuilder o) => o.UseSqlite("Data Source=shop.db");
}
 
using var db = new ShopContext();
var teuer = db.Artikel.Where(a => a.Preis > 10).OrderBy(a => a.Name).ToList();    // wird zu SQL

Logging und Konfiguration

Nutze ILogger<T> (Microsoft.Extensions.Logging) statt Console.WriteLine, appsettings.json und Umgebungsvariablen für Einstellungen, und User Secrets bzw. Umgebungsvariablen für Geheimnisse (nie im Code).

Merke

  • dotnet new/run/build/test/publish/add package sind die Standardbefehle
  • Die .csproj enthält Framework, Nullable und NuGet-Pakete
  • xUnit: [Fact], [Theory], Assert.Equal; Arrange–Act–Assert
  • ASP.NET Core Minimal APIs und Entity Framework Core decken Web und Datenbank ab
  • Konfiguration und Geheimnisse gehören nicht in den Quellcode

Übungsaufgabe

Lege mit dotnet new eine Lösung mit einer Bibliothek und einem xUnit-Testprojekt an und schreibe vier Tests.

Quiz zur Selbstkontrolle

in xUnit?

  • Ein Test, der mit mehreren Eingaben läuft ([InlineData]) (richtig)
  • Ein Test ohne Prüfung
  • Ein ausgelassener Test
  • Ein Leistungstest

Weiter im Kurs

Zurück: Dateien, JSON und Datum/Zeit

Weiter: Nachschlagen: Spickzettel

Alle Kapitel: C# im Überblick