Setup Mkdocs (#7)

* Compile docs with Mkdocs

* Add info about project
This commit is contained in:
Marcello 2022-05-26 19:11:28 +02:00
parent 4c5c361db7
commit 75b392d778
134 changed files with 549 additions and 1495 deletions

2925
docs/dotnet/C#/C#.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,195 @@
# [Async Programming](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/)
## Task Asynchronous Programming Model ([TAP][tap_docs])
[tap_docs]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model
It's possible to avoid performance bottlenecks and enhance the overall responsiveness of an application by using asynchronous programming.
However, traditional techniques for writing asynchronous applications can be complicated, making them difficult to write, debug, and maintain.
C# 5 introduced a simplified approach, **async programming**, that leverages asynchronous support in the .NET Runtime.
The compiler does the difficult work that the developer used to do, and the application retains a logical structure that resembles synchronous code.
In performance-sensitive code, asynchronous APIs are useful, because instead of wasting resources by forcing a thread to sit and wait for I/O to complete, a thread can kick off the work and then do something else productive in the meantime.
The `async` and `await` keywords in C# are the heart of async programming.
```cs
public async Task<TResult> MethodAsync
{
Task<TResult> resultTask = obj.OtherMethodAsync();
DoIndependentWork();
TResult result = await resultTask;
// if the is no work to be done before awaiting
TResult result = await obj.OtherMethodAsync();
return result;
}
```
Characteristics of Async Methods:
- The method signature includes an `async` modifier.
- The name of an async method, by convention, ends with an "Async" suffix.
- The return type is one of the following types:
- `Task<TResult>` if the method has a return statement in which the operand has type `TResult`.
- `Task` if the method has no return statement or has a return statement with no operand.
- `void` if it's an async event handler.
- Any other type that has a `GetAwaiter` method (starting with C# 7.0).
- Starting with C# 8.0, `IAsyncEnumerable<T>`, for an async method that returns an async stream.
The method usually includes at least one `await` expression, which marks a point where the method can't continue until the awaited asynchronous operation is complete.
In the meantime, the method is suspended, and control returns to the method's caller.
### Threads
Async methods are intended to be non-blocking operations. An `await` expression in an async method doesn't block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method.
The `async` and `await` keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active. It's possible to use `Task.Run` to move CPU-bound work to a background thread, but a background thread doesn't help with a process that's just waiting for results to become available.
The async-based approach to asynchronous programming is preferable to existing approaches in almost every case. In particular, this approach is better than the `BackgroundWorker` class for I/O-bound operations because the code is simpler and there is no need to guard against race conditions.
In combination with the `Task.Run` method, async programming is better than `BackgroundWorker` for CPU-bound operations because async programming separates the coordination details of running the code from the work that `Task.Run` transfers to the thread pool.
### Naming Convention
By convention, methods that return commonly awaitable types (for example, `Task`, `Task<T>`, `ValueTask`, `ValueTask<T>`) 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)
### `Task` return type
Async methods that don't contain a return statement or that contain a return statement that doesn't return an operand usually have a return type of `Task`. Such methods return `void` if they run synchronously.
If a `Task` return type is used for an async method, a calling method can use an `await` operator to suspend the caller's completion until the called async method has finished.
### `Task<TResult>` return type
The `Task<TResult>` return type is used for an async method that contains a return statement in which the operand is `TResult`.
The `Task<TResult>.Result` property is a **blocking property**. If it's accessed it before its task is finished, the thread that's currently active is blocked until the task completes and the value is available.
In most cases, access the value by using `await` instead of accessing the property directly.
### `void` return type
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<TResult>` 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<TResult>` and that calls to the method are awaited.
### Generalized async return types and `ValueTask<TResult>`
Starting with C# 7.0, an async method can return any type that has an accessible `GetAwaiter` method.
Because `Task` and `Task<TResult>` are **reference types**, memory allocation in performance-critical paths, particularly when allocations occur in tight loops, can adversely affect performance. Support for generalized return types means that it's possible to return a lightweight **value type** instead of a reference type to avoid additional memory allocations.
.NET provides the `System.Threading.Tasks.ValueTask<TResult>` structure as a lightweight implementation of a generalized task-returning value. To use the `System.Threading.Tasks.ValueTask<TResult>` type, add the **System.Threading.Tasks.Extensions** NuGet package to the project.
### 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<int> GetFirstToRespondAsync()
{
// Call two web services; take the first response.
Task<int>[] tasks = new[] { WebService1Async(), WebService2Async() };
// Await for the first one to respond.
Task<int> firstTask = await Task.WhenAny(tasks);
// Return the result.
return await firstTask;
}
```
### Execution & Synchronization Context
When the programs execution reaches an `await` expression for an operation that doesnt complete immediately, the code generated for that `await` will ensure that the
current execution context has been captured.
When the asynchronous operation completes, the remainder of the method will be executed through the execution context.
The execution context handles certain contextual information that needs to flow when one method invokes another (even when it does so indirectly)
While all `await` expressions capture the *execution context*, the decision of whether to flow *synchronization context* as well is controlled by the type being awaited.
Sometimes, it's better to avoid getting the synchronization context involved.
If work starting from a UI thread is performed, but there is no particular need to remain on that thread, scheduling every continuation through the synchronization context is unnecessary overhead.
If the asynchronous operation is a `Task`, `Task<T>`, `ValueTask` or `ValueTask<T>`, it's possible to discard the *synchronization context* by calling the `ConfigureAwait(false)`.
This returns a different representation of the asynchronous operation, and if this iss awaited that instead of the original task, it will ignore the current `SynchronizationContext` if there is one.
```cs
private async Task DownloadFileAsync(string fileName)
{
await OperationAsync(fileName).ConfigureAwait(false); // discarding original context
}
```
When writing libraries in most cases you it'ss best to call `ConfigureAwait(false)` anywhere `await` is used.
This is because continuing via the synchronization context can be expensive, and in some cases it can introduce the possibility of deadlock occurring.
The only exceptions are when are doing something that positively requires the synchronization context to be preserved, or it's know for certain that the library will only ever be used in
application frameworks that do not set up a synchronization context.
(ASP.NET Core applications do not use synchronization contexts, so it generally doesnt matter whether or not `ConfigureAwait(false)` is called in those)
## Error Handling
### Argument Validation
Inside an `async` method, the compiler treats all exceptions in the same way: none are allowed to pass up the stack as in a normal method, and they will always be reported by faulting the returned task.
This is true even of exceptions thrown before the first `await`.
If the calling method immediately calls `await` on the return task, this wont matter much—it will see the exception in any case.
But some code may choose not to wait immediately, in which case it wont see the argument exception until later.
```cs
async Task<string> MethodWithValidationAsync(string argument)
{
if(sting.IsNullOrEmpty(argument))
{
throw new ArgumentNullException(nameof(argument)); // will be thrown on await of MethodWithValidationAsync
}
// [...]
return await LongOperationAsync();
}
```
In cases where you want to throw this kind of exception straightaway, the usual technique is to write a normal method that validates the arguments before calling an async method that does the
work, and to make that second method either private or local.
```cs
// not marked with async, exception propagate directly to caller
public static Task<string> MethodWithValidationAsync(string argument)
{
if(sting.IsNullOrEmpty(argument))
{
throw new ArgumentNullException(nameof(argument)); // thrown immediately
}
return ActualMethodAsync(argument); // pass up task of inner method
}
private static async Task<string> ActualMethodAsync(string argument)
{
// [...]
return await LongOperationAsync();
}
```
**NOTE**: `await` extracts only the first exception of an `AggregateException`, this can cause the loss of information if a task (or group of tasks) has more than one error.

View file

@ -0,0 +1,296 @@
# C# Collections
## Arrays
An array is an object that contains multiple elements of a particular type. The number of elements is fixed for the lifetime of the array, so it must be specified when the array is created.
An array type is always a reference type, regardless of the element type. Nonetheless, the choice between reference type and value type elements makes a significant difference in an array's behavior.
```cs
type[] array = new type[dimension];
type array[] = new type[dimension]; //invalid
type[] array = {value1, value2, ..., valueN}; // initializer
var array = new type[] {value1, value2, ..., valueN}; // initializer (var type needs new operator)
var array = new[] {value1, value2, ..., valueN}; // initializer w/ element type inference (var type needs new operator), can be used as method arg
array[index]; // value access
array[index] = value; // value assignment
array.Length; // dimension of the array
// from IEnumerable<T>
array.OfType<Type>(); // filter array based on type, returns IEnumerable<Type>
```
### [Array Methods](https://docs.microsoft.com/en-us/dotnet/api/system.array?view=netcore-3.1#methods)
```cs
// overloaded search methods
Array.IndexOf(array, item); // return index of searched item in passed array
Array.LastIndexOf(array, item); // return index of searched item staring from the end of the array
Array.FindIndex(array, Predicate<T>) // returns the index of the first item matching the predicate (can be lambda function)
Array.FindLastIndex(array, Predicate<T>) // returns the index of the last item matching the predicate (can be lambda function)
Array.Find(array, Predicate<T>) // returns the value of the first item matching the predicate (can be lambda function)
Array.FindLast(array, Predicate<T>) // returns the value of the last item matching the predicate (can be lambda function)
Array.FindAll(array, Predicate<T>) // returns array of all items matching the predicate (can be lambda function)
Array.BinarySearch(array, value) // Searches a SORTED array for a value, using a binary search algorithm; returns the index of the found item
Array.Sort(array);
Array.Reverse(array); // reverses the order of array elements
Array.Clear(start_index, x); //removes reference to x elements starting at start index. Dimension of array unchanged (cleared elements value is set tu null)
Array.Resize(ref array, target_dimension); //expands or shrinks the array dimension. Shrinking drops trailing values. Array passed by reference.
// Copies elements from an Array starting at the specified index and pastes them to another Array starting at the specified destination index.
Array.Copy(sourceArray, sourceStartIndex, destinationArray, destinationStartIndex, numItemsToCopy);
// Copies elements from an Array starting at the first element and pastes them into another Array starting at the first element.
Array.Copy(sourceArray, destinationArray, numItemsToCopy);
Array.Clone(); // returns a shallow copy of the array
```
### Multidimensional Arrays
C# supports two multidimensional array forms: [jagged][jagg_arrays] arrays and [rectangular][rect_arrays] arrays (*matrices*).
[jagg_arrays]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/jagged-arrays
[rect_arrays]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/multidimensional-arrays
```cs
//specify first dimension
type[][] jagged = new type[][]
{
new[] {item1, item2, item3},
new[] {item1},
new[] {item1, item2},
...
}
// shorthand
type[][] jagged =
{
new[] {item1, item2, item3},
new[] {item1},
new[] {item1, item2},
...
}
// matrices
type[,] matrix = new type[n, m]; // n * m matrix
type[,] matrix = {{}, {}, {}, ...}; // {} for each row to initialize
type[, ,] tensor = new type[n, m, o] // n * m * o tensor
matrix.Length; // total number of elements (n * m)
matrix.GetLength(int dimension); // get the size of a particular direction
// row = 0, column = 1, ...
```
## [Lists](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1)
`List<T>` stores sequences of elements. It can grow or shrink, allowing to add or remove elements.
```cs
using System.Collections.Generics;
List<T> list = new List<T>();
List<T> list = new List<T> {item_1, ...}; // initialized usable since list implements IEnumerable<T> and has Add() method (even extension method)
List<T> list = new List<T>(dimension); // set list starting dimension
List<T> list = new List<T>(IEnumerable<T>); // create a list from an enumerable collection
list.Add(item); //item insertion into the list
list.AddRange(IEnumerable<T> collection); // insert multiple items
list.Insert(index, item); // insert an item at the specified index
list.InsertRange(index, item); // insert items at the specified index
list.IndexOf(item); // return index of searched item in passed list
list.LastIndexOf(item); // return index of searched item staring from the end of the array
list.FindIndex(Predicate<T>) // returns the index of the first item matching the predicate (can be lambda function)
list.FindLastIndex(Predicate<T>) // returns the index of the last item matching the predicate (can be lambda function)
list.Find(Predicate<T>) // returns the value of the first item matching the predicate (can be lambda function)
list.FindLast(Predicate<T>) // returns the value of the last item matching the predicate (can be lambda function)
list.FindAll(Predicate<T>) // returns list of all items matching the predicate (can be lambda function)
list.BinarySearch(value) // Searches a SORTED list for a value, using a binary search algorithm; returns the index of the found item
list.Remove(item); // remove item from list
list.RemoveAt(index); // remove item at specified position
list.RemoveRange(index, quantity); // remove quantity items at specified position
list.Contains(item); // check if item is in the list
list.TrueForAll(Predicate<T>); // Determines whether every element matches the conditions defined by the specified predicate
list[index]; // access to items by index
list[index] = value; // modify to items by index
list.Count; // number of items in the list
list.Sort(); // sorts item in crescent order
list.Reverse(); // Reverses the order of the elements in the list
// from IEnumerable<T>
list.OfType<Type>(); // filter list based on type, returns IEnumerable<Type>
list.OfType<Type>().ToList(); // filter list based on type, returns List<Type>
```
## [Iterators](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/iterators)
An iterator can be used to step through collections such as lists and arrays.
An iterator method or `get` accessor performs a custom iteration over a collection. An iterator method uses the `yield return` statement to return each element one at a time.
When a `yield return` statement is reached, the current location in code is remembered. Execution is restarted from that location the next time the iterator function is called.
It's possible to use a `yield break` statement or exception to end the iteration.
**Note**: Since an iterator returns an `IEnumerable<T>` is can be used to implement a `GetEnumerator()`.
```cs
// simple iterator
public static System.Collections.IEnumerable<int> IterateRange(int start = 0, int end)
{
for(int i = start; i < end; i++){
yield return i;
}
}
```
## List & Sequence Interfaces
### [`IEnumerable<T>`](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerable-1)
Exposes the enumerator, which supports a simple iteration over a collection of a specified type.
```cs
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator(); // return an enumerator
}
// iterate through a collection
public interface IEnumerator<T>
{
// properties
object Current { get; } // Get the element in the collection at the current position of the enumerator.
// methods
void IDisposable.Dispose(); // Perform application-defined tasks associated with freeing, releasing, or resetting unmanaged resources
bool MoveNext(); // Advance the enumerator to the next element of the collection.
void Reset(); // Set the enumerator to its initial position, which is before the first element in the collection.
}
```
**Note**: must call `Dispose()` on enumerators once finished with them, because many of them rely on this. `Reset()` is legacy and can, in some situations, throw `NotSupportedException()`.
### [`ICollection<T>`](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.icollection-1)
```cs
public interface ICollection<T> : IEnumerable<T>
{
// properties
int Count { get; } // Get the number of elements contained in the ICollection<T>
bool IsReadOnly { get; } // Get a value indicating whether the ICollection<T> is read-only
// methods
void Add (T item); // Add an item to the ICollection<T>
void Clear (); // Removes all items from the ICollection<T>
bool Contains (T item); // Determines whether the ICollection<T> contains a specific value
IEnumerator GetEnumerator (); // Returns an enumerator that iterates through a collection
bool Remove (T item); // Removes the first occurrence of a specific object from the ICollection<T>
}
```
### [`IList<T>`](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ilist-1)
```cs
public interface IList<T> : ICollection<T>, IEnumerable<T>
{
// properties
int Count { get; } // Get the number of elements contained in the ICollection<T>
bool IsReadOnly { get; } // Get a value indicating whether the ICollection<T> is read-only
T this[int index] { get; set; } // Get or set the element at the specified index
// methods
void Add (T item); // Add an item to the ICollection<T>
void Clear (); // Remove all items from the ICollection<T>
bool Contains (T item); // Determine whether the ICollection<T> contains a specific value
void CopyTo (T[] array, int arrayIndex); // Copy the elements of the ICollection<T> to an Array, starting at a particular Array index
IEnumerator GetEnumerator (); // Return an enumerator that iterates through a collection
int IndexOf (T item); // Determine the index of a specific item in the IList<T>
void Insert (int index, T item); // Insert an item to the IList<T> at the specified index
bool Remove (T item); // Remove the first occurrence of a specific object from the ICollection<T>
oid RemoveAt (int index); // Remove the IList<T> item at the specified index
}
```
## [Dictionaries](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2)
[ValueCollection](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.valuecollection)
[KeyCollection](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.keycollection)
**Notes**:
- Enumerating a dictionary will return `KeyValuePair<TKey, TValue>`.
- The `Dictionary<TKey, TValue>` collection class relies on hashes to offer fast lookup (`TKey` should have a good `GetHashCode()`).
```cs
Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>(); // init empty dict
Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>(IEqualityComparer<TKey>); // specify key comparer (TKey must implement Equals() and GetHashCode())
// initializer (implicitly uses Add method)
Dictionary<TKey, TValue> dict =
{
{ key, value }
{ key, value },
...
}
// object initializer
Dictionary<TKey, TValue> dict =
{
[key] = value,
[key] = value,
...
}
// indexer access
dict[key]; // read value associated with key (throws KeyNotFoundException if key does not exist)
dict[key] = value; // modify value associated with key (throws KeyNotFoundException if key does not exist)
dict.Count; // number of key-value pair stored in the dict
dict.Keys; // Dictionary<TKey,TValue>.KeyCollection containing the keys of the dict
dict.Values; // Dictionary<TKey,TValue>.ValueCollection containing the values of the dict
dict.Add(key, value); // ArgumentException if the key already exists
dict.Clear(); // empty the dictionary
dict.ContainsKey(key); // check if a key is in the dictionary
dict.ContainsValue(value); // check if a value is in the dictionary
dict.Remove(key); // remove a key-value pair
dict.Remove(key, out var); // remove key-value pair and copy TValue to var parameter
dict.TryAdd(key, value); // adds a key-value pair; returns true if pair is added, false otherwise
dict.TryGetValue(key, out var); // put the value associated with kay in the var parameter; true if the dict contains an element with the specified key, false otherwise.
```
## [Sets](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1)
Collection of non duplicate items.
```cs
HashSet<T> set = new HashSet<T>();
set.Add(T); // adds an item to the set; true if the element is added, false if the element is already present.
set.Clear(); //Remove all elements from a HashSet<T> object.
set.Contains(T); // Determine whether a HashSet<T> object contains the specified element.
set.CopyTo(T[]); // Coy the elements of a HashSet<T> object to an array.
set.CopyTo(T[], arrayIndex); // Copy the elements of a HashSet<T> object to an array, starting at the specified array index.
set.CopyTo(T[], arrayIndex, count); // Copies the specified number of elements of a HashSet<T> object to an array, starting at the specified array index.
set.CreateSetComparer(); // Return an IEqualityComparer object that can be used for equality testing of a HashSet<T> object.
set.ExceptWith(IEnumerable<T>); // Remove all elements in the specified collection from the current HashSet<T> object.
set.IntersectWith(IEnumerable<T>); // Modify the current HashSet<T> object to contain only elements that are present in that object and in the specified collection.
set.IsProperSubsetOf(IEnumerable<T>); // Determine whether a HashSet<T> object is a proper subset of the specified collection.
set.IsProperSupersetOf(IEnumerable<T>); // Determine whether a HashSet<T> object is a proper superset of the specified collection.
set.IsSubsetOf(IEnumerable<T>); // Determine whether a HashSet<T> object is a subset of the specified collection.
set.IsSupersetOf(IEnumerable<T>); // Determine whether a HashSet<T> object is a superset of the specified collection.
set.Overlaps(IEnumerable<T>); // Determine whether the current HashSet<T> object and a specified collection share common elements.
set.Remove(T); // Remove the specified element from a HashSet<T> object.
set.RemoveWhere(Predicate<T>); // Remove all elements that match the conditions defined by the specified predicate from a HashSet<T> collection.
set.SetEquals(IEnumerable<T>); // Determine whether a HashSet<T> object and the specified collection contain the same elements.
set.SymmetricExceptWith(IEnumerable<T>); // Modify the current HashSet<T> object to contain only elements that are present either in that object or in the specified collection, but not both.
set.UnionWith(IEnumerable<T>); // Modify the current HashSet<T> object to contain all elements that are present in itself, the specified collection, or both.
set.TryGetValue(T, out T); // Search the set for a given value and returns the equal value it finds, if any.
```

84
docs/dotnet/C#/linq.md Normal file
View file

@ -0,0 +1,84 @@
# LINQ
## LINQ to Objects
<!-- Page: 423/761 of "Ian Griffiths - Programming C# 8.0 - Build Cloud, Web, and Desktop Applications.pdf" -->
The term **LINQ to Objects** refers to the use of LINQ queries with any `IEnumerable` or `IEnumerable<T>` collection directly, without the use of an intermediate LINQ provider or API such as [LINQ to SQL](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/linq/) or [LINQ to XML](https://docs.microsoft.com/en-us/dotnet/standard/linq/linq-xml-overview).
LINQ to Objects will be used when any `IEnumerable<T>` is specified as the source, unless a more specialized provider is available.
### Query Expressions
All query expressions are required to begin with a `from` clause, which specifies the source of the query.
The final part of the query is a `select` (or `group`) clause. This determines the final output of the query and its system type.
```cs
// query expression
var result = from item in enumerable select item;
// where clause
var result = from item in enumerable where condition select item;
// ordering
var result = from item in enumerable orderby item.property select item; // ordered IEnumerable
// let clause, assign expression to variable to avoid re-evaluation on each cycle
var result = from item in enumerable let tmp = <sub-expr> ... // BEWARE: compiled code has a lot of overhead to satisfy let clause
// grouping (difficult to re-implement to obtain better performance)
var result = from item in enumerable group item by item.property; // returns IEnumerable<IGrouping<TKey,TElement>>
```
### How Query Expressions Expand
The compiler converts all query expressions into one or more method calls. Once it has done that, the LINQ provider is selected through exactly the same mechanisms that C# uses for any other method call.
The compiler does not have any built-in concept of what constitutes a LINQ provider.
```cs
// expanded query expression
var result = Enumerable.Where(item => condition).Select(item => item);
```
The `Where` and `Select` methods are examples of LINQ operators. A LINQ operator is nothing more than a method that conforms to one of the standard patterns.
### Methods on `Enumerable` or `IEnumerable<T>`
```cs
Enumerable.Range(int start, int end); // IEnumerable<int> of values between start & end
IEnumerable<TSource>.Select(Func<TSource, TResult> selector); // map
IEnumerable<TSource>.Where(Func<T, bool> predicate); // filter
IEnumerable<T>.FirstOrDefault(); // first element of IEnumerable or default(T) if empty
IEnumerable<T>.FirstOrDefault(T default); // specify returned default
IEnumerable<T>.FirstOrDefault(Func<T, bool> predicate); // first element to match predicate or default(T)
// same for LastOrDefault & SingleOrDefault
IEnumerable<T>.Chunk(size); // chunk an enumerable into slices of a fixed size
// T must implement IComparable<T>
IEnumerable<T>.Max();
IEnumerable<T>.Min();
// allow finding maximal or minimal elements using a key selector
IEnumerable<TSource>.MaxBy(Func<TSource, TResult> selector);
IEnumerable<TSource>.MinBy(Func<TSource, TResult> selector);
IEnumerable<T>.All(Func<T, bool> predicate); // check if condition is true for all elements
IEnumerable<T>.Any(Func<T, bool> predicate); // check if condition is true for at least one element
IEnumerable<T>.Concat(IEnumerable<T> enumerable);
// Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
IEnumerable<TFirst>.Zip(IEnumerable<TSecond> enumerable, Func<TFirst, TSecond, TResult> func);
IEnumerable<TFirst>.Zip(IEnumerable<TSecond> enumerable); // Produces a sequence of tuples with elements from the two specified sequences.
```
**NOTE**: `Enumerable` provides a set of `static` methods for querying objects that implement `IEnumerable<T>`. Most methods are extensions of `IEnumerable<T>`
```cs
Enumerable.Method(IEnumerable<T> source, args);
// if extension method same as
IEnumerable<T>.Method(args);
```

View file

@ -0,0 +1,168 @@
# Reactive Extensions (Rx)
[ReactiveX](https://reactivex.io "ReactiveX website")
The **Reactive Extensions** for .NET, or **Rx**, are designed for working with asynchronous and event-based sources of information.
Rx provides services that help orchestrate and synchronize the way code reacts to data from these kinds of sources.
Rx's fundamental abstraction, `IObservable<T>`, represents a sequence of items, and its operators are defined as extension methods for this interface.
This might sound a lot like LINQ to Objects, and there are similarities, not only does `IObservable<T>` have a lot in common with `IEnumerable<T>`, but Rx also supports almost all of the standard LINQ operators.
The difference is that in Rx, sequences are less passive. Unlike `IEnumerable<T>`, Rx sources do not wait to be asked for their items, nor can the consumer
of an Rx source demand to be given the next item. Instead, Rx uses a *push* model in which *the source notifies* its recipients when items are available.
Because Rx implements standard LINQ operators, it's possible to write queries against a live source. Rx goes beyond standard LINQ, adding its own operators that take into account the temporal nature of a live event source.
## Fundamental Interfaces
The two most important types in Rx are the `IObservable<T>` and `IObserver<T>` interfaces.
They are important enough to be in the System namespace. The other parts of Rx are in the `System.Reactive` NuGet package.
```cs
public interface IObservable<out T>
{
IDisposable Subscribe(IObserver<T> observer);
}
public interface IObserver<in T>
{
void OnCompleted();
void OnError(Exception error);
void OnNext(T value);
}
```
The fundamental abstraction in Rx, `IObservable<T>`, is implemented by *event sources*. Instead of using the `event` keyword, it models events as a *sequence of items*.
An `IObservable<T>` provides items to subscribers as and when its ready to do so.
It's possible to subscribe to a source by passing an implementation of `IObserver<T>` to the `Subscribe` method.
The source will invoke `OnNext` when it wants to report events, and it can call `OnCompleted` to indicate that there will be no further activity.
If the source wants to report an error, it can call `OnError`.
Both `OnCompleted` and `OnError` indicate the end of the stream, an observable should not call any further methods on the observer after that.
## Operators
### Chaining Operators
Most operators operate on an Observable and return an Observable. This allows to apply these operators one after the other, in a chain.
Each operator in the chain modifies the Observable that results from the operation of the previous operator.
A chain of Observable operators do not operate independently on the original Observable that originates the chain,
but they operate *in turn*, each one operating on the Observable generated by the operator immediately previous in the chain.
### Creating Observables
Operators that originate new Observables.
- `Create`: create an Observable from scratch by calling observer methods programmatically
- `Defer`: do not create the Observable until the observer subscribes, and create a fresh Observable for each observer
- `Empty/Never/Throw`: create Observables that have very precise and limited behavior
- `From*`: convert some other object or data structure into an Observable
- `Interval`: create an Observable that emits a sequence of integers spaced by a particular time interval
- `Return (aka Just)`: convert an object or a set of objects into an Observable that emits that or those objects
- `Range`: create an Observable that emits a range of sequential integers
- `Repeat`: create an Observable that emits a particular item or sequence of items repeatedly
- `Start`: create an Observable that emits the return value of a function
- `Timer`: create an Observable that emits a single item after a given delay
### Transforming Observables
Operators that transform items that are emitted by an Observable.
- `Buffer`: periodically gather items from an Observable into bundles and emit these bundles rather than emitting the items one at a time
- `SelectMany (aka FlatMap)`: transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable
- `GroupBy`: divide an Observable into a set of Observables that each emit a different group of items from the original Observable, organized by key
- `Select (aka Map)`: transform the items emitted by an Observable by applying a function to each item
- `Scan`: apply a function to each item emitted by an Observable, sequentially, and emit each successive value
- `Window`: periodically subdivide items from an Observable into Observable windows and emit these windows rather than emitting the items one at a time
### Filtering Observables
Operators that selectively emit items from a source Observable.
- `Throttle (aka Debounce)`: only emit an item from an Observable if a particular timespan has passed without it emitting another item
- `Distinct`: suppress duplicate items emitted by an Observable
- `ElementAt`: emit only item n emitted by an Observable
- `Where (aka Filter)`: emit only those items from an Observable that pass a predicate test
- `First`: emit only the first item, or the first item that meets a condition, from an Observable
- `IgnoreElements`: do not emit any items from an Observable but mirror its termination notification
- `Last`: emit only the last item emitted by an Observable
- `Sample`: emit the most recent item emitted by an Observable within periodic time intervals
- `Skip`: suppress the first n items emitted by an Observable
- `SkipLast`: suppress the last n items emitted by an Observable
- `Take`: emit only the first n items emitted by an Observable
- `TakeLast`: emit only the last n items emitted by an Observable
### Combining Observables
Operators that work with multiple source Observables to create a single Observable
- `And/Then/When`: combine sets of items emitted by two or more Observables by means of Pattern and Plan intermediaries
- `CombineLatest`: when an item is emitted by either of two Observables, combine the latest item emitted by each Observable via a specified function and emit items based on the results of this function
- `Join`: combine items emitted by two Observables whenever an item from one Observable is emitted during a time window defined according to an item emitted by the other Observable
- `Merge`: combine multiple Observables into one by merging their emissions
- `StartWith`: emit a specified sequence of items before beginning to emit the items from the source Observable
- `Switch`: convert an Observable that emits Observables into a single Observable that emits the items emitted by the most-recently-emitted of those Observables
- `Zip`: combine the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function
### Error Handling Operators
Operators that help to recover from error notifications from an Observable
- `Catch`: recover from an onError notification by continuing the sequence without error
- `Retry`: if a source Observable sends an onError notification, resubscribe to it in the hopes that it will complete without error
### Observable Utility Operators
A toolbox of useful Operators for working with Observables
- `Delay`: shift the emissions from an Observable forward in time by a particular amount
- `Do`: register an action to take upon a variety of Observable lifecycle events
- `Materialize/Dematerialize`: represent both the items emitted and the notifications sent as emitted items, or reverse this process
- `ObserveOn`: specify the scheduler on which an observer will observe this Observable
- `Synchronize (aka Serialize)`: force an Observable to make serialized calls and to be well-behaved
- `Subscribe`: operate upon the emissions and notifications from an Observable
- `SubscribeOn`: specify the scheduler an Observable should use when it is subscribed to
- `TimeInterval`: convert an Observable that emits items into one that emits indications of the amount of time elapsed between those emissions
- `Timeout`: mirror the source Observable, but issue an error notification if a particular period of time elapses without any emitted items
- `Timestamp`: attach a timestamp to each item emitted by an Observable
- `Using`: create a disposable resource that has the same lifespan as the Observable
### Conditional and Boolean Operators
Operators that evaluate one or more Observables or items emitted by Observables
- `All`: determine whether all items emitted by an Observable meet some criteria
- `Amb`: given two or more source Observables, emit all of the items from only the first of these Observables to emit an item
- `Contains`: determine whether an Observable emits a particular item or not
- `DefaultIfEmpty`: emit items from the source Observable, or a default item if the source Observable emits nothing
- `SequenceEqual`: determine whether two Observables emit the same sequence of items
- `SkipUntil`: discard items emitted by an Observable until a second Observable emits an item
- `SkipWhile`: discard items emitted by an Observable until a specified condition becomes false
- `TakeUntil`: discard items emitted by an Observable after a second Observable emits an item or terminates
- `TakeWhile`: discard items emitted by an Observable after a specified condition becomes false
### Mathematical and Aggregate Operators
Operators that operate on the entire sequence of items emitted by an Observable
- `Average`: calculates the average of numbers emitted by an Observable and emits this average
- `Concat`: emit the emissions from two or more Observables without interleaving them
- `Count`: count the number of items emitted by the source Observable and emit only this value
- `Max`: determine, and emit, the maximum-valued item emitted by an Observable
- `Min`: determine, and emit, the minimum-valued item emitted by an Observable
- `Aggregate (aka Reduce)`: apply a function to each item emitted by an Observable, sequentially, and emit the final value
- `Sum`: calculate the sum of numbers emitted by an Observable and emit this sum
### Connectable Observable Operators
Specialty Observables that have more precisely-controlled subscription dynamics
- `Connect`: instruct a connectable Observable to begin emitting items to its subscribers
- `Publish`: convert an ordinary Observable into a connectable Observable
- `RefCount`: make a Connectable Observable behave like an ordinary Observable
- `Replay`: ensure that all observers see the same sequence of emitted items, even if they subscribe after the Observable has begun emitting items
### Operators to Convert Observables
- `To*`: convert an Observable into another object or data structure

View file

@ -0,0 +1,51 @@
# Unit Testing
[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)
## xUnit
```cs
using System;
using Xunit;
namespace Project.Tests
{
public class ClassTest
{
[Fact]
public void TestMethod()
{
Assert.Equal(expected, actual); // works on collections
Assert.True(bool);
Assert.False(bool);
Assert.NotNull(nullable);
// Verifies that all items in the collection pass when executed against action
Assert.All<T>(IEnumerable<T> collection, Action<T> action);
}
}
}
```
### 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
var mockObj = new Mock<MockedType>();
mockObj.Setup(m => m.Method(It.IsAny<InputType>())).Returns(value);
mockObj.Object; // get mock
// check that the invocation is forwarded to the mock, n times
mockObj.Verify(m => m.Method(It.IsAny<InputType>()), Times.Once());
// check that the invocation is forwarded to the mock with a specific input
mockObj.Verify(m => m.Method(input), Times.Once());
```

View file

@ -0,0 +1,244 @@
# ASP.NET Configuration
## `.csproj`
```xml
<PropertyGroup>
<!-- enable documentation comments (can be used for swagger) -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- do not warn public classes w/o documentation comments -->
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
```
## `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>("#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<Startup>(); // 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<AppDbContext>(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
services.AddControllersWithViews(); // MVC Controllers
//or
services.AddServerSideBlazor(); // needs Razor Pages
services.AddSignalR();
// set dependency injection lifetimes
services.AddSingleton<ITransientService, ServiceImplementation>();
services.AddScoped<ITransientService, ServiceImplementation>();
services.AddTransient<ITransientService, ServiceImplementation>();
// add swagger
services.AddSwaggerGen(options => {
// OPTIONAL: use xml comments for swagger documentation
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});
}
// 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.UseSwagger();
app.UseSwaggerUI();
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.MapBlazorHub(); // SignalR Hub for Blazor Server
endpoints.MapHub("/hub/endpoint"); // SignalR Hub
endpoints.MapFallbackToPage("/_Host"); // fallback for razor server
});
}
}
}
```
## Application Settings
App settings are loaded (in order) from:
1. `appsettings.json`
2. `appsettings.<Environment>.json`
3. User Secrets
The environment is controlled by the env var `ASPNETCORE_ENVIRONMENT`. If a setting is present in multiple locations, the last one is used and overrides the previous ones.
### User Secrets
User secrets are specific to each machine and can be initialized with `dotnet user-secrets init`. Each application is linked with it's settings by a guid.
The settings are stored in:
- `%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json` (Windows)
- `~/.microsoft/usersecrets/<user_secrets_id>/secrets.json` (Linux/macOS)
Setting a value is done with `dotnet user-secrets set <key> <value>`, keys can be nested by separating each level with `:` or `__`.
## Options Pattern
The *options pattern* uses classes to provide strongly-typed access to groups of related settings.
```json
{
"SecretKey": "Secret key value",
"TransientFaultHandlingOptions": {
"Enabled": true,
"AutoRetryDelay": "00:00:07"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
```
```cs
// options model for binding
public class TransientFaultHandlingOptions
{
public bool Enabled { get; set; }
public TimeSpan AutoRetryDelay { get; set; }
}
```
```cs
// setup the options
builder.Services.Configure<TransientFaultHandlingOptions>(builder.Configuration.GetSection<TransientFaultHandlingOptions>(nameof(Options)));
builder.Services.Configure<TransientFaultHandlingOptions>(builder.Configuration.GetSection<TransientFaultHandlingOptions>(key));
```
```cs
class DependsOnOptions
{
private readonly IOptions<TransientFaultHandlingOptions> _options;
public DependsOnOptions(IOptions<TransientFaultHandlingOptions> options) => _options = options;
}
```
### [Options interfaces](https://docs.microsoft.com/en-us/dotnet/core/extensions/options#options-interfaces)
`IOptions<TOptions>`:
- Does not support:
- Reading of configuration data after the app has started.
- Named options
- Is registered as a Singleton and can be injected into any service lifetime.
`IOptionsSnapshot<TOptions>`:
- Is useful in scenarios where options should be recomputed on every injection resolution, in scoped or transient lifetimes.
- Is registered as Scoped and therefore cannot be injected into a Singleton service.
- Supports named options
`IOptionsMonitor<TOptions>`:
- Is used to retrieve options and manage options notifications for `TOptions` instances.
- Is registered as a Singleton and can be injected into any service lifetime.
- Supports:
- Change notifications
- Named options
- Reloadable configuration
- Selective options invalidation (`IOptionsMonitorCache<TOptions>`)

View file

@ -0,0 +1,416 @@
# 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](../../img/dotnet_blazor-server.png)
![Blazor WASM Architecture](../../img/dotnet_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 Structure
```txt
Project
|-Properties
| |- launchSettings.json
|
|-wwwroot --> static files
| |-css
| | |- site.css
| | |- bootstrap
| |
| |- favicon.ico
|
|-Pages
| |- _Host.cshtml --> fallback page
| |- 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 entry-point
|- 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 entry-point
```
### Blazor PWA Project Structure
```txt
Project
|-Properties
| |- launchSettings.json
|
|-wwwroot --> static files
| |-css
| | |- site.css
| | |- bootstrap
| |
| |- index.html
| |- favicon.ico
| |- manifest.json
| |- service-worker.js
| |- icon-512.png
|
|-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
```
### `manifest.json`, `service-worker.js` (Blazor PWA)
[PWA](https://web.dev/progressive-web-apps/)
[PWA MDN Docs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps)
[PWA Manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest)
[Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
```json
// manifest.json
{
"name": "<App Name>",
"short_name": "<Short App Name>",
"start_url": "./",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#03173d",
"icons": [
{
"src": "icon-512.png",
"type": "image/png",
"sizes": "512x512"
}
]
}
```
## Common Blazor Files
### `App.razor`
```cs
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
```
### `MainLayout.razor` (Blazor Server/WASM)
```cs
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu /> // NavMenu Component
</div>
<div class="main">
<div class="top-row px-4">
</div>
<div class="content px-4">
@Body
</div>
</div>
</div>
```
### `_Host.cshtml` (Blazor Server)
```html
@page "/"
@namespace BlazorServerDemo.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BlazorServerDemo</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<link href="BlazorServerDemo.styles.css" rel="stylesheet" />
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js"></script>
</body>
</html>
```
## Components (`.razor`)
[Blazor Components](https://docs.microsoft.com/en-us/aspnet/core/blazor/components/)
```cs
@page "/route/{RouteParameter}" // make component accessible from a URL
@page "/route/{RouteParameter?}" // specify route parameter as optional
@page "/route/{RouteParameter:<type>}" // specify route parameter type
@namespace <Namespace> // set the component namespace
@using <Namespace> // using statement
@inherits BaseType // inheritance
@attribute [Attribute] // apply an attribute
@inject Type objectName // dependency injection
// html of the page here
<Namespace.ComponentFolder.Component /> // access component w/o @using
<Component Property="value"/> // insert component into page, passing attributes
<Component @onclick="@CallbackMethod">
@ChildContent // segment of UI content
</Component>
@code {
// component model (Properties, Methods, ...)
[Parameter] // capture attribute
public Type Property { get; set; } = defaultValue;
[Parameter] // capture route parameters
public type RouteParameter { get; set;}
[Parameter] // segment of UI content
public RenderFragment ChildContent { get; set;}
private void CallbackMethod() { }
}
```
## State Management
### Blazor WASM
```cs
// setup state singleton
builder.Services.AddSingleton<StateContainer>();
```
```cs
// StateContainer singleton
using System;
public class StateContainer
{
private int _counter;
public string Property
{
get => _counter;
set
{
_counter = value;
NotifyStateChanged(); // will trigger StateHasChanged(), causing a render
}
}
public event Action OnChange;
private void NotifyStateChanged() => OnChange?.Invoke();
}
```
```cs
// component that changes the state
@inject StateContainer State
// Delegate event handlers automatically trigger a UI render
<button @onClick="@HandleClick">
Change State
</button>
@code {
private void HandleClick()
{
State.Property += 1; // update state
}
}
```
```cs
// component that should be update on state change
@implements IDisposable
@inject StateContainer State
<p>Property: <b>@State.Property</b></p>
@code {
// StateHasChanged notifies the component that its state has changed.
// When applicable, calling StateHasChanged causes the component to be rerendered.
protected override void OnInitialized()
{
State.OnChange += StateHasChanged;
}
public void Dispose()
{
State.OnChange -= StateHasChanged;
}
}
```
## Data Binding & Events
```cs
<p>
<button @on{DOM EVENT}="{DELEGATE}" />
<button @on{DOM EVENT}="{DELEGATE}" @on{DOM EVENT}:preventDefault /> // prevent default action
<button @on{DOM EVENT}="{DELEGATE}" @on{DOM EVENT}:preventDefault="{CONDITION}" /> // prevent default action if CONDITION is true
<button @on{DOM EVENT}="{DELEGATE}" @on{DOM EVENT}:stopPropagation />
<button @on{DOM EVENT}="{DELEGATE}" @on{DOM EVENT}:stopPropagation="{CONDITION}" /> // stop event propagation if CONDITION is true
<button @on{DOM EVENT}="@(e => Property = value)" /> // change internal state w/ lambda
<button @on{DOM EVENT}="@(e => DelegateAsync(e, value))" /> // invoke delegate w/ lambda
<input @ref="elementReference" />
<input @bind="{PROPERTY}" /> // updates variable on ONCHANGE event (focus loss)
<input @bind="{PROPERTY}" @bind:event="{DOM EVENT}" /> // updates value on DOM EVENT
<input @bind="{PROPERTY}" @bind:format="{FORMAT STRING}" /> // use FORMAT STRING to display value
<ChildComponent @bind-{PROPERTY}="{PROPERTY}" @bind-{PROPERTY}:event="{EVENT}" /> // bind to child component {PROPERTY}
<ChildComponent @bind-{PROPERTY}="{PROPERTY}" @bind-{PROPERTY}:event="{PROPERTY}Changed" /> // bind to child component {PROPERTY}, listen for custom event
</p>
@code {
private ElementReference elementReference;
public string Property { get; set; }
public EventCallback<Type> PropertyChanged { get; set; } // custom event {PROPERTY}Changed
// invoke custom event
public async Task DelegateAsync(EventArgs e, Type argument)
{
/* ... */
await PropertyChanged.InvokeAsync(e, argument); // notify parent bound prop has changed
await elementReference.FocusAsync(); // focus an element in code
}
}
```
**NOTE**: When a user provides an unparsable value to a data-bound element, the unparsable value is automatically reverted to its previous value when the bind event is triggered.
## Javascript/.NET Interop
[Call Javascript from .NET](https://docs.microsoft.com/en-us/aspnet/core/blazor/call-javascript-from-dotnet)
[Call .NET from Javascript](https://docs.microsoft.com/en-us/aspnet/core/blazor/call-dotnet-from-javascript)
### Render Blazor components from JavaScript [C# 10]
To render a Blazor component from JavaScript, first register it as a root component for JavaScript rendering and assign it an identifier:
```cs
// Blazor Server
builder.Services.AddServerSideBlazor(options =>
{
options.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
});
// Blazor WebAssembly
builder.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
```
Load Blazor into the JavaScript app (`blazor.server.js` or `blazor.webassembly.js`) and then render the component from JavaScript into a container element using the registered identifier, passing component parameters as needed:
```js
let containerElement = document.getElementById('my-counter');
await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 });
```
### Blazor custom elements [C# 10]
Experimental support is also now available for building custom elements with Blazor using the Microsoft.AspNetCore.Components.CustomElements NuGet package.
Custom elements use standard HTML interfaces to implement custom HTML elements.
To create a custom element using Blazor, register a Blazor root component as custom elements like this:
```cs
options.RootComponents.RegisterAsCustomElement<Counter>("my-counter");
```

View file

@ -0,0 +1,132 @@
# [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.
![filter-pipeline-1](../../img/dotnet_filter-pipeline-1.png)
![filter-pipeline-2](../../img/dotnet_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<CustomActionFilterAttribute>();
[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)
{
}
}
```

View file

@ -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](../../img/dotnet_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](../../img/dotnet_middleware-pipeline.png)
![mvc-endpoint](../../img/dotnet_mvc-endpoint.png)
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<HttpContext, bool>` 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 <App>
{
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(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 <App>
{
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseCustom(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomMiddleware>();
}
}
}
```
```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();
});
}
```

View file

@ -0,0 +1,163 @@
# Minimal API
**NOTE**: Requires .NET 6+
```cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IService, Service>();
builder.Services.AddScoped<IService, Service>();
builder.Services.AddTransient<IService, Service>();
var app = builder.Build();
// [...]
app.Run();
//or
app.RunAsync();
```
## Swagger
```cs
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// [...]
app.UseSwagger();
app.UseSwaggerUI();
// add returned content metadata to Swagger
app.MapGet("/route", Handler).Produces<Type>(statusCode);
// add request body contents metadata to Swagger
app.MapPost("/route", Handler).Accepts<Type>(contentType);
```
## MVC
```cs
builder.Services.AddControllersWithViews();
//or
builder.Services.AddControllers();
// Configure the HTTP request pipeline.
if (app.Environment.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.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
```
## Routing, Handlers & Results
To define routes and handlers using Minimal APIs, use the `Map(Get|Post|Put|Delete)` methods.
```cs
// the dependencies are passed as parameters in the handler delegate
app.MapGet("/route/{id}", (IService service, int id) => {
return entity is not null ? Results.Ok(entity) : Results.NotFound();
});
// pass delegate to use default values
app.MapGet("/search/{id}", Search);
IResult Search(int id, int? page = 1, int? pageSize = 10) { /* ... */ }
```
## Context
With Minimal APIs it's possible to access the contextual information by passing one of the following types as a parameter to your handler delegate:
- `HttpContext`
- `HttpRequest`
- `HttpResponse`
- `ClaimsPrincipal`
- `CancellationToken` (RequestAborted)
```cs
app.MapGet("/hello", (ClaimsPrincipal user) => {
return "Hello " + user.FindFirstValue("sub");
});
```
## Validation
Using [Minimal Validation](https://github.com/DamianEdwards/MinimalValidation) by Damian Edwards.
Alternatively it's possible to use [Fluent Validation](https://fluentvalidation.net/).
```cs
app.MapPost("/widgets", (Widget widget) => {
var isValid = MinimalValidation.TryValidate(widget, out var errors);
if(isValid)
{
return Results.Created($"/widgets/{widget.Name}", widget);
}
return Results.BadRequest(errors);
});
class Widget
{
[Required, MinLength(3)]
public string? Name { get; set; }
public override string? ToString() => Name;
}
```
## JSON Serialization
```cs
// Microsoft.AspNetCore.Http.Json.JsonOptions
builder.Services.Configure<JsonOptions>(opt =>
{
opt.SerializerOptions.PropertyNamingPolicy = new SnakeCaseNamingPolicy();
});
```
## Authorization
```cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();
builder.Services.AddAuthorization();
// or
builder.Services.AddAuthorization(options =>
{
// for all endpoints
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser();
})
// [...]
app.UseAuthentication();
app.UseAuthorization(); // must come before routes
// [...]
app.MapGet("/alcohol", () => Results.Ok()).RequireAuthorization("<policy>"); // on specific endpoints
app.MapGet("/free-for-all", () => Results.Ok()).AllowAnonymous();
```

259
docs/dotnet/asp.net/mvc.md Normal file
View file

@ -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 entry-point
|- 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<Entity> entities = _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) // receive 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 populated 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 populated 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
<form method="post" asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group row">
<div class="col-4">
<label asp-for="IntProp"></label>
</div>
<div class="col-8">
<input asp-for="IntProp" class="form-control"/>
<span asp-validation-for="IntProp" class="text-danger"></span> // error message displayed here
</div>
</div>
</form>
// client side validation
@section Scripts{
@{ <partial name="_ValidationScriptsPartial" /> }
}
```
### 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<Entity> entities = _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) // receive 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
}
}
}
```

View file

@ -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 entry-point
|- 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 <title>Page Title</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 DBContext through DI
public IndexModel(ApplicationDbContext db)
{
_db = db;
}
[BindProperty] // assumed to be received on POST
public IEnumerable<Entity> Entities { get; set; }
// HTTP Method Handler
public async Task OnGet()
{
// get data from DB (example operation)
Entities = await _db.Entities.ToListAsync();
}
// HTTP Method Handler
public async Task<IActionResult> 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 entry-point and default document (missing file in URL redirects to index)
| URL | Maps TO |
|------------------------|----------------------------------------------------|
| www.domain.com | /Pages/Index.cshtml |
| www.domain.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
<form method="post" asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group row">
<div class="col-4">
<label asp-for="IntProp"></label>
</div>
<div class="col-8">
<input asp-for="IntProp" class="form-control"/>
<span asp-validation-for="IntProp" class="text-danger"></span> // error message displayed here
</div>
</div>
</form>
// client side validation
@section Scripts{
@{ <partial name="_ValidationScriptsPartial" /> }
}
```
### 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<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
await _db.SaveChangesAsync();
return RedirectToPage("Index");
}
else
{
return Page();
}
}
}
}
```

View file

@ -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 <App>.Models.Entity // if MVC set type of elements passed to the view
@model <Page>Model // if Razor page set underlying class
@* razor comment *@
// substitute @variable with it's value
<tag>@variable</tag>
@{
// razor code block
// can contain C# or HTML
Model // access 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
<!-- tag helpers for a lin in ASP.NET MVC -->
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
```
### 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 <App>
@namespace <App>.Pages // or <Project>.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
<!-- disable email validation -->
<!span asp-validation-for="Email" ></!span>
```
### 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 <App>.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
<from>
// use the property as the label, eventually w/ [DisplayName("...")]
<label asp-for="EntityProp"></label>
@Html.LabelFor()
// automatically set the value at form compilation and submission
<input asp-for="EntityProp"/>
@Html.EditorFor()
</from>
// route config is {Controller}/{Action}/{Id?}
<a asp-controller="<Controller>" asp-action="<Action>">Link</a> // link to /Controller/Action
<a asp-controller="<Controller>" asp-action="<Action>" asp-route-Id="@model.Id">Link</a> // link to /Controller/Action/Id
@Html.ActionLink("<Link Text>", "<Action>", "<Controller>", new { @HTmlAttribute = value, Id = value }) // link to /Controller/Action/Id
// link to /Controller/Action?queryParameter=value
@Html.ActionLink("<Link Text>", "<Action>", "<Controller>", new { @HTmlAttribute = value, queryParameter = value })
<a asp-controller="<Controller>" asp-action="<Action>" asp-route-queryParameter="value">Link</a> // 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
<form asp-controller="Controller" asp-action="PostAction">
<select asp-for"EntityId" asp-items="Model.Entities">
</select>
<button type="submit">Send<button>
</form>
```
In `Controller.cs`:
```cs
public IActionResult GetAction()
{
var vm = new ViewModel();
vm.Entities = new SelectList(_context.Entities, "Id", "Text"); // fill SelectList
vm.EntityId = value; // set selected option (OPTIONAL)
return View(vm);
}
[HttpPost]
public IActionResult PostAction(ViewModel)
{
if(ModelState.IsValid)
{
// extract info from view model
// save to db
}
}
```

View file

@ -0,0 +1,53 @@
# ASP.NET REST API
```cs
[Route("api/endpoint")]
[ApiController]
public class EntitiesController : ControllerBase // API controller
{
private readonly IEntityService _service;
public EntitiesController(IEntityService service, IMapper mapper)
{
_service = service;
_mapper = mapper
}
[HttpGet] // GET api/endpoint
public ActionResult<IEnumerable<EntityDTO>> GetEntities()
{
IEnumerable<EntityDTO> results = /* ... */
return Ok(results);
}
[HttpGet("{id}")] // GET api/endpoint/{id}
public ActionResult<EntityDTO> GetEntityById(int id)
{
var result = /* .. */;
if(result != null)
{
return Ok(result);
}
return NotFound();
}
[HttpPost] // POST api/endpoint
public ActionResult<EntityDTO> CreateEntity([FromBody] EntityDTO entity)
{
// persist the entity
var id = /* ID of the created entity */
return Created(id, entity);
}
[HttpPut] // PUT api/endpoint
public ActionResult<EntityDTO> UpdateEntity([FromBody] EntityDTO entity)
{
// persist the updated entity
return Created(uri, entity);
}
}
```

View file

@ -0,0 +1,201 @@
# SignalR
The SignalR Hubs API enables to call methods on connected clients from the server. In the server code, define methods that are called by client. In the client code, define methods that are called from the server. SignalR takes care of everything behind the scenes that makes real-time client-to-server and server-to-client communications possible.
## Server-Side
### Configuration
In `Startup.cs`:
```cs
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)
{
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseEndpoints(endpoints =>
{
endpoints.MapHub("/hub/endpoint");
});
}
}
}
```
### Creating Hubs
```cs
public class CustomHub : Hub
{
public task HubMethod(Type args)
{
// trigger function on all clients and pass args to it
return Clients.All.SendAsync("CLientMethod", args);
// trigger function on caller client and pass args to it
return Clients.Caller.SendAsync("CLientMethod", args);
// trigger function on clients of a group and pass args to it
return Clients.Group("GroupName").SendAsync("CLientMethod", args);
// other operations
}
}
```
### Strongly Typed Hubs
A drawback of using `SendAsync` is that it relies on a magic string to specify the client method to be called. This leaves code open to runtime errors if the method name is misspelled or missing from the client.
An alternative to using SendAsync is to strongly type the Hub with `Hub<T>`.
```cs
public interface IHubClient
{
// matches method to be called on the client
Task ClientMethod(Type args);
}
```
```cs
public class CustomHub : Hub<IHubClient>
{
public Task HubMethod(Type args)
{
return Clients.All.ClientMethod(args);
}
}
```
Using `Hub<T>` enables compile-time checking of the client methods. This prevents issues caused by using magic strings, since `Hub<T>` can only provide access to the methods defined in the interface.
Using a strongly typed `Hub<T>` disables the ability to use `SendAsync`. Any methods defined on the interface can still be defined as asynchronous. In fact, each of these methods should return a `Task`. Since it's an interface, don't use the `async` keyword.
### Handling Connection Events
The SignalR Hubs API provides the OnConnectedAsync and OnDisconnectedAsync virtual methods to manage and track connections. Override the OnConnectedAsync virtual method to perform actions when a client connects to the Hub, such as adding it to a group.
```cs
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "GroupName");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "GroupName");
await base.OnDisconnectedAsync(exception);
}
```
Override the `OnDisconnectedAsync` virtual method to perform actions when a client disconnects.
If the client disconnects intentionally (by calling `connection.stop()`, for example), the exception parameter will be null.
However, if the client is disconnected due to an error (such as a network failure), the exception parameter will contain an exception describing the failure.
### Sending Errors to the client
Exceptions thrown in the hub methods are sent to the client that invoked the method. On the JavaScript client, the `invoke` method returns a JavaScript Promise. When the client receives an error with a handler attached to the promise using catch, it's invoked and passed as a JavaScript `Error` object.
If the Hub throws an exception, connections aren't closed. By default, SignalR returns a generic error message to the client.
If you have an exceptional condition you *do* want to propagate to the client, use the `HubException` class. If you throw a `HubException` from your hub method, SignalR will send the entire message to the client, unmodified.
```cs
public Task ThrowException()
{
throw new HubException("This error will be sent to the client!");
}
```
### Client-Side (JavaScript)
### Installing the client package
```sh
npm init -y
npm install @microsoft/signalr
```
npm installs the package contents in the `node_modules\@microsoft\signalr\dist\browser` folder. Create a new folder named signalr under the `wwwroot\lib` folder. Copy the signalr.js file to the `wwwroot\lib\signalr` folder.
Reference the SignalR JavaScript client in the `<script>` element. For example:
```html
<script src="~/lib/signalr/signalr.js"></script>
```
### Connecting to a Hub
[Reconnect Clients Docs](https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client#reconnect-clients)
```js
const connection = new signalR.HubConnectionBuilder()
.withUrl("/hub/endpoint")
.configureLogging(signalR.LogLevel.Information)
.withAutomaticReconnect() // optional
.build();
// async/await connection start
async function connect() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.error(err);
}
};
// promise connection start
function connect() {
connection.start()
.then(() => {})
.catch((err) => {console.error(err)});
}
```
### Call hub methods fom the client
JavaScript clients call public methods on hubs via the `invoke` method of the `HubConnection`. The `invoke` method accepts:
- The name of the hub method.
- Any arguments defined in the hub method.
```js
try {
await connection.invoke("HubMethod", args);
} catch (err) {
console.error(err);
}
```
The `invoke` method returns a JavaScript `Promise`. The `Promise` is resolved with the return value (if any) when the method on the server returns. If the method on the server throws an error, the `Promise` is rejected with the error message. Use `async` and `await` or the `Promise`'s then and catch methods to handle these cases.
JavaScript clients can also call public methods on hubs via the the `send` method of the `HubConnection`. Unlike the `invoke` method, the send method doesn't wait for a response from the server. The send method returns a JavaScript `Promise`. The `Promise` is resolved when the message has been sent to the server. If there is an error sending the message, the `Promise` is rejected with the error message. Use `async` and `await` or the `Promise`'s then and catch methods to handle these cases.
### Call client methods from the hub
To receive messages from the hub, define a method using the `on` method of the `HubConnection`. The `on` method accepts:
- The name of the JavaScript client method.
- Arguments the hub passes to the method.
```cs
connection.on("ClientMethod", (args) => { /* ... */});
```

View file

@ -0,0 +1,85 @@
# WebForms
## `Page.aspx`
The fist loaded page is `Default.aspx` and its underlying code.
```html
<!-- directive -->
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Project.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"> <!-- XML Namespace -->
<head runat="server"> <!-- runat: handle as ASP code -->
<title></title>
</head>
<body>
<!-- web forms require a form tag to be the whole body -->
<form id="form1" runat="server"> <!-- runat: handle as ASP code -->
<div>
</div>
</form>
</body>
</html>
```
### Page Directive
```cs
<%@ Page Language="C#" // define language used (can be C# or VB)
AutoEventWireup="true" // automatically create and setup event handlers
CodeBehind="Default.aspx.cs" // define the underlying code file
Inherits="EmptyWebForm.Default" %>
```
### Web Controls
```xml
<asp:Control ID="" runat="server" ...></asp:Control>
<!-- Label: empty text will diplay ID, use empty space as text for empty label -->
<asp:Label ID="lbl_" runat="server" Text=" "></asp:Label>
<!-- TextBox -->
<asp:TextBox ID="txt_" runat="server"></asp:TextBox>
<!-- Button -->
<asp:Button ID="btn_" runat="server" Text="ButtonText" OnClick="btn_Click" />
<!-- HyperLink -->
<asp:HyperLink ID="lnk_" runat="server" NavigateUrl="~/Page.aspx">LINK TEXT</asp:HyperLink>
<!-- LinkButton: POstBackEvent reloads the page -->
<asp:LinkButton ID="lbtHome" runat="server" PostBackUrl="~/Page.aspx" OnClick="lbt_Click">BUTTON TEXT</asp:LinkButton>
<!-- Image -->
<asp:Image ID="img_" runat="server" ImageUrl="~/Images/image.png"/>
<!-- ImageButton -->
<asp:ImageButton ID="imb_" runat="server" ImageUrl="~/Images/image.png" PostBackUrl="~/Page.aspx"/>
<!-- SqlSataSource; connection string specified in Web.config -->
<asp:SqlDataSource ID="sds_" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>" SelectCommand="SQL Query"></asp:SqlDataSource>
```
## `Page.aspx.cs`
```cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Project
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Control_Event(object sender, EventArgs e)
{
// actions on event trigger
}
}
}
```

View file

@ -0,0 +1,113 @@
# [ADO.NET](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ "ADO.NET Docs")
`ADO.NET` is a set of classes that expose data access services for .NET.
The `ADO.NET` classes are found in `System.Data.dll`, and are integrated with the XML classes found in `System.Xml.dll`.
[ADO.NET provider for SQLite](https://system.data.sqlite.org/index.html/doc/trunk/www/index.wiki "System.Data.SQLite")
## [Connection Strings](https://www.connectionstrings.com)
### [SQL Server 2019](https://www.connectionstrings.com/sql-server-2019/)
- Standard Security:
- `Server=<server_name>; Database=<database>; UID=<user>; Pwd=<password>;`
- `Server=<server_name>; Database=<database>; User ID=<user>; Password=<password>;`
- `Data Source=<server_name>; Initial Catalog=<database>; UID=<user>; Pwd=<password>;`
- Specific Instance: `Server=<server_name>\<instance_name>; Database=<database>; User ID=<user>; Password=<password>;`
- Trusted Connection (WinAuth): `Server=<server_name>; Database=<database>; Trusted_Connection=True;`
- MARS: `Server=<server_name>; Database=<database>; Trusted_Connection=True; MultipleActiveResultSets=True;`
**NOTE**: *Multiple Active Result Sets* (MARS) is a feature that works with SQL Server to allow the execution of multiple batches on a single connection.
### [SQLite](https://www.connectionstrings.com/sqlite/)
- Basic: `Data Source: path\to\db.sqlite3; Version=3;`
- In-Memory Database: `Data Source=:memory:; Version=3; New=True`
- With Password: `Data Source: path\to\db.sqlite3; Version=3; Password=<password>`
## Connection to DB
```cs
using System;
using System.Data.SqlClient; // ADO.NET Provider, installed through NuGet
namespace <namespace>
{
class Program
{
static void Main(string[] args)
{
// Connection to SQL Server DBMS
SqlConnectionStringBuilder connectionString = new SqlConnectionStringBuilder();
connectionString.DataSource = "<server_name>";
connectionString.UserID = "<user>";
connectionString.Password = "<password>";
connectionString.InitialCatalog = "<database>";
// more compact
SqlConnectionStringBuilder connectionString = new SqlConnectionStringBuilder("Server=<server_name>;Database=<database>;UID=<user>;Pwd=<password>")
}
}
}
```
## DB Interrogation
### `SqlConnection`
```cs
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = connectionString.ConnectionString;
connection.Open(); // start communication w/ sql server
}
// more compact
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open()
}
```
### [SqlCommand](https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand)
```cs
string sql = "<sql_instruction>";
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection; // SqlConnection
command.CommandText = "... @Parameter"; // or name of StoredProcedure
// add parameters to the SqlParameterCollection, WARNING: table names or columns cannot be parameters
command.Parameters.Add("@Parameter", SqlDbType.<DBType>, columnLength).Value = value;
command.Parameters.AddWithValue("@Parameter", value);
command.Parameters.AddWithValue("@Parameter", (object) value ?? DBNull.Value); // if Parameter is nullable
// Create an instance of a SqlParameter object.
command.CreateParameter();
command.CommandType = CommandType.Text; // or StoredProcedure
int affectedRows = command.ExecuteNonQuery(); // execute the query and return the number of affected rows
}
```
### `SqlDataReader`
```cs
using (SqlDataReader cursor = command.ExecuteReader()) // object to get data from db
{
while (cursor.Read()) // get data till possible
{
// preferred methodology
cursor["<column_name>"].ToString(); // retrieve data form the column
cursor[<column_index>].ToString(); // retrieve data form the column
// check for null before retrieving the value
if(!cursor.IsDBNull(n))
{
cursor.Get<SystemType>(index); // retrieve data form the n-th column
}
}
}
```

View file

@ -0,0 +1,125 @@
# Entity Framework
## Model & Data Annotations
```cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace <Project>.Model
{
public class Entity
{
[Key] // set as PK (Id & EntityId are automatically detected to be PKs)
public int Id { get; set; }
[Required]
public Type ForeignObject { get; set; } // Not Null in DB
public Type ForeignObject { get; set; } // Allow Null in DB
public int Prop { get; set; } // Not Null in DB (primitive are not nullable)
public int? Prop { get; set; } // Allow Null in DB
}
}
```
## Context
NuGet Packages to install:
- `Microsoft.EntityFrameworkCore`
- `Microsoft.EntityFrameworkCore.Tools` to use migrations in Visual Studio
- `Microsoft.EntityFrameworkCore.Tools.DotNet` to use migrations in `dotnet` cli (`dotnet-ef`)
- `Microsoft.EntityFrameworkCore.Design` *or* `Microsoft.EntityFrameworkCore.<db_provider>.Design` needed for tools to work (bundled w\ tools)
- `Microsoft.EntityFrameworkCore.<db_provider>`
```cs
using Microsoft.EntityFrameworkCore;
namespace <Project>.Model
{
class Context : DbContext
{
private const string _connectionString = "Server=<server_name>;Database=<database>;UID=<user>;Pwd=<password>";
// connect to db
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString); // specify connection
}
// or
public Context(DbContextOptions options) : base(options)
{
}
//DBSet<TEntity> represents the collection of all entities in the context (or that can be queried from the database) of a given type
public DbSet<Entity> Entities { get; set; }
public DbSet<Entity> Entities => Set<Entity>(); // with nullable reference types
}
}
```
## Migrations
Create & Update DB Schema if necessary.
In Package Manager Shell:
```ps1
PM> Add-Migration <migration_name>
PM> update-database [-Verbose] # use the migrations to modify the db, -Verbose to show SQL queries
```
In dotnet cli:
```ps1
dotnet tool install --global dotnet-ef # if not already installed
dotnet ef migrations add <migration_name>
dotnet ef database update
```
## CRUD
### Create
```cs
context.Add(entity);
context.AddRange(entities);
context.SaveChanges();
```
### Read
[Referenced Object Not Loading Fix](https://stackoverflow.com/a/5385288)
```cs
context.Entities.ToList();
context.Entities.Find(id);
// force read of foreign key identifying referenced obj
context.Entities.Include(c => c.ForeignObject).Find(id);
```
### Update
```cs
context.Entities.Update(entity);
context.UpdateRange(entities);
context.SaveChanges();
```
### Delete
```cs
context.Entities.Remove(entity);
context.RemoveRange(entities);
context.SaveChanges();
```