ASP.NET Grundlagen
Datum: August 2022
Lesedauer: 12 Minuten
In diesem Blogpost wird beschrieben, was ASP.NET ist und welche Projekte damit erstellt werden können.
.NET
NET ist eine Entwicklungsplattform von Microsoft, um Apps zu bauen. Das Framework ist gratis, cross-platform, open-source, objektbasiert und zählt heute zu den effizientesten.
Eckdaten
Erstveröffentlichungsdatum: Ende des Jahres 2000 Entwickler: Microsoft Aktuelle Version: 6.0.3 Programmiersprachen: C#, F#, Visual Basic, C++
ASP.NET
ASP.NET (Active Server Pages Network Enabled Technologies) ist ein Web Framework zur Erstellung von dynamischen HTML, CSS und JS Webapps, Applikationen und Services. Dieses gibt es seit dem Jahre 2002.
Zu den Vorteilen von ASP.NET zählen:
- Reduzierte Entwicklungszeit
- Out of the box Features
- Einfachheit
- Sicherheit
- Anpassungsfähigkeit
- ...
ASP.NET Applikationen haben jeweils zwei Files.
Das Program.cs
konfiguriert, baut und startet die Applikation.
Im appsettings.json
befindet sich die Konfiguration. In dieser Datei sind die erlaubten Hosts, Logging Informationen und auch Connection Strings festgehalten. Diese Daten werden im Program.cs
verwendet, können aber auch von Models benutzt werden.
Learning
Wenn Fragen zu einem bestimmten Punkt bezüglich ASP.NET aufkommen, kann man die jeweils relativ schnell klären.
Für Anfänger sind zudem die Microsoft Tutorials in Videoformat zu empfehlen: https://dotnet.microsoft.com/en-us/learn/aspnet (opens in a new tab)
Modelle
Beim Erstellen eines Projektes in VS stösst man auf einige Möglichkeiten. In diesem Abschnitt gehe ich auf die einzelnen Modelle ein.
Web Forms
Dokumentation: https://docs.microsoft.com/en-us/aspnet/web-forms/ (opens in a new tab)
Was ist Web Forms?
Web Forms Applikationen werden über den Browser aufgerufen. Die Seiten werden mit HTML, client-scripts, server controls und server code
geschrieben. Beim Aufruf wird alles kompiliert und vom Framework ausgeführt.
Eine solche Webseite besteht jeweils aus einem File für Code und Markup. In VS ist die Web Forms Entwicklung äusserst benutzerfreundlich. Dies
gilt als der grösste Vorteil. Man kann Elemente hineinziehen und Seiten über einen Editor bearbeiten. Zudem ist es möglich, auf eine einfache Art und
Weise, Properties, Methoden und Events zu setzen.
Heutzutage wird Web Forms aber kaum noch eingesetzt. Die Vorgehensweise ist nicht mehr modern und auch die Tutorials sind längst veraltet.
Installation
Beim Erstellen eines neuen Projektes muss man folgende Vorlage auswählen.
In einem späteren Schritt kann man sich dann für Web Forms entscheiden.
Es kann durchaus sein, dass Web Forms hier nicht angezeigt wird. Dann muss man vorerst unter ASP. NET web development
zwei Dinge
installieren:
- .NET Framework project and item templates
- Additional project templates
MVC
Dokumentation: https://docs.microsoft.com/en-us/aspnet/core/mvc/overview?view=aspnetcore-6.0 (opens in a new tab) Tutorial: https://docs.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/start-mvc?view=aspnetcore-6.0&tabs=visual-studio (opens in a new tab)
Was ist MVC?
MVC ist ein weit verbreitetes Konzept. Dabei trennt man in Models, Views und Controllers.
Der grosse Vorteil ist, dass man mit MVC GUIs und APIs in einem Projekt erstellen kann. Zudem ist es der moderne Projekttyp, welcher schon am
längsten existiert. Aus diesen Gründen gibt es auch sehr viele Projekte, die diesen Ansatz verfolgen.
Installation
Beispiel
In diesem wird demonstriert, was es braucht, um eine Seite anzuzeigen.
Oft wird vergessen, dass das MVC nicht nur aus drei Teilen besteht. Denn ein Request muss zuerst auf einen Controller geroutet werden.
In ASP.NET findet man die Routing im Program.cs
.
Dieses File beschreibt, wie die URL beim Aufruf der Seite auszusehen hat.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Wenn der Request jetzt mit der URL /Home/Index/2
abgesetzt wird. Dann wird im HomeController
die Index
Methode mit dem Parameter 2
durchgeführt.
Hier geht man davon aus, dass sowas wie /Welcome/kay/3
aufgerufen wird.
Wichtig ist auch, dass die Route dementsprechend angepasst ist.
using Microsoft.AspNetCore.Mvc;
namespace MvcMovie.Controllers
{
public class HelloWorldController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Welcome(string name, int numTimes = 1)
{
ViewData["Message"] = "Hello " + name;
ViewData["NumTimes"] = numTimes;
return View();
}
}
}
Nachdem der Code in der Welcome Methode durchlaufen wurde und ViewData abgefüllt ist, wird die View Methode ausgeführt. Diese zeigt diese View an, welche denselben Namen trägt.
@{
ViewData["Title"] = "Welcome";
}
<h2>Welcome</h2>
<ul>
@for (int i = 0; i < (int)ViewData["NumTimes"]!; i++)
{
<li>@ViewData["Message"]</li>
}
</ul>
In der View wird neben dem HTML vom @
gebrauch gemacht. Durch dieses kann C# Code geschrieben werden.
In diesem Fall wird über einen Loop der Name eine bestimmte Anzahl Male ausgegeben.
Model
In MVC gibt es einige nützliche Dinge, die man tun kann, welche mit dem Model zu tun haben.
Entity Framework
Arbeitet man mit Models, ergibt es auch Sinn, vom EF gebrauch zu machen.
Durch das EF kann man die Datenbank aus dem Code heraus oder Code auf der Grundlage der Datenbank generieren. Dabei unterscheidet man
zwischen Model First und DB First. Da ich in meinem aktuellen Projekt von der Model First Variante Gebrauch mache, erläutere ich diese hier.
Vorerst habe ich die entsprechenden Models erstellt. Danach konnte ich über die Package Manger Console eine Migration erstellen und diese ausführen.
Add-Migration InitialCreate
Update-Database
Zeile 1: Generiert ein Migrations/{timestamp}_InitialCreate.cs
File.
Zeile 2: Verwendet die generierte Migration um die Datenbank zu aktualisieren.
Context
Beim Vorgang wurde eine Context Klasse erstellt.
Diese enthält die DbSet<Nistbox.Models.Nistbox>
Properties, welches die Daten aus der Datenbank repräsentieren.
Das Projekt handelt von Nistkästen (Nestboxes).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Nestbox.Models;
namespace Nestbox.Data
{
public class NestboxContext : DbContext
{
public NestboxContext (DbContextOptions<NestboxContext> options)
: base(options)
{
}
public DbSet<Nestbox.Models.Coordinates>? Coordinates { get; set; }
public DbSet<Nestbox.Models.Log>? Log { get; set; }
public DbSet<Nestbox.Models.Nestbox>? Nestbox { get; set; }
}
}
Dependency Injection
In den Controllern kann der NestboxContext durch die DI verwendet werden.
private readonly NistboxContext _context;
public NistboxesController(NistboxContext context)
{
_context = context;
}
Um dies zu ermöglichen, wurde im Program.cs
der folgende Code hinzugefügt.
builder.Services.AddDbContext<NistboxContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("NistboxContext") ??
throw new InvalidOperationException("Connection string 'NistboxContext' not found.")
)
);
Connection String
Um eine Verbindung zur DB aufzubauen, wird ein Connection String benötigt.
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-Nestbox-CONNECTION-KEY;
Trusted_Connection=True;MultipleActiveResultSets=true",
"NistboxContext": "Server=(localdb)\\mssqllocaldb;Database=Nestbox.Data;Trusted_Connection=True;
MultipleActiveResultSets=true"
},
Initial Create
Durch dieses File werden die Daten in der DB erstellt.
// ...
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<float>(
name: "Longitude",
table: "Coordinates",
type: "real",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AlterColumn<float>(
name: "Latitude",
table: "Coordinates",
type: "real",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
}
// ...
Alternativen
Wie bereits beschrieben, gibt es auch den DB-First Ansatz. Dabei erstellt man die Tabellen auf der Datenbank und generiert anschliessen den Code
daraus.
Mehr dazu erfährt man hier: https://docs.microsoft.com/en-us/ef/ef6/modeling/designer/workflows/database-first (opens in a new tab)
View
C# Code
Wie schon oben beschrieben, kann man in den Views C# Code schreiben. Deshalb werden einem einige Türen geöffnet. In der Dokumentation wird noch mehr dazu beschrieben: https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/views/ (opens in a new tab)
Partial Views
Auch ein Vorteil von MVC ist, dass es saubere Views gibt, welche die Daten nur noch darstellen müssen. Jedoch können solche Views auch sehr
gross und unübersichtlich werden.
Aus diesem Grund kann man Teile einer View auslagern. Dies wird auch schon standardmässig getan.
Um dies nachzuvollziehen, kann man die Layout-Datei betrachten. Darin befindet sich nämlich @RenderBody()
, welche den Inhalt der Seiten in das
Layout hineinfügt.
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
Solche Partial Views sind an den Bodenstrichen zu erkennen.
Man kann auch selbst solche Views erstellen und einbinden.
@Html.Partial("_NavBar")
Controller
In Controllers kann man viel anstellen. Wichtig ist aber auch, wie man mit der View kommuniziert. Dies wird hier beschrieben.
Passing Data into Views
Wenn die Logik in den Controllern geschieht und in den Views angezeigt werden muss, kommt die Frage auf, wie man die Daten in die Views hinein
bekommt.
Dafür gibt es verschiedene Möglichkeiten.
ViewData["Movie"] = movie;
return View();
<h2>@( ((Movie) ViewData["Movie"]).Name )</h2>
View Method
Später konnte man dann den Wert direkt über die View Methode übergeben.
return View(movie);
<h2>@Model.Name</h2>
ViewBag
Das Problem mit den beiden vorherigen Varianten ist, dass man nicht leicht mehrere Daten übergeben kann. Aus diesem Grund gibt es ViewBag.
ViewBag.Title= "Hello World";
return View();
<h2>@ViewBag.Title</h2>
Action Results
View ist nur eine der Helper Methoden. Dies sind ein paar weitere.
namespace Vidly.Controllers
{
public class MoviesController : Controller
{
// GET: Movies
public ActionResult Random()
{
var movie = new Movie() { Name = "Shrek" };
return View(movie);
//return Content("Hello Wolrd");
//return HttpNotFound();
//return new EmptyResult();
//return RedirectToAction("Index", "Home", new { page = 1, sortBy = "name" });
}
}
}
Blazor
Dokumentation: https://docs.microsoft.com/en-us/aspnet/core/blazor/?WT.mc_id=dotnet-35129-website&view=aspnetcore-6.0 (opens in a new tab)
Tutorial: https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/intro (opens in a new tab)
Webseite: https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor (opens in a new tab)
Was ist Blazor?
Bei Blazor unterscheidet man zwischen zwei Projekttypen.
Zum einen gibt es Blazor Server. Blazor Server ist äusserst einzigartig. Es kombiniert serverseitiges und clientseitiges Rendering. So kann es die
Seiten sehr schnell zusammenstellen, bleibt aber trotzdem sehr interaktiv. Der User erhält also die Webseite schnell und kann zudem, wie
beispielsweise in React oder Angular, Elemente hinzufügen. Als frontendseitige Sprache wird C# anstelle von JS eingesetzt. Das Markup und der
Code wird in Razor Files geschrieben.
Durch diese Kombination von serverseitigem und cleintsetigem Rendering gilt Blazor Server als eines der schnellsten Projekte.
Es gibt aber auch noch Blazor WebAssembly. Solche Projekte werden nur clientseitig geladen. Zudem sind WebAssembly Applikation schlecht
geschützt. Man darf also keine Keys verwenden, keine direkten Datenbankverbindungen aufbauen und nicht einmal das appsettings File ist
erwünscht. Will man mit Daten arbeiten, so ist eine zusätzliche API notwendig.
Neben diesen Nachteilen gibt es aber auch einen wichtigen Grund, es zu verwenden. Solche Projekte können, nach einem Download von ca. 10 Megabyte, offline verwendet werden. Für den User sieht es danach wie eine Desktop-Applikation aus.
Installation
Bei der Blazor installation ist es wichtig, dass man das gewünschte Projekt wählt.
Beispiel
In diesem Fall zeige ich, wie mächtig Blazor Server ist.
Dieses Razor File reicht aus, um einen voll funktionsfähigen Counter zu bauen.
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Razor Pages
Tutorial: https://docs.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/?view=aspnetcore-6.0 (opens in a new tab) Dokumentation: https://docs.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-6.0&tabs=visual-studio (opens in a new tab)
Was ist Razor Pages?
Beim MVC Modell musste man sich jeweils um einiges an verschiedenen Orten kümmern. Mit Razor Pages sollte man Seiten produktiver erstellen
können. Razor Pages ist laut Microsoft das neuere und einfachere Modell wie MVC. Jedes File stellt einen Endpoint dar. Und das HTML kann elegant
mithilfe von C# ausgegeben werden.
Gerade für statische Anwendungen ist dieser Projekttyp empfehlenswert. Denn das serverseitige Rendering ist äusserst schnell.
Installation
Hier kann man nach der ASP.NET Core Web App Installation bereits von den Razor Pages Features Gebrauch machen.
Beispiel
Wie man an diesem simplen Beispiel erkennen kann, besteht eine Seite aus zwei Files.
Das cshtml File stellt die Seite dar.
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
Im cshtml.cs
File werden die Daten bereitgestellt.
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorPagesMovie.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
}
In diesem weiteren, etwas grösserem, Beispiel erkennt man gut, wie die zwei Files zusammenspielen.
Im verschachteltem cshtml.cs
File werden die Daten behandelt und an die Seite geliefert.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Data;
using RazorPagesMovie.Models;
namespace RazorPagesMovie.Pages.Movies
{
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return (_context.Movie?.Any(e => e.ID == id)).GetValueOrDefault();
}
}
}
In den cshtml Dateien befindet sich unter anderem gewöhnliches HTML. Jedoch kann man hier die Daten aus dem Controller nutzen.
Hier sieht man auch wieder, dass sich die verschiedenen Konzepte nicht in allen Punkten voneinander unterscheiden.
@page
@model RazorPagesMovie.Pages.Movies.EditModel
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Movie.ID" />
<div class="form-group">
<label asp-for="Movie.Title" class="control-label"></label>
<input asp-for="Movie.Title" class="form-control" />
<span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.Genre" class="control-label"></label>
<input asp-for="Movie.Genre" class="form-control" />
<span asp-validation-for="Movie.Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.Price" class="control-label"></label>
<input asp-for="Movie.Price" class="form-control" />
<span asp-validation-for="Movie.Price" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="./Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
API
An dieser Stelle auch noch erwähnenswert, ist das API-Modell.
Bei solchen Projekten hat man keine GUI. Man erstellt eine Anwendung, welche auf Anfragen ein Json (oder sonstige Daten) zurückgibt. So sind die
Daten auch gut geschützt.
Dabei ist der Zweck, dass eine andere Applikation, wie z. B. ein React Projekt, von der API Gebrauch macht.
Ein solches Projekt sollte man nicht erstellen, wenn man in kurzer Zeit eine Applikation erstellen will. Denn damit ein User die App bedienen kann,
muss diese zuerst von einem Frontend verwendet werden.
(Ausser man will, dass der User die Daten im Request Body mitschickt. Dies ist aber nicht für den DAU (opens in a new tab) geeignet.)
Fazit
In meinem aktuellen Projekt arbeite ich mit dem MVC Modell. Bisher habe ich damit nur positive Erfahrungen gemacht. MVC ermöglicht, meiner Meinung
nach, eine effiziente Arbeit. Zudem bleibt, durch die strikte Trennung, auch alles sauber und übersichtlich.
Ich habe aber auch die anderen Modelle ausprobiert. Auch an denen kann ich bisher nichts aussetzen.
Man kann auch nicht behaupten, dass eines der Modelle besser ist als die anderen. Denn je nach Projekt gibt es eine Variante, welche am sinnvollsten ist.
Deshalb ist es hilfreich, wenn man die Konzepte kennt und unterscheiden kann.
Anmerkung: Man kann Projekte bei Bedarf auch kombinieren.
Hier noch das Wichtigste im Überblick.
Projekttyp | Vorteile | Nachteile |
---|---|---|
Web Forms | Drag and Drop | Nicht mehr modern, veraltete Tutorials, sollte nicht mehr eingesetzt werden |
MVC | Hohe Geschwindigkeit, GUI und API in einem Projekt möglich, bekanntes Konzept, weit verbreitet | Schon etwas älter, Blazor Server als moderne Alternative |
Blazor Server | Beste Geschwindigkeit, serverseitiges und clientseitiges Rendering, hohe Interaktivität für den Nutzer | Nicht optimal für statische Seiten |
Blazor WebAssembly | Offline Access nach Download (ca. 10 MB) | Nur clientseitig, Sicherheitsrisiken, erfordert API-Nutzung |
Razor Pages | Hohe Geschwindigkeit, schnelle Erstellung von Seiten, beste Wahl für statische Inhalte | Serverseitig gebunden |
API | Sehr schnell, einfach umzusetzen, hohe Sicherheit | Keine GUI, muss von anderen Applikationen genutzt werden, nicht direkt mit den anderen Projekttypen vergleichbar |