From d7f852bf72fdcc0d67669120bb0c8c49a5da844a Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Sun, 28 Mar 2021 23:04:10 +0200 Subject: [PATCH 1/9] Add Godot scripting starting notes --- .NET/Godot/Scripting.md | 140 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 .NET/Godot/Scripting.md diff --git a/.NET/Godot/Scripting.md b/.NET/Godot/Scripting.md new file mode 100644 index 0000000..0297414 --- /dev/null +++ b/.NET/Godot/Scripting.md @@ -0,0 +1,140 @@ +# Godot Scripting + +## Basics + +```cs +using Godot; + + +public class NodeName : NodeType +{ + [Export] // make variable visible in inspector + Type variable = value; + + + + // Called when the node enters the scene tree for the first time. + public override void _Ready() + { + GetNode("NodeName"); // fetch a child node in the scene + GetNode("ParentNode/ChildNode"); // fetch a child node in the scene + + AddToGroup("Group"); // add a node to a group (similar to tags) + + GetTree().CallGroup("Group", "Function"); // call Function on all group members + var groupMembers = GetTree().GetNodesInGroup("Group"); + } + + // Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(float delta) + { + + } + + public void _OnEmitterSignal() { } +} +``` + +### Overridable Functions + +```cs +public override void _EnterTree() +{ + // When the node enters the Scene Tree, it becomes active and this function is called. + // Children nodes have not entered the active scene yet. + // In general, it's better to use _ready() for most cases. + base._EnterTree(); +} + +public override void _Ready() +{ + // This function is called after _enter_tree, but it ensures + // that all children nodes have also entered the Scene Tree, + // and became active. + base._Ready(); +} + +public override void _ExitTree() +{ + // When the node exits the Scene Tree, this function is called. + // Children nodes have all exited the Scene Tree at this point and all became inactive. + base._ExitTree(); +} + +public override void _Process(float delta) +{ + // This function is called every frame. + base._Process(delta); +} + +public override void _PhysicsProcess(float delta) +{ + // This is called every physics frame. + base._PhysicsProcess(delta); +} +``` + +### Creating Nodes + +```cs +private Sprite _sprite; + +public override void _Ready() +{ + base._Ready(); + + _sprite = new Sprite(); // Create a new sprite + + AddChild(_sprite); // Add it as a child of this node + _sprite.Free(); // Immediately removes the node from the scene and frees it. +} +``` + +**Note**: When a node is freed, it also frees all its child nodes. + +The safest way to delete a node is by using `Node.QueueFree()`. This erases the node safely during idle. + +### Instantiting Scenes + +```cs +// STEP 1: load the scene +var scene = GD.Load("res://scene.tscn"); // Will load when the script is instanced. + +// STEP 2: instantiate the scene-node +var node = scene.Instance(); +AddChild(node); +``` + +The advantage of this two-step process is that a packed scene may be kept loaded and ready to use so that it's possible to create as many instances as desired. +This is especially useful to quickly instance several enemies, bullets, and other entities in the active scene. + +## Signals + +Signals are Godot's version of the *observer* pattern. They allow a node to send out a message that other nodes can listen for and respond to. + +Signals are a way to decouple game objects, which leads to better organized and more manageable code. Instead of forcing game objects to expect other objects to always be present, they can instead emit signals that all interested objects can subscribe to and respond to. + +```cs +public override _Ready() +{ + GetNode("Node").Connect("signal", targetNode, nameof(TargetFunction)); // connect node and signal +} + +// Signal Handler +public void OnEmitterSignal() { } +``` + +### Custom Signals + +```cs +public class Node : Node2D +{ + [Signal] + public delegate void CustomSignal(Type arg, ...); + + public override void _Ready() + { + EmitSignal(nameof(CustomSignal), args); + } +} +``` From bb533f11834ffcaf961aac4421b69c16d18bbcf1 Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Sun, 28 Mar 2021 23:04:10 +0200 Subject: [PATCH 2/9] Add Godot scripting starting notes --- .NET/Godot/Scripting.md | 140 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 .NET/Godot/Scripting.md diff --git a/.NET/Godot/Scripting.md b/.NET/Godot/Scripting.md new file mode 100644 index 0000000..0297414 --- /dev/null +++ b/.NET/Godot/Scripting.md @@ -0,0 +1,140 @@ +# Godot Scripting + +## Basics + +```cs +using Godot; + + +public class NodeName : NodeType +{ + [Export] // make variable visible in inspector + Type variable = value; + + + + // Called when the node enters the scene tree for the first time. + public override void _Ready() + { + GetNode("NodeName"); // fetch a child node in the scene + GetNode("ParentNode/ChildNode"); // fetch a child node in the scene + + AddToGroup("Group"); // add a node to a group (similar to tags) + + GetTree().CallGroup("Group", "Function"); // call Function on all group members + var groupMembers = GetTree().GetNodesInGroup("Group"); + } + + // Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(float delta) + { + + } + + public void _OnEmitterSignal() { } +} +``` + +### Overridable Functions + +```cs +public override void _EnterTree() +{ + // When the node enters the Scene Tree, it becomes active and this function is called. + // Children nodes have not entered the active scene yet. + // In general, it's better to use _ready() for most cases. + base._EnterTree(); +} + +public override void _Ready() +{ + // This function is called after _enter_tree, but it ensures + // that all children nodes have also entered the Scene Tree, + // and became active. + base._Ready(); +} + +public override void _ExitTree() +{ + // When the node exits the Scene Tree, this function is called. + // Children nodes have all exited the Scene Tree at this point and all became inactive. + base._ExitTree(); +} + +public override void _Process(float delta) +{ + // This function is called every frame. + base._Process(delta); +} + +public override void _PhysicsProcess(float delta) +{ + // This is called every physics frame. + base._PhysicsProcess(delta); +} +``` + +### Creating Nodes + +```cs +private Sprite _sprite; + +public override void _Ready() +{ + base._Ready(); + + _sprite = new Sprite(); // Create a new sprite + + AddChild(_sprite); // Add it as a child of this node + _sprite.Free(); // Immediately removes the node from the scene and frees it. +} +``` + +**Note**: When a node is freed, it also frees all its child nodes. + +The safest way to delete a node is by using `Node.QueueFree()`. This erases the node safely during idle. + +### Instantiting Scenes + +```cs +// STEP 1: load the scene +var scene = GD.Load("res://scene.tscn"); // Will load when the script is instanced. + +// STEP 2: instantiate the scene-node +var node = scene.Instance(); +AddChild(node); +``` + +The advantage of this two-step process is that a packed scene may be kept loaded and ready to use so that it's possible to create as many instances as desired. +This is especially useful to quickly instance several enemies, bullets, and other entities in the active scene. + +## Signals + +Signals are Godot's version of the *observer* pattern. They allow a node to send out a message that other nodes can listen for and respond to. + +Signals are a way to decouple game objects, which leads to better organized and more manageable code. Instead of forcing game objects to expect other objects to always be present, they can instead emit signals that all interested objects can subscribe to and respond to. + +```cs +public override _Ready() +{ + GetNode("Node").Connect("signal", targetNode, nameof(TargetFunction)); // connect node and signal +} + +// Signal Handler +public void OnEmitterSignal() { } +``` + +### Custom Signals + +```cs +public class Node : Node2D +{ + [Signal] + public delegate void CustomSignal(Type arg, ...); + + public override void _Ready() + { + EmitSignal(nameof(CustomSignal), args); + } +} +``` From 0278a9ad8b3e9c597da090e3b0e2d353cf956746 Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Tue, 7 Sep 2021 20:55:50 +0200 Subject: [PATCH 3/9] Add notes on git commit --- Git/git.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Git/git.md b/Git/git.md index 7112ca4..5b2b37f 100644 --- a/Git/git.md +++ b/Git/git.md @@ -1,4 +1,4 @@ -# Git Cheatsheet +# Git ## Glossary @@ -65,7 +65,7 @@ def load(id): # human-readable names for SHA-1 hashes (mutable) references = map -# bind a regerence to a hash +# bind a reference to a hash def update_reference(name, id): references = id @@ -98,7 +98,7 @@ def load_reference(name_or_id): `git status`: shows the status of changes as untracked, modified, or staged `git add `: add files to the staging area -`git add -p `: interacively stage chuncks of a file +`git add -p `: interactively stage chunks of a file `git blame `: show who last edited which line @@ -111,7 +111,7 @@ def load_reference(name_or_id): `git diff `: show differences in a file since a particular snapshot `git diff `: show differences in a file between two snapshots `git diff --cached`: show what is about to be committed -`git diff ...`: show content diff between two branches +`git diff ...`: show content diff between two branches `git bisect`: binary search history (e.g. for regressions) @@ -166,14 +166,14 @@ def load_reference(name_or_id): Git supports two types of tags: *lightweight* and *annotated*. -A lightweight tag is very much like a branch that doesn’t change—it’s just a pointer to a specific commit. +A lightweight tag is very much like a branch that doesn't change—it's just a pointer to a specific commit. Annotated tags, however, are stored as full objects in the Git database. -They’re checksummed;contain the tagger name, email, and date; have a tagging message; and can be signed and verifiedwith GNU Privacy Guard (GPG). -It’s generally recommended creating annotated tags so it's possible to have all this information. +They're checksummed;contain the tagger name, email, and date; have a tagging message; and can be signed and verified with GNU Privacy Guard (GPG). +It's generally recommended creating annotated tags so it's possible to have all this information. `git tag`: list existing tags -`git tag -l|--list `: list existing tags mathcing a wildard or pattern +`git tag -l|--list `: list existing tags matching a wildcard or pattern `git tag []`: create a *lightweight* tag on the commit `git tag -a [ -m ]`: create am *annotated* tag on the commit @@ -197,10 +197,10 @@ It’s generally recommended creating annotated tags so it's possible to have al `git branch`: show list of all existing branches (* indicates current) `git checkout `: change current branch (update HEAD) and update working directory `git branch -d `: delete specified branch -`git branch -m `: rename a branch without affecting the branch’s history +`git branch -m `: rename a branch without affecting the branch's history `git merge `: merges into current branch -`git merge --continue`: continue previous merge after solving a merge conflinct +`git merge --continue`: continue previous merge after solving a merge conflict `git mergetool`: use a fancy tool to help resolve merge conflicts `git rebase`: rebase set of patches onto a new base `git rebase -i`: interactive rebasing @@ -210,6 +210,7 @@ It’s generally recommended creating annotated tags so it's possible to have al ### Undo & [Rewriting History](https://www.themoderncoder.com/rewriting-git-history/) `git commit --amend`: replace last commit by creating a new one (can add files or rewrite commit message) +`git commit --amend -m "amended message"`: replace last commit by creating a new one (can add files or rewrite commit message) `git commit --amend --no-edit`: replace last commit by creating a new one (can add files or rewrite commit message) `git reset HEAD `: unstage a file `git reset `: undo all commits after specified commit, preserving changes locally @@ -234,7 +235,7 @@ git pull # get up to date git checkout git rebase # rebase commits on master (moves branch start point on last master commit) -git chechout +git checkout git rebase # moves commits from the branch on top of master ``` @@ -255,7 +256,7 @@ git pull # clone branch ```ps1 git fetch upstream # Fetch the branches and their respective commits from the upstream repository -git checkout main # chechout fork's main primary branch +git checkout main # checkout fork's main primary branch git merge upstream/main # Merge the changes from the upstream default branch into the local default branch From 1c9cf283ec86df5773bde21cf7f23e4f87397793 Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Wed, 8 Sep 2021 13:39:45 +0200 Subject: [PATCH 4/9] Add notes on async context and async composition --- DotNet/C#/Async Programming.md | 81 ++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/DotNet/C#/Async Programming.md b/DotNet/C#/Async Programming.md index 4eaf77c..48a3e39 100644 --- a/DotNet/C#/Async Programming.md +++ b/DotNet/C#/Async Programming.md @@ -24,7 +24,7 @@ public async Task MethodAsync TResult result = await resultTask; // if the is no work to be done before awaiting - TResutl result = await obj.OtherMethodAsync(); + TResult result = await obj.OtherMethodAsync(); return result; } @@ -54,7 +54,7 @@ In combination with the `Task.Run` method, async programming is better than `Bac ### Naming Convention -By convention, methods that return commonly awaitable types (for example, `Task`, `Task`, `ValueTask`, `ValueTask`) should have names that end with "Async". Methods that start an asynchronous operation but do not return an awaitable type should not have names that end with "Async", but may start with "Begin", "Start", or some other verb to suggest this method does not return or throw the result of the operation. +By convention, methods that return commonly awaitable types (for example, `Task`, `Task`, `ValueTask`, `ValueTask`) should have names that end with *Async*. Methods that start an asynchronous operation but do not return an awaitable type should not have names that end with *Async*, but may start with "Begin", "Start", or some other verb to suggest this method does not return or throw the result of the operation. ## [Async Return Types](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/async-return-types) @@ -73,7 +73,7 @@ The `Task.Result` property is a **blocking property**. If it's accessed The `void` return type is used in asynchronous event handlers, which require a `void` return type. For methods other than event handlers that don't return a value, it's best to return a `Task` instead, because an async method that returns `void` can't be awaited. Any caller of such a method must continue to completion without waiting for the called async method to finish. The caller must be independent of any values or exceptions that the async method generates. -The caller of a void-returning async method *can't catch exceptions thrown from the method*, and such unhandled exceptions are likely to cause the application to fail. If a method that returns a `Task` or `Task` throws an exception, the exception is stored in the returned task. The exception is rethrown when the task is awaited. Therefore, make sure that any async method that can produce an exception has a return type of `Task` or `Task` and that calls to the method are awaited. +The caller of a void-returning async method *can't catch exceptions thrown from the method*, and such unhandled exceptions are likely to cause the application to fail. If a method that returns a `Task` or `Task` throws an exception, the exception is stored in the returned task. The exception is re-thrown when the task is awaited. Therefore, make sure that any async method that can produce an exception has a return type of `Task` or `Task` and that calls to the method are awaited. ### Generalized async return types and `ValueTask` @@ -86,3 +86,78 @@ Because `Task` and `Task` are **reference types**, memory allocation in ### Async streams with `IAsyncEnumerable` Starting with C# 8.0, an async method may return an async stream, represented by `IAsyncEnumerable`. An async stream provides a way to enumerate items read from a stream when elements are generated in chunks with repeated asynchronous calls. + +### Async Composition + +```cs +public async Task DoOperationsConcurrentlyAsync() +{ + Task[] tasks = new Task[3]; + tasks[0] = DoOperation0Async(); + tasks[1] = DoOperation1Async(); + tasks[2] = DoOperation2Async(); + + // At this point, all three tasks are running at the same time. + + // Now, we await them all. + await Task.WhenAll(tasks); +} + +public async Task GetFirstToRespondAsync() +{ + // Call two web services; take the first response. + Task[] tasks = new[] { WebService1Async(), WebService2Async() }; + + // Await for the first one to respond. + Task firstTask = await Task.WhenAny(tasks); + + // Return the result. + return await firstTask; +} +``` + +### Context & Avoiding Context + +What exactly is the "context"? + +- on a UI thread it's a UI context. +- when responding to an ASP.NET request, then it's an ASP.NET request context. +- Otherwise, it's usually a thread pool context. + +```cs +private async Task DownloadFileAsync(string fileName) +{ + // Use HttpClient or whatever to download the file contents. + var fileContents = await DownloadFileContentsAsync(fileName).ConfigureAwait(false); + + // Note that because of the ConfigureAwait(false), we are not on the original context here. + // Instead, we're running on the thread pool. + + // Write the file contents out to a disk file. + await WriteToDiskAsync(fileName, fileContents).ConfigureAwait(false); + + // The second call to ConfigureAwait(false) is not *required*, but it is Good Practice. +} + +// UI Example +private async void DownloadFileButton_Click(object sender, EventArgs e) +{ + // Since we asynchronously wait, the UI thread is not blocked by the file download. + await DownloadFileAsync(fileNameTextBox.Text); + + // Since we resume on the UI context, we can directly access UI elements. + resultTextBox.Text = "File downloaded!"; +} + +// ASP.NET example +protected async void MyButton_Click(object sender, EventArgs e) +{ + // Since we asynchronously wait, the ASP.NET thread is not blocked by the file download. + // This allows the thread to handle other requests while we're waiting. + await DownloadFileAsync(...); + + // Since we resume on the ASP.NET context, we can access the current request. + // We may actually be on another *thread*, but we have the same ASP.NET request context. + Response.Write("File downloaded!"); +} +``` \ No newline at end of file From 04eb974d71874f6a5d955690e8f1c6640e91084e Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Thu, 9 Sep 2021 11:30:18 +0200 Subject: [PATCH 5/9] Add notes about Mocking --- DotNet/C#/Unit Testing.md | 52 +++++++++++---------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/DotNet/C#/Unit Testing.md b/DotNet/C#/Unit Testing.md index 0159c0e..b9668f3 100644 --- a/DotNet/C#/Unit Testing.md +++ b/DotNet/C#/Unit Testing.md @@ -1,42 +1,5 @@ # Unit Testing -## MSTest - -[Microsoft Unit Testing Tutorial](https://docs.microsoft.com/en-us/visualstudio/test/walkthrough-creating-and-running-unit-tests-for-managed-code?view=vs-2019) - -To test a project add a **MSTest Test Projet** to the solution. - -The test runner will execute any methods marked with `[TestInitialize]` once for every test the class contains, and will do so before running the actual test method itself. -The `[TestMethod]` attribute tells the test runner which methods represent tests. - -In `TestClass.cs`: - -```cs -using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Project.Tests -{ - [TestClass] - public class TestClass - { - [TestMethod] - public void TestMethod() - { - Assert.AreEqual(expected, actual); - Assert.IsTrue(bool); - Assert.IsFalse(bool); - Assert.IsNotNull(nullable); - - // assertions on collections - CollectionAssert.AreEqual(expexcted, actual), - } - } -} -``` - ---- - [UnitTest Overloaded Methods](https://stackoverflow.com/a/5666591/8319610) [Naming standards for unit tests](https://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html) @@ -64,3 +27,18 @@ namespace Project.Tests } } ``` + +## Mocking with Moq + +```cs +var mockObj = new Mock(); + +mockObj.Setup(m => m.Method(It.IsAny())).Returns(value); +mockObj.Object; // get mock + +// check that the invocation is forwarded to the mock, n times +mockObj.Verify(m => m.Method(It.IsAny()), Times.Once()); + +// check that the invocation is forwarded to the mock with a specific input +mockObj.Verify(m => m.Method(input), Times.Once()); +``` \ No newline at end of file From 3bf43bc55b5fd74d777f43d23f86933e05f787c7 Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Thu, 9 Sep 2021 11:54:24 +0200 Subject: [PATCH 6/9] Add notes on XUnit test setup --- DotNet/C#/Unit Testing.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/DotNet/C#/Unit Testing.md b/DotNet/C#/Unit Testing.md index b9668f3..0eb214e 100644 --- a/DotNet/C#/Unit Testing.md +++ b/DotNet/C#/Unit Testing.md @@ -28,6 +28,13 @@ namespace Project.Tests } ``` +### Test Setup & Teardown + +xUnit.net creates a new instance of the test class for every test that is run, so any code which is placed into the constructor of the test class will be run for every single test. +This makes the constructor a convenient place to put reusable context setup code. + +For context cleanup, add the `IDisposable` interface to the test class, and put the cleanup code in the `Dispose()` method. + ## Mocking with Moq ```cs From 8a746346258563f75acd40b365db057362ee7fff Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Thu, 16 Sep 2021 14:54:30 +0200 Subject: [PATCH 7/9] Remove duplicate units section, fix typos --- CSS/CSS.md | 120 +++++++++++++++++++++-------------------------------- 1 file changed, 47 insertions(+), 73 deletions(-) diff --git a/CSS/CSS.md b/CSS/CSS.md index 07cfd3c..89dae18 100644 --- a/CSS/CSS.md +++ b/CSS/CSS.md @@ -5,7 +5,7 @@ ### Inline CSS ```html - + ``` Uses the HTML style attribute. @@ -20,14 +20,14 @@ Not recommended except in cases where choices are constrained. selector { property: value; /* box and display */ - /* alignement, position */ + /* alignment, position */ /* margin, border, padding */ /* max/min height-width*/ /* colors */ /* font */ - /* text alignement */ + /* text alignment */ } @@ -42,7 +42,7 @@ Not Recommended, only use when the number of rules is small and there are constr ```html - + ``` @@ -52,7 +52,7 @@ Easier to maintain, especially in larger projects. ## Selectors -Tthe selector points to the html element to style. +The selector points to the html element to style. ```css selector {property: value; property: value} @@ -87,7 +87,7 @@ Should apply to one element on a page. ### Class Selector -Many elements can have the same class, calsses are used to group HTML elements togheter. +Many elements can have the same class, classes are used to group HTML elements together. ```css .selector {property: value;} /* selects */ @@ -291,19 +291,19 @@ selector:pseudo-class { property: value; } `a:hover {...}` selects a link when the mouse rolls over it (hover state). `a:active {...}` selects the link while it's being activated (clicked or otherwise). -`selector:focus {...}` selects an element whent the user focuses on it (e.g. tab w/ keyboard). Often used on links, inputs, textareas. +`selector:focus {...}` selects an element when the user focuses on it (e.g. tab w/ keyboard). Often used on links, inputs, text-areas. ### User Interface State Pseudo-Classes -`input:enabled {...}` selects an input that is in the default state of enabled and aviable for use. -`input:disabled {...}` selects an input that has the attibute. +`input:enabled {...}` selects an input that is in the default state of enabled and available for use. +`input:disabled {...}` selects an input that has the attribute. `input:checked {...}` selects checkboxes or radio buttons that are checked. `input:indeterminate {...}` selects checkboxes or radio button that has neither selected nor unselected. ### Structural & Position Pseudo-Classes -`selector:first-child {...}` selects an element if it’s the first child within its parent. -`selector:last-child {...}` selects an element if it’s the last element within its parent. +`selector:first-child {...}` selects an element if it's the first child within its parent. +`selector:last-child {...}` selects an element if it's the last element within its parent. `selector:only-child {...}` will select an element if it is the only element within a parent. `selector:first-of-type {...}` selects the first element of its type within a parent. @@ -339,44 +339,45 @@ When used within selectors allow unique parts of the page to be stylized. Only o ```css a::after/before { - propery: value; + property: value; content: " (" attr(attribute_name) ")"; - propery: value; + property: value; } ``` ### Fragment Pseudo-Elements -`selector::selection {...}` identifies part of the document that has been selected, or highlighted, by a user’s actions. +`selector::selection {...}` identifies part of the document that has been selected, or highlighted, by a user's actions. `selector::-moz-selection {...}` Mozilla prefixed fragment pseudo-element has been added to ensure the best support for all browser. ## Units ### Absolute Length units -| Unit | Name | Equivalent to | -| ---- | ------------------- | -------------------- | -| cm | centimeters | 1cm = 38px = 25/64in | -| mm | Millimeters | 1mm = 1/10th of 1cm | -| Q | Quarter-millimeters | 1Q = 1/40th of 1cm | -| in | Inches | 1in = 2.54cm = 96px | -| pc | Picas | 1pc = 1/6th of 1in | -| pt | Points | 1pt = 1/72th of 1in | -| px | Pixels | 1px = 1/96th of 1in | +| Unit | Name | Equivalent to | +| ------ | ------------------- | -------------------- | +| `cm` | centimeters | 1cm = 38px = 25/64in | +| `mm` | Millimeters | 1mm = 1/10th of 1cm | +| `Q` | Quarter-millimeters | 1Q = 1/40th of 1cm | +| `in` | Inches | 1in = 2.54cm = 96px | +| `pc` | Picas | 1pc = 1/6th of 1in | +| `pt` | Points | 1pt = 1/72th of 1in | +| `px` | Pixels | 1px = 1/96th of 1in | ### Relative Length Units -| Unit | Relative to | -| ---- | ------------------------------------------------------------------- | -| rem | Font size of the root element. | -| em | Font size of the parent or the element itself | -| ex | x-height of the element's font. | -| ch | The advance measure (width) of the glyph "0" of the element's font. | -| lh | Line height of the element. | -| vw | 1% of the viewport's width. | -| vh | 1% of the viewport's height. | -| vmin | 1% of the viewport's smaller dimension. | -| vmax | 1% of the viewport's larger dimension. | +| Unit | Relative to | +| ------ | ------------------------------------------------------------------- | +| `rem` | Font size of the root element. | +| `em` | Font size of the parent or the element itself | +| `ex` | x-height of the element's font. | +| `ch` | The advance measure (width) of the glyph "0" of the element's font. | +| `lh` | Line height of the element. | +| `vw` | 1% of the viewport's width. | +| `vh` | 1% of the viewport's height. | +| `vmin` | 1% of the viewport's smaller dimension. | +| `vmax` | 1% of the viewport's larger dimension. | +| `%` | Relative to the parent element | ## Element Properties @@ -449,7 +450,7 @@ Can also accept Keywords: `left`, `center`, `right` and `top`, `center`, `bottom **Background-attachment** Specifies whether the background image should scroll with the page or be fixed -### Font Familty +### Font Family The font family defines which font is used. When listing multiple fonts, always list a generic name last. @@ -516,7 +517,7 @@ selector { ```css selector { text-decoration: line color style thickness; - text-align: alignement; + text-align: alignment; } ``` @@ -541,7 +542,7 @@ selector { ### Width Sets the width of a block-level element or img; does not work for inline elements (unless their display property is changed). -Accepts a veriety of length units. +Accepts a variety of length units. ```css selector { @@ -555,7 +556,7 @@ selector { ### Height Sets the height of a block-level element or img; does not work for inline elements (unless their display property is changed). -Accepts a veriety of length units. +Accepts a variety of length units. ```css selector { @@ -590,33 +591,6 @@ selector { } ``` -## CSS Units - -### Absolute Lengths - -| Symbol | Unit | -| ------ | ---------------------------- | -| `cm` | centimeters | -| `mm` | millimeters | -| `in` | inch (1 in = 96px = 2.54 cm) | -| `px` | pixel (1 px = 1/96 of 1 in) | -| `pt` | points (1 pt = 1/72 of 1 in) | -| `pc` | picas (1 pc = 12 pt) | - -### Relative Lengths - -| Symbol | Unit | -| ------ | ----------------------------------------------------------------------------------------- | -| `em` | Relative to the font-size of the element (2em means 2 times the size of the current font) | -| `ex` | Relative to the x-height of the current font (rarely used) | -| `ch` | Relative to width of the "0" (zero) | -| `rem` | Relative to font-size of the root element | -| `vw` | Relative to 1% of the width of the viewport* | -| `vh` | Relative to 1% of the height of the viewport* | -| `vmin` | Relative to 1% of viewport's* smaller dimension | -| `vmax` | Relative to 1% of viewport's* larger dimension | -| `%` | Relative to the parent element | - ## CSS Cascading The browser assigns different priorities to CSS depending on the type of selector. @@ -689,7 +663,7 @@ selector { #### Padding shortcuts ```css -selectopr { +selector { padding: top right bottom left; /* Four values (TRBL) */ padding: top right/left bottom; /* Three values (T/TL/B) */ padding: top/bottom right/left; /* Two values (TB/RL) */ @@ -728,9 +702,9 @@ selector { Defines whether the width and height (and min/max) of an element should include padding and borders or not. ```css -selecot { - box-sizinbg: content-box; /* Border and padding are not included */ - box-sizinbg: border-box; /* Include the content, padding and border */ +selector { + box-sizing: content-box; /* Border and padding are not included */ + box-sizing: border-box; /* Include the content, padding and border */ } ``` @@ -968,7 +942,7 @@ When elements overlap, the order of overlapping can be changed with z-index: - Without z-index, elements stack in the order that they appear in the DOM - Elements with non-static positioning will always appear on top of elements with default static positioning -**Nesting is importan**t: if element *B* is on top of element *A*, a child of element *A* can never be higher than element *B*. +**Nesting is important**: if element *B* is on top of element *A*, a child of element *A* can never be higher than element *B*. ```css selector { @@ -981,7 +955,7 @@ selector { ```css selector { display: grid; - grid-template-columns: 1fr 1fr 1fr; /* 3 equl columns */ + grid-template-columns: 1fr 1fr 1fr; /* 3 equal columns */ grid-template-rows: 1fr 1fr 1fr; /* 3 equal rows */ } ``` @@ -1043,7 +1017,7 @@ Risks miscommunications with clients and impacts the User Experience (UX). 3. **Use content to define the layout and design**: Understand how content can inform design - Don't spend time creating templates you won’t use + Don't spend time creating templates you won't use Focus on problem-solving for your users 4. **Use proto-content**: @@ -1156,7 +1130,7 @@ A media query is a logical expression: true or false; if a media query is true, | `all` | all media type devices | | `print` | printers | | `screen` | computer screens, tablets, smart-phones, etc | -| `speech` | screenreaders that "reads" the page out loud | +| `speech` | screen readers that "reads" the page out loud | ```css @media type operator (feature) { From f7115c4f8f87e01880db8fe7bb899bb7754f323b Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Fri, 17 Sep 2021 23:29:57 +0200 Subject: [PATCH 8/9] Rename .Net to DotNet --- .NET/ASP.NET/App Configuration.md | 177 -- .NET/ASP.NET/Blazor.md | 120 - .NET/ASP.NET/Filters.md | 134 -- .NET/ASP.NET/MVC.md | 259 --- .NET/ASP.NET/Middleware.md | 207 -- .NET/ASP.NET/REST API.md | 336 --- .NET/ASP.NET/Razor Pages.md | 236 -- .NET/ASP.NET/Razor Syntax.md | 164 -- .NET/ASP.NET/WebForms/Page.aspx.cs.md | 26 - .NET/ASP.NET/WebForms/Page.aspx.md | 58 - .NET/C#/Async Programming.md | 88 - .NET/C#/C# Collections.md | 305 --- .NET/C#/C#.md | 2904 ------------------------ .NET/C#/LINQ.md | 76 - .NET/C#/Reactive Extensions.md | 39 - .NET/Database/ADO.NET.md | 113 - .NET/Database/EntityFramework.md | 203 -- .NET/Godot/Scripting.md | 138 -- .NET/Lib/SkiaSharp.md | 35 - .NET/Unity/Collisions.md | 81 - .NET/Unity/Coroutines.md | 30 - .NET/Unity/Input Manager.md | 81 - .NET/Unity/Prefabs Instantiation.md | 14 - .NET/Unity/Raycasting.md | 83 - .NET/Unity/Scripting.md | 135 -- .NET/Unity/Vector, Tranfrorm, Space.md | 87 - .NET/Xamarin/App.xaml.cs.md | 35 - .NET/Xamarin/App.xaml.md | 51 - .NET/Xamarin/Page.xaml.cs.md | 22 - .NET/Xamarin/Page.xaml.md | 86 - .NET/Xamarin/ViewModel.cs.md | 35 - .NET/Xamarin/Xamarin.Essentials.md | 41 - 32 files changed, 6399 deletions(-) delete mode 100644 .NET/ASP.NET/App Configuration.md delete mode 100644 .NET/ASP.NET/Blazor.md delete mode 100644 .NET/ASP.NET/Filters.md delete mode 100644 .NET/ASP.NET/MVC.md delete mode 100644 .NET/ASP.NET/Middleware.md delete mode 100644 .NET/ASP.NET/REST API.md delete mode 100644 .NET/ASP.NET/Razor Pages.md delete mode 100644 .NET/ASP.NET/Razor Syntax.md delete mode 100644 .NET/ASP.NET/WebForms/Page.aspx.cs.md delete mode 100644 .NET/ASP.NET/WebForms/Page.aspx.md delete mode 100644 .NET/C#/Async Programming.md delete mode 100644 .NET/C#/C# Collections.md delete mode 100644 .NET/C#/C#.md delete mode 100644 .NET/C#/LINQ.md delete mode 100644 .NET/C#/Reactive Extensions.md delete mode 100644 .NET/Database/ADO.NET.md delete mode 100644 .NET/Database/EntityFramework.md delete mode 100644 .NET/Godot/Scripting.md delete mode 100644 .NET/Lib/SkiaSharp.md delete mode 100644 .NET/Unity/Collisions.md delete mode 100644 .NET/Unity/Coroutines.md delete mode 100644 .NET/Unity/Input Manager.md delete mode 100644 .NET/Unity/Prefabs Instantiation.md delete mode 100644 .NET/Unity/Raycasting.md delete mode 100644 .NET/Unity/Scripting.md delete mode 100644 .NET/Unity/Vector, Tranfrorm, Space.md delete mode 100644 .NET/Xamarin/App.xaml.cs.md delete mode 100644 .NET/Xamarin/App.xaml.md delete mode 100644 .NET/Xamarin/Page.xaml.cs.md delete mode 100644 .NET/Xamarin/Page.xaml.md delete mode 100644 .NET/Xamarin/ViewModel.cs.md delete mode 100644 .NET/Xamarin/Xamarin.Essentials.md diff --git a/.NET/ASP.NET/App Configuration.md b/.NET/ASP.NET/App Configuration.md deleted file mode 100644 index d609a3b..0000000 --- a/.NET/ASP.NET/App Configuration.md +++ /dev/null @@ -1,177 +0,0 @@ -# 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/.NET/ASP.NET/Blazor.md b/.NET/ASP.NET/Blazor.md deleted file mode 100644 index b216a44..0000000 --- a/.NET/ASP.NET/Blazor.md +++ /dev/null @@ -1,120 +0,0 @@ -# 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/.NET/ASP.NET/Filters.md b/.NET/ASP.NET/Filters.md deleted file mode 100644 index 3317029..0000000 --- a/.NET/ASP.NET/Filters.md +++ /dev/null @@ -1,134 +0,0 @@ -# [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/.NET/ASP.NET/MVC.md b/.NET/ASP.NET/MVC.md deleted file mode 100644 index b03d930..0000000 --- a/.NET/ASP.NET/MVC.md +++ /dev/null @@ -1,259 +0,0 @@ -# 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/.NET/ASP.NET/Middleware.md b/.NET/ASP.NET/Middleware.md deleted file mode 100644 index 28206db..0000000 --- a/.NET/ASP.NET/Middleware.md +++ /dev/null @@ -1,207 +0,0 @@ -# [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/.NET/ASP.NET/REST API.md b/.NET/ASP.NET/REST API.md deleted file mode 100644 index 47d0c12..0000000 --- a/.NET/ASP.NET/REST API.md +++ /dev/null @@ -1,336 +0,0 @@ -# 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/.NET/ASP.NET/Razor Pages.md b/.NET/ASP.NET/Razor Pages.md deleted file mode 100644 index 1442230..0000000 --- a/.NET/ASP.NET/Razor Pages.md +++ /dev/null @@ -1,236 +0,0 @@ -# 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/.NET/ASP.NET/Razor Syntax.md b/.NET/ASP.NET/Razor Syntax.md deleted file mode 100644 index 1809542..0000000 --- a/.NET/ASP.NET/Razor Syntax.md +++ /dev/null @@ -1,164 +0,0 @@ -# [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 - -
- - - -