From ac39bb52096501b85a4a6a0c0892723e12147e61 Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Fri, 17 Sep 2021 23:30:18 +0200 Subject: [PATCH] Rename .Net to DotNet --- DotNet/ASP.NET/App Configuration.md | 177 ++ DotNet/ASP.NET/Blazor.md | 120 + DotNet/ASP.NET/Filters.md | 134 + DotNet/ASP.NET/MVC.md | 259 ++ DotNet/ASP.NET/Middleware.md | 207 ++ DotNet/ASP.NET/REST API.md | 336 +++ DotNet/ASP.NET/Razor Pages.md | 236 ++ DotNet/ASP.NET/Razor Syntax.md | 164 ++ DotNet/ASP.NET/WebForms/Page.aspx.cs.md | 26 + DotNet/ASP.NET/WebForms/Page.aspx.md | 58 + DotNet/C#/Async Programming.md | 88 + DotNet/C#/C# Collections.md | 305 +++ DotNet/C#/C#.md | 2904 ++++++++++++++++++++++ DotNet/C#/LINQ.md | 76 + DotNet/C#/Reactive Extensions.md | 39 + DotNet/Database/ADO.NET.md | 113 + DotNet/Database/EntityFramework.md | 203 ++ DotNet/Godot/Scripting.md | 138 + DotNet/Lib/SkiaSharp.md | 35 + DotNet/Unity/Collisions.md | 81 + DotNet/Unity/Coroutines.md | 30 + DotNet/Unity/Input Manager.md | 81 + DotNet/Unity/Prefabs Instantiation.md | 14 + DotNet/Unity/Raycasting.md | 83 + DotNet/Unity/Scripting.md | 135 + DotNet/Unity/Vector, Tranfrorm, Space.md | 87 + DotNet/Xamarin/App.xaml.cs.md | 35 + DotNet/Xamarin/App.xaml.md | 51 + DotNet/Xamarin/Page.xaml.cs.md | 22 + DotNet/Xamarin/Page.xaml.md | 86 + DotNet/Xamarin/ViewModel.cs.md | 35 + DotNet/Xamarin/Xamarin.Essentials.md | 41 + 32 files changed, 6399 insertions(+) create mode 100644 DotNet/ASP.NET/App Configuration.md create mode 100644 DotNet/ASP.NET/Blazor.md create mode 100644 DotNet/ASP.NET/Filters.md create mode 100644 DotNet/ASP.NET/MVC.md create mode 100644 DotNet/ASP.NET/Middleware.md create mode 100644 DotNet/ASP.NET/REST API.md create mode 100644 DotNet/ASP.NET/Razor Pages.md create mode 100644 DotNet/ASP.NET/Razor Syntax.md create mode 100644 DotNet/ASP.NET/WebForms/Page.aspx.cs.md create mode 100644 DotNet/ASP.NET/WebForms/Page.aspx.md create mode 100644 DotNet/C#/Async Programming.md create mode 100644 DotNet/C#/C# Collections.md create mode 100644 DotNet/C#/C#.md create mode 100644 DotNet/C#/LINQ.md create mode 100644 DotNet/C#/Reactive Extensions.md create mode 100644 DotNet/Database/ADO.NET.md create mode 100644 DotNet/Database/EntityFramework.md create mode 100644 DotNet/Godot/Scripting.md create mode 100644 DotNet/Lib/SkiaSharp.md create mode 100644 DotNet/Unity/Collisions.md create mode 100644 DotNet/Unity/Coroutines.md create mode 100644 DotNet/Unity/Input Manager.md create mode 100644 DotNet/Unity/Prefabs Instantiation.md create mode 100644 DotNet/Unity/Raycasting.md create mode 100644 DotNet/Unity/Scripting.md create mode 100644 DotNet/Unity/Vector, Tranfrorm, Space.md create mode 100644 DotNet/Xamarin/App.xaml.cs.md create mode 100644 DotNet/Xamarin/App.xaml.md create mode 100644 DotNet/Xamarin/Page.xaml.cs.md create mode 100644 DotNet/Xamarin/Page.xaml.md create mode 100644 DotNet/Xamarin/ViewModel.cs.md create mode 100644 DotNet/Xamarin/Xamarin.Essentials.md diff --git a/DotNet/ASP.NET/App Configuration.md b/DotNet/ASP.NET/App Configuration.md new file mode 100644 index 0000000..d609a3b --- /dev/null +++ b/DotNet/ASP.NET/App Configuration.md @@ -0,0 +1,177 @@ +# ASP.NET Configuration + +## `Program.cs` + +```cs +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace App +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); // start and config ASP.NET Core App + + // or start Blazor WASM Single Page App + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("#app"); + + builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + + await builder.Build().RunAsync(); + } + + // for MVC, Razor Pages and Blazor Server + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); // config handled in Startup.cs + }); + } +} +``` + +## `Startup.cs` + +```cs +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace App +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the DI container. + public void ConfigureServices(IServiceCollection services) + { + // set db context for the app using the connection string + services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); + + // Captures synchronous and asynchronous Exception instances from the pipeline and generates HTML error responses. + services.AddDatabaseDeveloperPageExceptionFilter(); + + // use Razor Pages, runtime compilation needs Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation pkg + services.AddRazorPages().AddRazorRuntimeCompilation(); + // or + services.AddControllers(); // controllers w/o views + //or + sevices.AddControllersWithViews(); // MVC Controllers + //or + services.AddServerSideBlazor(); // needs Razor Pages + + // set dependency injection lifetimes + services.AddSingleton(); + services.AddScoped(); + services.AddTransient(); + + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } + app.UseHttpsRedirection(); + app.UseStaticFiles(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + // MVC routing + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}" + ); + // or + endpoints.MapControllers(); // map controllers w/o views + // or + endpoints.MapRazorPages(); + // or + endpoints.MapRazorHub(); + endpoints.MapFallbackToPage("/_Host"); // fallback for razor server + }); + } + } +} +``` + +## `appsettings.json` + +Connection Strings & Secrets. + +```json +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} +``` + +## `launchsettings.json` + +Launch Settings & Deployement Settings. + +```json +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51144", + "sslPort": 44335 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} +``` diff --git a/DotNet/ASP.NET/Blazor.md b/DotNet/ASP.NET/Blazor.md new file mode 100644 index 0000000..b216a44 --- /dev/null +++ b/DotNet/ASP.NET/Blazor.md @@ -0,0 +1,120 @@ +# Blazor + +Blazor apps are based on *components*. A **component** in Blazor is an element of UI, such as a page, dialog, or data entry form. + +Components are .NET C# classes built into .NET assemblies that: + +- Define flexible UI rendering logic. +- Handle user events. +- Can be nested and reused. +- Can be shared and distributed as Razor class libraries or NuGet packages. + +![Blazor Server Architecture](https://docs.microsoft.com/en-us/aspnet/core/blazor/index/_static/blazor-server.png) +![Blazor WASM Architecture](https://docs.microsoft.com/en-us/aspnet/core/blazor/index/_static/blazor-webassembly.png) + +The component class is usually written in the form of a Razor markup page with a `.razor` file extension. Components in Blazor are formally referred to as *Razor components*. + +## Project Structure & Important Files + +### Blazor Server Project Stucture + +```txt +Project +|-Properties +| |- launchSettings.json +| +|-wwwroot --> static files +| |-css +| | |- site.css +| | |- bootstrap +| | +| |- favicon.ico +| +|-Pages +| |- Page.cshtml +| |- Page.cshtml.cs +| |- Component.razor +| |- Index.razor +| |- ... +| +|-Shared +| |- MainLayout.razor +| |- MainLayout.razor.css +| |- ... +| +|- _Imports.razor --> @using imports +|- App.razor --> component root of the app +| +|- appsettings.json --> application settings +|- Program.cs --> App entrypoint +|- Startup.cs --> services and middleware configs +``` + +### Blazor WASM Project Structure + +```txt +Project +|-Properties +| |- launchSettings.json +| +|-wwwroot --> static files +| |-css +| | |- site.css +| | |- bootstrap +| | +| |- index.html +| |- favicon.ico +| +|-Pages +| |- Component.razor +| |- Index.razor +| |- ... +| +|-Shared +| |- MainLayout.razor +| |- MainLayout.razor.css +| |- ... +| +|- _Imports.razor --> @using imports +|- App.razor --> component root of the app +| +|- appsettings.json --> application settings +|- Program.cs --> App entrypoint +``` + +### `App.razor` + +```cs + + + + + + +

Sorry, there's nothing at this address.

+
+
+
+``` + +## Components (`.razor`) + +```cs +@page "/route" + + + + // insert component into page + +@code { + // component model (Properties, Methods, ...) + + [Parameter] // use prop as HTML attribute + purblic Type Property { get; set; } = defaultValue; +} +``` + + diff --git a/DotNet/ASP.NET/Filters.md b/DotNet/ASP.NET/Filters.md new file mode 100644 index 0000000..3317029 --- /dev/null +++ b/DotNet/ASP.NET/Filters.md @@ -0,0 +1,134 @@ +# [Filters](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters) + +**Filters** in ASP.NET Core allow code to be run *before* or *after* specific stages in the request processing pipeline. + +Built-in filters handle tasks such as: + +- Authorization (preventing access to resources a user isn't authorized for). +- Response caching (short-circuiting the request pipeline to return a cached response). + +Custom filters can be created to handle cross-cutting concerns. Examples of cross-cutting concerns include error handling, caching, configuration, authorization, and logging. Filters avoid duplicating code. + +## **How filters work** + +Filters run within the *ASP.NET Core action invocation pipeline*, sometimes referred to as the *filter pipeline*. The filter pipeline runs after ASP.NET Core selects the action to execute. + +![1][filter-pipeline-1] ![2][filter-pipeline-2] + +[filter-pipeline-1]: https://docs.microsoft.com/it-it/aspnet/core/mvc/controllers/filters/_static/filter-pipeline-1.png +[filter-pipeline-2]: https://docs.microsoft.com/en-gb/aspnet/core/mvc/controllers/filters/_static/filter-pipeline-2.png + +## **Filter types** + +Each filter type is executed at a different stage in the filter pipeline: + +- **Authorization filters** run first and are used to determine whether the user is authorized for the request. Authorization filters short-circuit the pipeline if the request is not authorized. +- **Resource filters**: + - Run after authorization. + - `OnResourceExecuting` runs code before the rest of the filter pipeline. For example, `OnResourceExecuting` runs code before model binding. + - `OnResourceExecuted` runs code after the rest of the pipeline has completed. +- **Action filters**: + - Run code immediately before and after an action method is called. + - Can change the arguments passed into an action. + - Can change the result returned from the action. + - Are **not** supported in Razor Pages. +- **Exception filters** apply global policies to unhandled exceptions that occur before the response body has been written to. +- **Result filters** run code immediately before and after the execution of action results. They run only when the action method has executed successfully. They are useful for logic that must surround view or formatter execution. + +## **Implementation** + +Filters support both synchronous and asynchronous implementations through different interface definitions. + +For example, `OnActionExecuting` is called before the action method is called. `OnActionExecuted` is called after the action method returns. +Asynchronous filters define an `On-Stage-ExecutionAsync` method, for example `OnActionExecutionAsync`. + +Interfaces for multiple filter stages can be implemented in a single class. + +## **Built-in filter attributes** + +ASP.NET Core includes built-in *attribute-based* filters that can be subclassed and customized. +Several of the filter interfaces have corresponding attributes that can be used as base classes for custom implementations. + +Filter attributes: + +- [ActionFilterAttribute](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters.actionfilterattribute) +- [ExceptionFilterAttribute](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters.exceptionfilterattribute) +- [ResultFilterAttribute](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters.resultfilterattribute) +- [FormatFilterAttribute](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.formatfilterattribute) +- [ServiceFilterAttribute](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.servicefilterattribute) +- [TypeFilterAttribute](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.typefilterattribute) + +## **Filter scopes** + +A filter can be added to the pipeline at one of three *scopes*: + +- Using an attribute on a controller action. Filter attributes cannot be applied to Razor Pages handler methods. + +```cs +// services.AddScoped(); +[ServiceFilter(typeof(CustomActionFilterAttribute))] +public IActionResult Index() +{ + return Content("Header values by configuration."); +} +``` + +- Using an attribute on a controller or Razor Page. + +```cs +// services.AddControllersWithViews(options => { options.Filters.Add(new CustomResponseFilterAttribute(args)); }); +[CustomResponseFilterAttribute(args)] +public class SampleController : Controller + +// or + +[CustomResponseFilterAttribute(args)] +[ServiceFilter(typeof(CustomActionFilterAttribute))] +public class IndexModel : PageModel +``` + +- Globally for all controllers, actions, and Razor Pages. + +```cs +public void ConfigureServices(IServiceCollection services) +{ + services.AddControllersWithViews(options => + { + options.Filters.Add(typeof(CustomActionFilter)); + }); +} +``` + +## Filter Order of Execution + +When there are multiple filters for a particular stage of the pipeline, scope determines the default order of filter execution. Global filters surround class filters, which in turn surround method filters. + +As a result of filter nesting, the *after* code of filters runs in the reverse order of the *before* code. The filter sequence: + +- The *before* code of global filters. + - The *before* code of controller and Razor Page filters. + - The *before* code of action method filters. + - The *after* code of action method filters. + - The *after* code of controller and Razor Page filters. +- The *after* code of global filters. + +### Cancellation and Short-Circuiting + +The filter pipeline can be short-circuited by setting the `Result` property on the `ResourceExecutingContext` parameter provided to the filter method. + +```cs +public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter +{ + public void OnResourceExecuting(ResourceExecutingContext context) + { + context.Result = new ContentResult() + { + Content = "Resource unavailable - header not set." + }; + } + + public void OnResourceExecuted(ResourceExecutedContext context) + { + } +} +``` diff --git a/DotNet/ASP.NET/MVC.md b/DotNet/ASP.NET/MVC.md new file mode 100644 index 0000000..b03d930 --- /dev/null +++ b/DotNet/ASP.NET/MVC.md @@ -0,0 +1,259 @@ +# ASP.NET (Core) MVC Web App + +## Project Structure + +```txt +Project +|-Properties +| |- launchSettings.json +| +|-wwwroot --> location of static files +| |-css +| | |- site.css +| | +| |-js +| | |- site.js +| | +| |-lib +| | |- bootstrap +| | |- jquery +| | |- ... +| | +| |- favicon.ico +| +|-Model +| |-ErrorViewModel.cs +| |- Index.cs +| |-... +| +|-Views +| |-Home +| | |- Index.cshtml +| | +| |-Shared +| | |- _Layout.cshtml --> reusable default page layout +| | |- _ValidationScriptsPartial --> jquery validation script imports +| | +| |- _ViewImports.cshtml --> shared imports and tag helpers for all views +| |- _ViewStart.cshtml --> shared values for all views +| |- ... +| +|-Controllers +| |-HomeController.cs +| +|- appsettings.json +|- Program.cs --> App entrypoiny +|- Startup.cs --> App config +``` + +**Note**: `_` prefix indicates page to be imported. + +## Controllers + +```cs +using Microsoft.AspNetCore.Mvc; +using App.Models; +using System.Collections.Generic; + +namespace App.Controllers +{ + public class CategoryController : Controller + { + private readonly AppDbContext _db; + + // get db context through dependency injection + public CategoryController(AppDbContext db) + { + _db = db; + } + + // GET /Controller/Index + public IActionResult Index() + { + IEnumerable enities = _db.Entities; + return View(Entities); // pass data to the @model + } + + // GET /Controller/Create + public IActionResult Create() + { + return View(); + } + + // POST /Controller/Create + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult Create(Entity entity) // recieve data from the @model + { + _db.Entities.Add(entity); + _db.SaveChanges(); + return RedirectToAction("Index"); // redirection + } + + // GET - /Controller/Edit + public IActionResult Edit(int? id) + { + if(id == null || id == 0) + { + return NotFound(); + } + + Entity entity = _db.Entities.Find(id); + if (entity == null) + { + return NotFound(); + } + + return View(entity); // return pupulated form for updating + } + + // POST /Controller/Edit + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult Edit(Entity entity) + { + if (ModelState.IsValid) // all rules in model have been met + { + _db.Entities.Update(entity); + _db.SaveChanges(); + return RedirectToAction("Index"); + } + + return View(entity); + } + + // GET /controller/Delete + public IActionResult Delete(int? id) + { + if (id == null || id == 0) + { + return NotFound(); + } + + Entity entity = _db.Entities.Find(id); + if (entity == null) + { + return NotFound(); + } + + return View(entity); // return pupulated form for confirmation + } + + // POST /Controller/Delete + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult Delete(Entity entity) + { + if (ModelState.IsValid) // all rules in model have been met + { + _db.Entities.Remove(entity); + _db.SaveChanges(); + return RedirectToAction("Index"); + } + + return View(entity); + } + } +} +``` + +## Data Validation + +### Model Annotations + +In `Entity.cs`: + +```cs +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; + +namespace App.Models +{ + public class Entity + { + [DisplayName("Integer Number")] + [Required] + [Range(1, int.MaxValue, ErrorMessage = "Error Message")] + public int IntProp { get; set; } + } +} +``` + +### Tag Helpers & Client Side Validation + +In `View.cshtml`; + +```cs +
+
+ +
+
+ +
+ +
+ + // error message displyed here +
+
+
+ +// client side validation +@section Scripts{ + @{ } +} +``` + +### Server Side Validation + +```cs +using Microsoft.AspNetCore.Mvc; +using App.Models; +using System.Collections.Generic; + +namespace App.Controllers +{ + public class CategoryController : Controller + { + private readonly AppDbContext _db; + + // get db context through dependency injection + public CategoryController(AppDbContext db) + { + _db = db; + } + + // GET /Controller/Index + public IActionResult Index() + { + IEnumerable enities = _db.Entities; + return View(Entities); // pass data to the @model + } + + // GET /Controller/Create + public IActionResult Create() + { + return View(); + } + + // POST /Controller/Create + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult Create(Entity entity) // recieve data from the @model + { + if (ModelState.IsValid) // all rules in model have been met + { + _db.Entities.Add(entity); + _db.SaveChanges(); + return RedirectToAction("Index"); + } + + return View(entity); // return model and display error messages + } + } +} +``` diff --git a/DotNet/ASP.NET/Middleware.md b/DotNet/ASP.NET/Middleware.md new file mode 100644 index 0000000..28206db --- /dev/null +++ b/DotNet/ASP.NET/Middleware.md @@ -0,0 +1,207 @@ +# [Middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware) + +Middleware is software that's assembled into an app pipeline to handle requests and responses. Each component: + +- Chooses whether to pass the request to the next component in the pipeline. +- Can perform work before and after the next component in the pipeline. + +Request delegates are used to build the request pipeline. The request delegates handle each HTTP request. + +Request delegates are configured using [Run][Run_docs], [Map][Map_docs], and [Use][Use_docs] extension methods. + +An individual request delegate can be specified in-line as an anonymous method (called in-line middleware), or it can be defined in a reusable class. +These reusable classes and in-line anonymous methods are *middleware*, also called *middleware components*. + +Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline or short-circuiting the pipeline. +When a middleware short-circuits, it's called a *terminal middleware* because it prevents further middleware from processing the request. + +[Use_docs]: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.useextensions.use +[Run_docs]: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.runextensions.run +[Map_docs]: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.mapextensions.map + +## Middleware Pipeline + +The ASP.NET Core request pipeline consists of a sequence of request delegates, called one after the other. + +![request-delegate-pipeline](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index/_static/request-delegate-pipeline.png) + +Each delegate can perform operations before and after the next delegate. Exception-handling delegates should be called early in the pipeline, so they can catch exceptions that occur in later stages of the pipeline. It's possible to chain multiple request delegates together with `Use`. + +The *next* parameter represents the next delegate in the pipeline. It's possible to short-circuit the pipeline by *not calling* the next parameter. +When a delegate doesn't pass a request to the next delegate, it's called *short-circuiting the request pipeline*. +Short-circuiting is often desirable because it avoids unnecessary work. + +It's possible to perform actions both *before* and *after* the next delegate: + +```cs +public class Startup +{ + public void Configure(IApplicationBuilder app) + { + // "inline" middleware, best if in own class + app.Use(async (context, next) => + { + // Do work that doesn't write to the Response. + await next.Invoke(); + // Do logging or other work that doesn't write to the Response. + }); + } +} +``` + +`Run` delegates don't receive a next parameter. The first `Run` delegate is always terminal and terminates the pipeline. + +```cs +public class Startup +{ + public void Configure(IApplicationBuilder app) + { + // "inline" middleware, best if in own class + app.Use(async (context, next) => + { + // Do work that doesn't write to the Response. + await next.Invoke(); + // Do logging or other work that doesn't write to the Response. + }); + + app.Run(async context => + { + // no invocation of next + }); + } +} +``` + +## Middleware Order + +![middleware-pipeline](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index/_static/middleware-pipeline.svg) +![mvc-endpoint](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index/_static/mvc-endpoint.svg) + +The Endpoint middleware executes the filter pipeline for the corresponding app type. + +The order that middleware components are added in the `Startup.Configure` method defines the order in which the middleware components are invoked on requests and the reverse order for the response. The order is **critical** for security, performance, and functionality. + +```cs +public void Configure(IApplicationBuilder app, IWebHostEnvironment env) +{ + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseDatabaseErrorPage(); + } + else + { + app.UseExceptionHandler("/Error"); + app.UseHsts(); + } + + app.UseHttpsRedirection(); + app.UseStaticFiles(); + // app.UseCookiePolicy(); + + app.UseRouting(); + // app.UseRequestLocalization(); + // app.UseCors(); + + app.UseAuthentication(); + app.UseAuthorization(); + // app.UseSession(); + // app.UseResponseCompression(); + // app.UseResponseCaching(); + + app.UseEndpoints(endpoints => + { + endpoints.MapRazorPages(); + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + }); +} +``` + +[Built-in Middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/#built-in-middleware) + +## Branching the Middleware Pipeline + +`Map` extensions are used as a convention for branching the pipeline. `Map` branches the request pipeline based on matches of the given request path. +If the request path starts with the given path, the branch is executed. + +When `Map` is used, the matched path segments are removed from `HttpRequest.Path` and appended to `HttpRequest.PathBase` for each request. + +`MapWhen` branches the request pipeline based on the result of the given predicate. +Any *predicate* of type `Func` can be used to map requests to a new branch of the pipeline. + +`UseWhen` also branches the request pipeline based on the result of the given predicate. +Unlike with `MapWhen`, this branch is rejoined to the main pipeline if it doesn't short-circuit or contain a terminal middleware. + +## Custom Middleware Classes + +Middleware is generally encapsulated in a class and exposed with an extension method. + +```cs +using Microsoft.AspNetCore.Http; +using System.Globalization; +using System.Threading.Tasks; + +namespace +{ + public class CustomMiddleware + { + private readonly RequestDelegate _next; + + public RequestCultureMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context) + { + // Do work that doesn't write to the Response. + await _next(context); // Call the next delegate/middleware in the pipeline + // Do logging or other work that doesn't write to the Response. + } + } +} +``` + +The middleware class **must** include: + +- A public constructor with a parameter of type [RequestDelegate][RequestDelegate_docs]. +- A public method named `Invoke` or `InvokeAsync`. This method must: + - Return a `Task`. + - Accept a first parameter of type [HttpContext][HttpConrext_Docs]. + +[RequestDelegate_docs]: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.requestdelegate +[HttpConrext_Docs]: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.httpcontext + +## Middleware Extension Methods + +```cs +using Microsoft.AspNetCore.Builder; + +namespace +{ + public static class MiddlewareExtensions + { + public static IApplicationBuilder UseCustom(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} +``` + +```cs +// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. +public void Configure(IApplicationBuilder app, IWebHostEnvironment env) +{ + // other middlewares + + app.UseCustom(); // add custom middleware in the pipeline + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); +} +``` diff --git a/DotNet/ASP.NET/REST API.md b/DotNet/ASP.NET/REST API.md new file mode 100644 index 0000000..47d0c12 --- /dev/null +++ b/DotNet/ASP.NET/REST API.md @@ -0,0 +1,336 @@ +# ASP .NET REST API + +## Startup class + +- Called by `Program.cs` + +```cs +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); // controllers w/o views + //or + sevices.AddControllersWithViews(); // MVC Controllers + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} +``` + +## DB Context (EF to access DB) + +NuGet Packages to install: + +- `Microsoft.EntityFrameworkCore` +- `Microsoft.EntityFrameworkCore.Tools` +- `Microsoft.EntityFrameworkCore.Design` *or* `Microsoft.EntityFrameworkCore..Design` +- `Microsoft.EntityFrameworkCore.` + +In `AppDbContext.cs`: + +```cs +using .Model; +using Microsoft.EntityFrameworkCore; + +namespace .Repo +{ + public class AppDbContext : DbContext + { + public AppDbContext(DbContextOptions options) : base(options) + { + + } + //DBSet represents the collection of all entities in the context, or that can be queried from the database, of a given type + public DbSet entities { get; set; } + } +} +``` + +In `appsettings.json`: + +```json +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "CommanderConnection" : "Server=;Database=;UID=;Pwd=" + } +} +``` + +In `Startup.cs`: + +```cs +// This method gets called by the runtime. Use this method to add services to the container. +public void ConfigureServices(IServiceCollection services) +{ + // SqlServer is the db used in this example + services.AddDbContext(option => option.UseSqlServer(Configuration.GetConnectionString("CommanderConnection"))); + services.AddControllers(); +} +``` + +### Migrations + +- Mirroring of model in the DB. +- Will create & update DB Schema if necessary + +In Packge Manager Shell: + +```ps1 +add-migrations +update-database # use the migrations to modify the db +``` + +## Repository + +In `IEntityRepo`: + +```cs +using .Model; +using System.Collections.Generic; + +namespace .Repository +{ + public interface IEntityRepo + { + IEnumerable SelectAll(); + Entity SelectOneById(int id); + + ... + } +} +``` + +In `EntityRepo`: + +```cs +using .Model; +using System.Collections.Generic; + +namespace .Repo +{ + public class EntityRepo : IEntityRepo + { + private readonly AppDbContext _context; + + public EntityRepo(AppDbContext context) + { + _context = context; + } + + public IEnumerable SelectAll() + { + return _context.Entities.ToList(); // linq query (ToList()) becomes sql query + } + + public Entity SelectOneById(int id) + { + return _context.Entities.FirstOrDefault(p => p.Id == id); + } + + ... + } +} +``` + +## Data Transfer Objects (DTOs) + +A **DTO** is an object that defines how the data will be sent and recieved over the network (usually as JSON). +Without a DTO the JSON response (or reqest) could contain irrilevant, wrong or unformatted data. +Moreover, by decoupling the JSON response from the actual data model, it's possible to change the latter without breaking the API. +DTOs must be mapped to the internal methods. + +Required NuGet Packages: + +- AutoMapper.Extensions.Microsoft.DependencyInjection + +In `StartUp.cs`: + +```cs +using AutoMapper; + +// ... + +public void ConfigureServices(IServiceCollection services) +{ + // other services + + services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); // set automapper service +} +``` + +In `EntityDTO.cs`: + +```cs +namespace .DTOs +{ + // define the data to be serialized in JSON (can differ from model) + public class EntityCrudOpDTO // e.g: EntityReadDTO, ... + { + // only properties to be serialized + } +} +``` + +In `EntitiesProfile.cs`: + +```cs +using AutoMapper; +using .DTOs; +using .Model; + +namespace .Profiles +{ + public class EntitiesProfile : Profile + { + public EntitiesProfile() + { + CreateMap(); // map entity to it's DTO + } + } +} +``` + +## Controller (No View) + +Uses [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) to recieve a suitable implementation of `IEntityRepo`, + +### Service Lifetimes + +- `AddSignleton`: same for every request +- `AddScoped`: created once per client +- `Transient`: new instance created every time + +In `Startup.cs`: + +```cs +// This method gets called by the runtime. Use this method to add services to the container. +public void ConfigureServices(IServiceCollection services) +{ + services.AddControllers(); + services.AddScoped(); // map the interface to its implementation, needed for dependency injection +} +``` + +### Request Mappings + +```cs +using .Model; +using .Repo; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; + +namespace .Controllers +{ + [Route("api/endpoint")] + [ApiController] + public class EntitiesController : ControllerBase // MVC controller w/o view + { + private readonly ICommandRepo _repo; + private readonly IMapper _mapper; // AutoMapper class + + public EntitiesController(IEntityRepo repository, IMapper mapper) // injection og the dependency + { + _repo = repository; + _mapper = mapper + } + + [HttpGet] // GET api/endpoint + public ActionResult> SelectAllEntities() + { + var results = _repo.SelectAll(); + + return Ok(_mapper.Map(results)); // return an action result OK (200) with the results + } + + // default binding source: [FromRoute] + [HttpGet("{id}")] // GET api/endpoint/{id} + public ActionResult SelectOneEntityById(int id) + { + var result = _repo.SelectOneById(id); + + if(result != null) + { + return Ok(_mapper.Map(result)); // transfrom entity to it's DTO + } + + return NotFound(); // ActionResult NOT FOUND (404) + } + } +} +``` + +## Controller (With View) + +```cs +using .Model; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace .Controllers +{ + [Route("api/endpoint")] + [ApiController] + public class EntitiesController : Controller + { + private readonly AppDbContext _db; + + public EntitiesController(AppDbContext db) + { + _db = db; + } + + [HttpGet] + public IActionResult SelectAll() + { + return Json(new { data = _db.Entities.ToList() }); // json view + } + } +} +``` diff --git a/DotNet/ASP.NET/Razor Pages.md b/DotNet/ASP.NET/Razor Pages.md new file mode 100644 index 0000000..1442230 --- /dev/null +++ b/DotNet/ASP.NET/Razor Pages.md @@ -0,0 +1,236 @@ +# Razor Pages + +## Project Structure + +```txt +Project +|-Properties +| |- launchSettings.json +| +|-wwwroot --> static files +| |-css +| | |- site.css +| | +| |-js +| | |- site.js +| | +| |-lib +| | |- jquery +| | |- bootstrap +| | |- ... +| | +| |- favicon.ico +| +|-Pages +| |-Shared +| | |- _Layout.cshtml --> reusable default page layout +| | |- _ValidationScriptsPartial --> jquery validation script imports +| | +| |- _ViewImports.cshtml --> shared imports and tag helpers for all views +| |- _ViewStart.cshtml --> shared values for all views +| |- Index.cshtml +| |- Index.cshtml.cs +| |- ... +| +|- appsettings.json --> application settings +|- Program.cs --> App entrypoint +|- Startup.cs +``` + +**Note**: `_` prefix indicates page to be imported + +Razor Pages components: + +- Razor Page (UI/View - `.cshtml`) +- Page Model (Handlers - `.cshtml.cs`) + +in `Index.cshtml`: + +```cs +@page // mark as Razor Page +@model IndexModel // Link Page Model + +@{ + ViewData["Title"] = "Page Title" // same as Page Title +} + +// body contents +``` + +in `Page.cshtml.cs`: + +```cs +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace App.Pages +{ + public class IndexModel : PageModel + { + // HTTP Method + public void OnGet() { } + + // HTTP Method + public void OnPost() { } + } +} +``` + +## Razor Page + +```cs +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace App.Pages +{ + public class IndexModel : PageModel + { + private readonly ApplicationDbContext _db; // EF DB Context + + // Get DBContex through DI + public IndexModel(ApplicationDbContext db) + { + _db = db; + } + + [BindProperty] // assumed to be recieved on POST + public IEnumerable Entities { get; set; } + + // HTTP Method Handler + public aysnc Task OnGet() + { + // get data from DB (example operation) + Entities = await _db.Entities.ToListAsync(); + } + + // HTTP Method Handler + public async Task OnPost() + { + if (ModelState.IsValid) + { + // save to DB (example operation) + await _db.Entities.AddAsync(Entity); + await _db.SaveChangesAsync(); + + return RedirectToPage("Index"); + } + else + { + return Page(); + } + } + } +} +``` + +## Routing + +Rules: + +- URL maps to a physical file on disk +- Razor paged needs a root folder (Default "Pages") +- file extension not included in URL +- `Index.cshtml` is enrtypoint and default document (missing file in URL redirects to index) + +| URL | Maps TO | +|------------------------|----------------------------------------------------| +| www.domain.com | /Pages/Index.cshtml | +| www.doamin.com/Index | /Pages/Index.html | +| www.domain.com/Account | /Pages/Account.cshtml, /Pages/Account/Index.cshtml | + +## Data Validation + +### Model Annotations + +In `Entity.cs`: + +```cs +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; + +namespace App.Models +{ + public class Entity + { + [DisplayName("Integer Number")] + [Required] + [Range(1, int.MaxValue, ErrorMessage = "Error Message")] + public int IntProp { get; set; } + } +} +``` + +### Tag Helpers & Client Side Validation + +In `View.cshtml`; + +```cs +
+
+ +
+
+ +
+ +
+ + // error message displyed here +
+
+
+ +// client side validation +@section Scripts{ + @{ } +} +``` + +### Server Side Validation + +```cs +using Microsoft.AspNetCore.Mvc; +using App.Models; +using System.Collections.Generic; + +namespace App.Controllers +{ + public class IndexModel : PageModel + { + private readonly ApplicationDbContext _db; + + // get db context through dependency injection + public IndexModel(AppDbContext db) + { + _db = db; + } + + [BindProperty] + public Entity Entity { get; set; } + + public async Task OnGet(int id) + { + Entity = await _db.Entities.FindAsync(id); + } + + public async Task OnPost() + { + if (ModelState.IsValid) + { + + await _db.SaveChangesAsync(); + + return RedirectToPage("Index"); + } + else + { + return Page(); + } + } + } +} +``` diff --git a/DotNet/ASP.NET/Razor Syntax.md b/DotNet/ASP.NET/Razor Syntax.md new file mode 100644 index 0000000..1809542 --- /dev/null +++ b/DotNet/ASP.NET/Razor Syntax.md @@ -0,0 +1,164 @@ +# [Razor Syntax](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor) + +## Markup + +```cs +@page // set this as razor page + +@model .Models.Entity // if MVC set type of elements passed to the view +@model Model // if Razor page set underlying class + +@* razor comment *@ + +// substituite @variable with it's value +@variable + +@{ + // razor code block + // can contain C# or HTML + + Model // acces to passed @model (MVC) +} + +@if (condition) { } + +@for (init, condition, iteration) { } + +@Model.Property // display Property value (MVC) +``` + +--- + +## Tag Helpers (ASP.NET Core) + +**Tag helpers** are reusable components for automating the generation of HTML in Razor Pages. Tag helpers target specific HTML tags. + +Example: + +```html + +Home +``` + +### Managing Tag Helpers + +The `@addTagHelper` directive makes Tag Helpers available to the view. Generally, the view file is `Pages/_ViewImports.cshtml`, which by default is inherited by all files in the `Pages` folder and subfolders, making Tag Helpers available. + +```cs +@using +@namespace .Pages // or .Models +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +``` + +The first parameter after `@addTagHelper` specifies the Tag Helpers to load (`*` for all Tag Helpers), and the second parameter (e.g. `Microsoft.AspNetCore.Mvc.TagHelpers`) specifies the assembly containing the Tag Helpers. +`Microsoft.AspNetCore.Mvc.TagHelpers` is the assembly for the built-in ASP.NET Core Tag Helpers. + +#### Opting out of individual elements + +It's possible to disable a Tag Helper at the element level with the Tag Helper opt-out character (`!`) + +```cshtml + + +``` + +### Explicit Tag Helpers + +The `@tagHelperPrefix` directive allows to specify a tag prefix string to enable Tag Helper support and to make Tag Helper usage explicit. + +```cshtml +@tagHelpersPrefix th: +``` + +### Important Tag Helpers (`asp-`) & HTML Helpers (`@Html`) + +[Understanding Html Helpers](https://stephenwalther.com/archive/2009/03/03/chapter-6-understanding-html-helpers) + +```cs +@model .Models.Entity + +// Display the name of the property +@Html.DisplayNameFor(model => model.EntityProp) +@nameof(Model.EntityProp) + +// Display the value of the property +@Html.DisplayFor(model => model.EntityProp) +@Model.EntityProp + + + // use the property as the label, eventyally w/ [DysplayName("...")] + + @Html.LabelFor() + + // automatically set the value at form compilation and submission + + @Html.EditorFor() + + +// route config is {Controller}/{Action}/{Id?} +Link // link to /Controller/Action +Link // link to /Controller/Action/Id +@Html.ActionLink("", "", "", new { @HTmlAttribute = value, Id = value }) // link to /Controller/Action/Id + +// link to /Controller/Action?queryParameter=value +@Html.ActionLink("", "", "", new { @HTmlAttribute = value, queryParameter = value }) +Link // asp-route-* for query strings +``` + +### [Select Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms) + +[StackOverflow](https://stackoverflow.com/a/34624217) +[SelectList Docs](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.selectlist) + +In `ViewModel.cs`: + +```cs +class ViewModel +{ + public int EntityId { get; set; } // value selected in form ends up here + + // object has numeric id and other props + public SelectList Entities = new() + + public ViewModel(){ } // parameterless constructor (NEEDED) +} +``` + +In `View.cs` + +```cs +@model ViewModel + +
+ + + +