Getting started with StackExchange.Redis


Binaries for StackExchange.Redis are available on Nuget, and the source is available on Github.

Common Tasks

Getting started with Dapper.NET

What is Dapper?

Dapper is a micro-ORM for .Net that extends your IDbConnection, simplifying query setup, execution, and result-reading.

How do I get it?

Common Tasks


StackExchange.Redis's profiling features are composed of the IProfiler interface, and the ConnectionMultiplexer.RegisterProfiler(IProfiler), ConnectionMultiplexer.BeginProfiling(object), ConnectionMultiplexer.FinishProfiling(object) methods.

Begin and Finish profiling take a context object so that related commands can be grouped together.

This grouping works by querying your IProfiler interface for a context object at the start of a command, before any threading shenanigans have happened, and associating that command with a any other commands that have the same context object. Begin must be called with the same context object so StackExchange.Redis knows to start profiling commands with that context object, and Finish is called to stop profiling and return the results.

Type Handlers

Type Handlers allow database types to be converted to .Net custom types.

Parameter Syntax Reference

The syntax for expressing parameters varies between RDBMS. All the examples above use SQL Server syntax, i.e. @foo; however, ?foo and :foo should also work fine.

Getting started with .NET Framework

The .NET Framework is a set of libraries and a runtime, originally designed by Microsoft. All .NET programs compile to a bytecode called Microsoft Intermediate Language (MSIL). The MSIL is run by the Common Language Runtime (CLR).

Below you can find several examples of "Hello World" in various languages that support the .NET Framework. "Hello World" is a program that displays "Hello World" on the display device. It's used for illustrating the basic syntax for constructing a working program. It can also be used as a sanity test to make sure that a language's compiler, development environment, and runtime environment are all working correctly.

List of languages supported by .NET

Getting started with C# Language

C# is a multi-paradigm, C-descendant programming language from Microsoft. C# is a managed language that compiles to CIL, intermediate bytecode which can be executed on Windows, Mac OS X and Linux.

Versions 1.0, 2.0 and 5.0 were standardized by ECMA (as ECMA-334), and standardization efforts for modern C# are underway.

Verbatim Strings

To concatenate string literals, use the @ symbol at the beginning of each string.

var combinedString = @"\t means a tab" + @" and \n means a newline";


All operators are defined as static methods and they are not virtual and they are not inherited.

Operator Precedence

All operators have a particular "precedence" depending on which group the operator falls in (operators of the same group have equal precedence). Meaning some operators will be applied before others. What follows is a list of groups (containing their respective operators) ordered by precedence (highest first):

  • Primary Operators

    • a.b - Member access.
    • a?.b - Null conditional member access.
    • -> - Pointer dereferencing combined with member access.
    • f(x) - Function invocation.
    • a[x] - Indexer.
    • a?[x] - Null conditional indexer.
    • x++ - Postfix increment.
    • x-- - Postfix decrement.
    • new - Type instantiation.
    • default(T) - Returns the default initialized value of type T.
    • typeof - Returns the Type object of the operand.
    • checked - Enables numeric overflow checking.
    • unchecked - Disables numeric overflow checking.
    • delegate - Declares and returns a delegate instance.
    • sizeof - Returns the size in bytes of the type operand.
  • Unary Operators

    • +x - Returns x.
    • -x - Numeric negation.
    • !x - Logical negation.
    • ~x - Bitwise complement/declares destructors.
    • ++x - Prefix increment.
    • --x - Prefix decrement.
    • (T)x - Type casting.
    • await - Awaits a Task.
    • &x - Returns the address (pointer) of x.
    • *x - Pointer dereferencing.
  • Multiplicative Operators

    • x * y - Multiplication.
    • x / y - Division.
    • x % y - Modulus.
  • Additive Operators

    • x + y - Addition.
    • x – y - Subtraction.
  • Bitwise Shift Operators

    • x << y - Shift bits left.
    • x >> y - Shift bits right.
  • Relational/Type-testing Operators

    • x < y - Less than.
    • x > y - Greater than.
    • x <= y - Less than or equal to.
    • x >= y - Greater than or equal to.
    • is - Type compatibility.
    • as - Type conversion.
  • Equality Operators

    • x == y - Equality.
    • x != y - Not equal.
  • Logical AND Operator

    • x & y - Logical/bitwise AND.
  • Logical XOR Operator

    • x ^ y - Logical/bitwise XOR.
  • Logical OR Operator

    • x | y - Logical/bitwise OR.
  • Conditional AND Operator

    • x && y - Short-circuiting logical AND.
  • Conditional OR Operator

    • x || y - Short-circuiting logical OR.
  • Null-coalescing Operator

    • x ?? y - Returns x if it is not null; otherwise, returns y.
  • Conditional Operator

    • x ? y : z - Evaluates/returns y if x is true; otherwise, evaluates z.

Related Content

Extension Methods

Extension methods are syntactic sugar that allow static methods to be invoked on object instances as if they were a member of the type itself.

Extension methods require an explicit target object. You will need to use the this keyword to access the method from within the extended type itself.

Extensions methods must be declared static, and must live in a static class.

Which namespace?

The choice of namespace for your extension method class is a trade-off between visibility and discoverability.

The most commonly mentioned option is to have a custom namespace for your extension methods. However this will involve a communication effort so that users of your code know that the extension methods exist, and where to find them.

An alternative is to choose a namespace such that developers will discover your extension methods via Intellisense. So if you want to extend the Foo class, it is logical to put the extension methods in the same namespace as Foo.

It is important to realise that nothing prevents you using "someone else's" namespace: Thus if you want to extend IEnumerable, you can add your extension method in the System.Linq namespace.

This is not always a good idea. For example, in one specific case, you may want to extend a common type (bool IsApproxEqualTo(this double value, double other) for example), but not have that 'pollute' the whole of System. In this case it is preferable to chose a local, specific, namespace.

Finally, it is also possible to put the extension methods in no namespace at all!

A good reference question: How do you manage the namespaces of your extension methods?


Care should be taken when creating extension methods to ensure that they are appropriate for all possible inputs and are not only relevant to specific situations. For example, it is possible to extend system classes such as string, which makes your new code available to any string. If your code needs to perform domain specific logic on a domain specific string format, an extension method would not be appropriate as its presence would confuse callers working with other strings in the system.

The following list contains basic features and properties of extension methods

  1. It must be a static method.
  2. It must be located in a static class.
  3. It uses the "this" keyword as the first parameter with a type in .NET and this method will be called by a given type instance on the client side.
  4. It also shown by VS intellisense. When we press the dot . after a type instance, then it comes in VS intellisense.
  5. An extension method should be in the same namespace as it is used or you need to import the namespace of the class by a using statement.
  6. You can give any name for the class that has an extension method but the class should be static.
  7. If you want to add new methods to a type and you don't have the source code for it, then the solution is to use and implement extension methods of that type.
  8. If you create extension methods that have the same signature methods as the type you are extending, then the extension methods will never be called.

Collection Initializers

The only requirement for an object to be initialized using this syntactic sugar is that the type implements System.Collections.IEnumerable and the Add method. Although we call it a collection initializer, the object does not have to be an collection.

String Interpolation

String interpolation is a shorthand for the string.Format() method that makes it easier to build strings with variable and expression values inside of them.

var name = "World";
var oldWay = string.Format("Hello, {0}!", name);  // returns "Hello, World"
var newWay = $"Hello, {name}!";                   // returns "Hello, World"

C# 6.0 Features

The sixth version of C# was released July 2015 alongside Visual Studio 2015 and .NET 4.6.

As well as adding some new language features it includes a complete rewrite of the compiler. Previously csc.exe was a native Win32 application written in C++, with C# 6 it is now a .NET managed application written in C#. This rewrite was known as project "Roslyn" and the code is now open source and available on GitHub.

Constructors and Finalizers

C# does not actually have destructors, but rather Finalizers which use C++ style destructor syntax. Specifying a destructor overrides the Object.Finalize() method which cannot be called directly.

Unlike other languages with similar syntax, these methods are not called when objects go out of scope, but are called when the Garbage Collector runs, which occurs under certain conditions. As such, they are not guaranteed to run in any particular order.

Finalizers should be responsible for cleaning up unmanaged resources only (pointers acquired via the Marshal class, received through p/Invoke (system calls) or raw pointers used within unsafe blocks). To clean up managed resources, please review IDisposable, the Dispose pattern and the statement.

(Further reading: When should I create a destructor?)


C# has a predefined collection of "keywords" (or reserved words) which each have a special function. These words can not be used as identifiers (names for variables, methods, classes, etc.) unless prefixed with @.

  • byte
  • case
  • class
  • explicit
  • object
  • private
  • protected
  • public
  • short

Apart from these, C# also uses some keywords to provide specific meaning in code. They are called contextual keywords. Contextual keywords can be used as identifiers and doesn't need to be prefixed with @ when used as identifiers.

  • add
  • alias
  • ascending
  • descending
  • dynamic
  • from
  • get
  • global
  • group
  • into
  • join
  • let
  • orderby
  • remove
  • select
  • set
  • value


Generics in C# are supported all the way down to the runtime: generic types built with C# will have their generic semantics preserved even after compiled to CIL.

This effectively means that, in C#, it is possible to reflect on generic types and see them as they were declared or check if an object is an instance of a generic type, for example. This is in contrast with type erasure, where generic type information is removed during compilation. It is also in contrast with the template approach to generics, where multiple concrete generic types become multiple non-generic types at runtime, and any metadata required to further instantiate the original generic type definitions is lost.

Be careful, however, when reflecting on generic types: generic types' names will be altered on compilation, substituting the angled brackets and the type parameters' names by a backtick followed by the number of generic type parameters. Thus, a Dictionary<TKey, Tvalue> will be translated to Dictionary`2.


Reflection allows code to access information about the assemblies, modules and types at run-time (program execution). This can then be further used to dynamically create, modify or access types. Types include properties, methods, fields and attributes.

Further Reading :


Reflection in .Net Framework


Classes can inherit directly from only one class, but (instead or at the same time) can implement one or more interfaces.

Structs can implement interfaces but cannot explicitly inherit from any type. They implicitly inherit from System.ValueType, which in turn inherits directly from System.Object.

Static classes cannot implement interfaces.


There are several kinds of collection:

  • Array
  • List
  • Queue
  • SortedList
  • Stack


Do not use the XmlSerializer to parse HTML. For this, special libraries are available like the HTML Agility Pack


  • See also .

The LINQ built-in methods are extension methods for the IEnumerable<T> interface that live in the System.Linq.Enumerable class in the System.Core assembly. They are available in .NET Framework 3.5 and later.

LINQ allows for simple modification, transformation, and combination of various IEnumerables using a query-like or functional syntax.

While the standard LINQ methods can work on any IEnumerable<T>, including the simple arrays and List<T>s, they can also be used on database objects, where the set of LINQ expressions can be transformed in many cases to SQL if the data object supports it. See LINQ to SQL.

For the methods that compare objects (such as Contains and Except), IEquatable<T>.Equals is used if the type T of the collection implements that interface. Otherwise, the standard Equals and GetHashCode of the type (possibly overriden from the default Object implementations) are used. There are also overloads for these methods that allow to specify a custom IEqualityComparer<T>.

For the ...OrDefault methods, default(T) is used to generate default values.

Official reference: Enumerable class

Lazy Evaluation

Virtually every query that returns an IEnumerable<T> is not evaluated immediately; instead, the logic is delayed until the query is iterated over. One implication is that each time someone iterates over an IEnumerable<T> created from one of these queries, e.g., .Where(), the full query logic is repeated. If the predicate is long-running, this can be a cause for performance issues.

One simple solution (when you know or can control the approximate size of the resulting sequence) is to fully buffer the results using .ToArray() or .ToList(). .ToDictionary() or .ToLookup() can fulfill the same role. One can also, of course, iterate over the entire sequence and buffer the elements according to other custom logic.

ToArray() or ToList()?

Both .ToArray() and .ToList() loop through all elements of an IEnumerable<T> sequence and save the results in a collection stored in-memory. Use the following guidelines to determine which to choose:

  • Some APIs may require a T[] or a List<T>.
  • .ToList() typically runs faster and generates less garbage than .ToArray(), because the latter must copy all the elements into a new fixed-size collection one more time than the former, in almost every case.
  • Elements can be added to or removed from the List<T> returned by .ToList(), whereas the T[] returned from .ToArray() remains a fixed size throughout its lifetime. In other words, List<T> is mutable, and T[] is immutable.
  • The T[] returned from.ToArray() uses less memory than the List<T> returned from .ToList(), so if the result is going to be stored for a long time, prefer .ToArray(). Calling List<T>.TrimExcess() would make the memory difference strictly academic, at the cost of eliminating the relative speed advantage of .ToList().

Null-Coalescing Operator

The null coalescing operator itself is two consecutive question mark characters: ??

It is a shorthand for the conditional expression:

possibleNullObject != null ? possibleNullObject : defaultValue

The left-side operand (object being tested) must be a nullable value type or reference type, or a compile error will occur.

The ?? operator works for both reference types and value types.