Skip to content

Practical samples of aspnet core 2.1 projects you can use. Readme contains explanations on all projects.

License

Notifications You must be signed in to change notification settings

DeBockTim/practical-aspnetcore

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

140 samples for ASP.NET Core 2.1 fundamentals (updated daily)

If you are studying ASP.NET Core, I am lurking on this Gitter Channel.

Welcome

The goal of this project is to enable .NET programmers to learn the new ASP.NET Core stack from the ground up directly from code. There is so much power in the underlying ASP.NET Core stack. Don't miss them!

I highly recommend using Visual Studio Code to play around with these samples but it is not required. You can use Visual Studio 2017 as well.

MVC, SignalR and Blazor

There are dedicated samples for ASP.NET Core MVC 2.1 here (8 samples), ASP.NET Core SignalR 2.1 here (0 sample) and Blazor here (1 sample). The rest of projects here are for ASP.NET Core only.

How to run these samples

To run these samples, simply open your command line console, go to each folder and execute dotnet watch run.

Most of the examples here uses Microsoft.AspNetCore package which is a package consisted of

  Microsoft.AspNetCore.Diagnostics
  Microsoft.AspNetCore.HostFiltering
  Microsoft.AspNetCore.Hosting
  Microsoft.AspNetCore.Routing
  Microsoft.AspNetCore.Server.IISIntegration
  Microsoft.AspNetCore.Server.Kestrel
  Microsoft.AspNetCore.Server.Kestrel.Https
  Microsoft.Extensions.Configuration.CommandLine
  Microsoft.Extensions.Configuration.EnvironmentVariables
  Microsoft.Extensions.Configuration.FileExtensions
  Microsoft.Extensions.Configuration.Json
  Microsoft.Extensions.Configuration.UserSecrets
  Microsoft.Extensions.Logging
  Microsoft.Extensions.Logging.Configuration
  Microsoft.Extensions.Logging.Console
  Microsoft.Extensions.Logging.Debug

When an example requires packages that are not listed here, it will be added to the project file.

What's new in ASP.NET Core 2.1(2)

Pre-requisite: Make sure you download .NET Core SDK 2.1.0 otherwise below examples won't work.

New code based idiom to start your host for ASP.NET Core 2.1

It is recommended to use the following approach

  public class Program
  {
      public static void Main(string[] args)
      {
          CreateWebHostBuilder(args).Build().Run();
      }

      public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
          WebHost.CreateDefaultBuilder(args)
              .UseStartup<Startup>()
              .UseEnvironment("Development");
  }

instead of

    public class Program
    {
      public static void Main(string[] args)
      {
          BuildWebHost(args).Run();
      }

      public static IWebHost BuildWebHost(string[] args) =>
          WebHost.CreateDefaultBuilder(args)
              .UseStartup<Startup>()
              .UseEnvironment("Development")
              .Build();
    }
  • Hello World with Microsoft.AspNetCore.App package

    If you are targeting netcoreapp2.1, you can use Microsoft.AspNetCore.App meta package that download most of the necessary packages to develop an ASP.NET Core/MVC system (including EF DB support).

    This package is a trimmed version of Microsoft.AspNetCore.All meta package. You can find more details about the removed dependencies here.

    Microsoft.AspNetCore.App is going to be the default meta package when you create a new ASP.NET Core 2.1 package.

  • HttpClientFactory

    Now you can have centrally managed instance of HttpClient using IHttpClientFactory via dependency injection.

  • Supress Status Messages

    You can hide status messages when you start up your web application. It's a small useful thing.

What's new in ASP.NET Core 2.0 (11)

This section will show new things in ASP.NET Core 2.0. This is a good explanation on what's new on ASP.NET Core 2.0.

  • Hello World with Microsoft.AspNetCore.All package

    If you are targeting netcoreapp2.0, you can use Microsoft.AspNetCore.All meta package that download most of the necessary packages to develop an ASP.NET Core/MVC system (including EF DB support).

    It also adds the following packages

    Installing Microsoft.IdentityModel.Logging 1.1.4.
    Installing Microsoft.IdentityModel.Tokens 5.1.4.
    Installing runtime.win-x64.runtime.native.System.Data.SqlClient.sni 4.4.0.
    Installing runtime.win-x86.runtime.native.System.Data.SqlClient.sni 4.4.0.
    Installing runtime.win-arm64.runtime.native.System.Data.SqlClient.sni 4.4.0.
    Installing System.IdentityModel.Tokens.Jwt 5.1.4.
    Installing System.Text.Encoding.CodePages 4.4.0.
    Installing runtime.native.System.Data.SqlClient.sni 4.4.0.
    Installing Microsoft.Azure.KeyVault.WebKey 2.0.7.
    Installing Microsoft.Rest.ClientRuntime.Azure 3.3.7.
    Installing Microsoft.Rest.ClientRuntime 2.3.8.
    Installing SQLitePCLRaw.lib.e_sqlite3.v110_xp 1.1.7.
    Installing SQLitePCLRaw.lib.e_sqlite3.linux 1.1.7.
    Installing SQLitePCLRaw.lib.e_sqlite3.osx 1.1.7.
    Installing SQLitePCLRaw.provider.e_sqlite3.netstandard11 1.1.7.
    Installing Microsoft.IdentityModel.Protocols 2.1.4.
    Installing Microsoft.NETCore.App 2.0.0-preview2-25407-01.
    Installing Microsoft.NETCore.DotNetHostPolicy 2.0.0-preview2-25407-01.
    Installing Microsoft.NETCore.Platforms 2.0.0-preview2-25405-01.
    Installing NETStandard.Library 2.0.0-preview2-25401-01.
    Installing Microsoft.NETCore.DotNetHostResolver 2.0.0-preview2-25407-01.
    Installing Microsoft.Packaging.Tools 1.0.0-preview2-25401-01.
    Installing System.Interactive.Async 3.1.1.
    Installing SQLitePCLRaw.core 1.1.7.
    Installing Microsoft.IdentityModel.Protocols.OpenIdConnect 2.1.4.
    Installing SQLitePCLRaw.bundle_green 1.1.7.
    Installing Microsoft.Azure.KeyVault 2.3.2.
    Installing Microsoft.IdentityModel.Clients.ActiveDirectory 3.14.1.
    Installing WindowsAzure.Storage 8.1.4.
    Installing System.Data.SqlClient 4.4.0.
    Installing Microsoft.NETCore.DotNetAppHost 2.0.0-preview2-25407-01.
    

    In ASP.NET Core 2.0, this is the recommended way to start your host

    public class Program
    {
      public static void Main(string[] args)
      {
          BuildWebHost(args).Run();
      }
    
      public static IWebHost BuildWebHost(string[] args) =>
          WebHost.CreateDefaultBuilder(args)
              .UseStartup<Startup>()
              .UseEnvironment("Development")
              .Build();
    }
    
  • A new way of configuring logging

    Now you configure logging at Program instead of Startup.Configure via ConfigureLogging.

  • Logging filtering

    Now you can adjust what kind of logging information from various part of ASP.NET Core and your app you want show/stored.

  • IConfiguration is now core

    ASP.NET Core 1.1

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory logger)
        {
            //These are the three default services available at Configure
            app.Run(context =>
            {
                return context.Response.WriteAsync('hello world');
            });
        }
    

    ASP.NET Core 2.0

      public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory logger, IConfiguration configuration)
      {
          //These are the four default services available at Configure
          app.Run(context =>
          {
              return context.Response.WriteAsync(configuration["greeting"]);
          });
      }
    
  • Session Feature with Redis using BinaryFormatter

    This shows how to use session with Redis store. We use BinaryFormatter which is only available at .NET Core 2.0 or above to serialize and deserialize your object. The better way is to serialize your object using Json - BinaryFormatter is SLOW.

    Make sure you have Redis running on your localhost at default port. The connection string is specified at appsetings.json.

  • Session Feature with Redis using JSON Serialization

    This shows how to use session with Redis store using Json instead of BinaryFormatter.

    Make sure you have Redis running on your localhost at default port. The connection string is specified at appsetings.json.

  • Anti Forgery on Form

    This exists on since .NET Core 1.0 however the configuration for the cookie has changed slightly. We are using IAntiForgery interface to store and generate anti forgery token to prevent XSRF/CSRF attacks.

  • Razor Pages Basic

    This is the simplest example of the brand new Razor Pages. It shows the two approaches to Razor Pages, one with inline code behind and another with separate code behind.

  • Razor Pages and MVC Basic

    Compare and contrast on how the same task can be performed by using Razor Pages and MVC.

    This sample also shows you how to us Entity Framework Core In-Memory Database.

  • UseRouter extension

    Use app.UseRouter() extension to create minimalistic HTTP services similar to Nancy.

  • UseRouter extension 2

    Use app.UseRouter() with alternative lambda signature.

  • IHostedService

    Implement background tasks using the new IHostedService interface.

Foundation ASP.NET Core 2.1 Samples

All these projects require the following dependencies

   "Microsoft.AspNetCore" : "2.1.0"
  • Hello World (21)

  • Request(8)

    This section shows all the different ways you capture input and examine request to your web application.

  • Routing (9)

    We go deep into Microsoft.AspNetCore.Routing library that provides routing facilities in your aspnetcore apps. There are several samples to illuminate this powerful library.

    • Router

      A single route handler that handles every path request.

    • Router 2

      Two route handler, one for home page (/) and the other takes the rest of the request using asterisk (*) in the url template.

    • Router 3

      We are exploring default handler - this is the entry point to create your own framework.

    • Router 4

      We are mixing optional route parameter, route parameter with default value and default handler.

    • [Router 5]

      This is still broken. I am trying to figure out how to do nested routing. Wish me luck!

    • Router 6

      We are building a template route segment by segment and parts by parts, oldskool. We are using TemplateMatcher, TemplateSegment and TemplatePart.

      Hold your mask, we are going deep.

    • Router 7

      We are creating a routing template with two segments, one with Literal part and the other Parameter part, e.g, "/page/{*title}"

    • Router 8

      We are creating a routing template with one segment consisted of two parts, one Literal and one Parameter, e.g. "/page{*title}". Note the difference between this example and Router 7.

    • Router 9

      I am still trying to determine whether TemplateMatcher uses the InlineConstraint information.

      Update: No, TemplateMatcher does not run constraints. #362

    • Router 10

      We have been building a RouteTemplate manually using TemplateSegment and TemplatePart. In this example we are using TemplateParser to build the RouteTemplate using string.

  • Middleware (8)

    We will explore all aspect of middleware building in this section. There is no extra dependency taken other than Kestrel and dotnet watch.

    • Middleware 1

      This example shows how to pass information from one middleware to another using HttpContext.Items.

    • Middleware 3

      This is the simplest middleware class you can create.

    • Middleware 4

      Use app.Map (MapMiddleware) to configure your middleware pipeline to respond only on specific url path.

    • Middleware 5

      Nested app.Map (show Request.Path and Request.PathBase).

    • Middleware 6

      Use app.MapWhen(MapWhenMiddleware) and Nested app.Map (show Request.Path and Request.PathBase).

    • Middleware 7

      Use MapMiddleware and MapWhenMiddleware directly without using extensions (show Request.Path and Request.PathBase).

    • Middleware 8

      Demonstrate the various ways you can inject dependency to your middleware class manually.

    • Middleware 9

      Demonstrate how to ASP.NET Core built in DI (Dependency Injection) mechanism to provide dependency for your middleware.

  • Features (7)

    Features are collection of objects you can obtain from the framework at runtime that serve different purposes.

    • Server Addresses Feature

      Use this Feature to obtain a list of urls that your app is responding to.

    • Request Feature

      Obtain details of a current request. It has some similarity to HttpContext.Request. They are not equal. HttpContext.Request has more properties.

    • Connection Feature

      Use IHttpConnectionFeature interface to obtain local ip/port and remote ip/port.

    • Custom Featur

      Create your own custom Feature and pass it along from a middleware.

    • Custom Feature - Override

      Shows how you can replace an implementation of a Feature with another within the request pipeline.

    • Request Culture Feature

      Use this feature to detect the culture of a web request through IRequestCultureFeature. It needs the following dependency "Microsoft.AspNetCore.Localization": "2.1.0".

    • Session Feature

      Use session within your middlewares. This sample shows a basic usage of in memory session. It needs the following dependency '"Microsoft.AspNetCore.Session" : "1.1.0-"and"Microsoft.Extensions.Caching.Memory" : "2.1.0-"`.

  • Dependency Injection (2)

    ASP.NET Corenetcore lives and die by DI. It relies on Microsoft.Extensions.DependencyInjection library. There is no need to put this dependency in your project.json explicitly because aspnetcore already has this package as its own dependency.

    • Dependency Injection 1 - The basic

      Demonstrate the three lifetime registrations for the out of the box DI functionality: singleton (one and only forever), scoped (one in every request) and transient (new everytime).

    • Dependency Injection 3 - Easy registration

      Register all objects configured by classes that implements a specific interface (IBootstrap in this example). This is useful when you have large amount of classes in your project that needs registration. You can register them near where they are (usually in the same folder) instead of registering them somewhere in a giant registration function.

      Note: example 2 is forthcoming. The inspiration has not arrived yet.

  • File Provider (2)

    We will deal with various types of file providers supported by ASP.NET Core

  • In Memory Caching (a.k.a local cache) (4)

    These samples depends on Microsoft.Extensions.Caching.Memory library. Please add this dependency to your project.json.

    • Caching - Absolute/Sliding expiration

      This is the most basic caching you can use either by setting absolute or sliding expiration for your cache. Absolute expiration will remove your cache at a certain point in the future. Sliding expiration will remove your cache after period of inactivity.

    • Caching 2 - File dependency

      Add file dependency to your caching so when the file changes, your cache expires.

      You need to put the cache file in your project.json so it gets copied over, e.g.

      "buildOptions": { "emitEntryPoint": true, "copyToOutput": ["cache-file.txt"] }

      Note: example 1 is forthcoming. The inspiration has not arrived yet.

    • Caching 3 - Cache removal event

      Register callback when a cached value is removed.

    • Caching 4 - CancellationChangeToken dependency

      Bind several cache entries to a single dependency that you can reset manually.

  • Configuration (7)

    This section is all about configuration, from memory configuration to INI, JSON and XML.

    • Configuration

      This is the 'hello world' of configuration. Just use a memory based configuration and read/write values to/from it.

    • Configuration - Options

      Use IOptions at the most basic.

    • Configuration - Environment variables

      Load environment variables and display all of them.

    • Configuration - INI file

      Read from INI file. It requires taking a new dependency, "Microsoft.Extensions.Configuration.INI" : "2.1.0".

    • Configuration - INI file - Options

      Read from INI file (with nested keys) and IOptions. It requires taking two new dependencies, "Microsoft.Extensions.Configuration.INI" : "2.1.0" and "Microsoft.Extensions.Options.ConfigurationExtensions" : "2.1.0".

    • Configuration - XML file

      Read from XML file. It requires taking a new dependency, "Microsoft.Extensions.Configuration.Xml" : "2.1.0".

      Note: This Xml Configuration provider does not support repeated element.

      The following configuration settings will break:

      <appSettings>
        <add key="webpages:Version" value="3.0.0.0" />
        <add key="webpages:Enabled" value="false" />
      </appSettings>
      

      On the other hand you can get unlimited nested elements and also attributes.

    • Configuration - XML file - Options

      Read from XML file and use IOptions. It requires taking two new dependencies, "Microsoft.Extensions.Configuration.Xml" : "2.1.0" and "Microsoft.Extensions.Options.ConfigurationExtensions" : "2.1.0".

  • Localization and Globalization (6)

    This section is all about languages, culture, etc.

    • Localization

      Shows the most basic use of localization using a resource file. This sample only supports French language (because we are fancy). It needs the following dependency "Microsoft.AspNetCore.Localization": "2.1.0" and "Microsoft.Extensions.Localization": "2.1.0".

    • Localization - 2

      We build upon the previous sample and demonstrate how to switch request culture via query string using the built in QueryStringRequestCultureProvider. This sample supports English and French.

    • Localization - 3

      Demonstrate the difference between Culture and UI Culture.

    • Localization - 4

      Demonstrate how to switch request culture via cookie using the built in CookieRequestCultureProvider. This sample supports English and French.

    • Localization - 5

      Demonstrate using Portable Object (PO) files to support localization instead of the cumbersome resx file. This sample requires OrchardCore.Localization.Core package. This sample requires ASPNET Core 2.

    • Localization - 6

      This is a continuation of previous sample but with context, which allows the same translation key to return different strings.

  • URL Redirect/Rewriting (6)

    This section explore the dark arts of URL Rewriting

    • Rewrite

      Shows the most basic of URL rewriting which will redirect (returns HTTP 302) anything to the home page "/". It requires an additional "Microsoft.AspNetCore.Rewrite" : "2.1.0-*" dependency.

      If you have used routing yet, I recommend of checking out the routing examples.

    • Rewrite - 2

      Redirect (returns HTTP 302) anything with an extension e.g. about-us.html or welcome.aspx to home page (/). It also shows how to capture the matched regex values.

    • Rewrite - 3

      Rewrite anything with an extension e.g. about-us.html or welcome.aspx to home page (/). It also shows how to capture the matched regex values.

    • Rewrite - 4

      Permanent Redirect (returns HTTP 301) anything with an extension e.g. about-us.html or welcome.aspx to home page (/). It also shows how to capture the matched regex values.

    • Rewrite - 5

      Implement a custom redirect logic based on IRule implementation. Require additional dependency of "Microsoft.AspNetCore.StaticFiles": "1.1.0" to serve images.

      This custom redirection logic allows us to simply specify the image file names without worrying about their exact path e.g.'xx.jpg' and 'yy.png'.

    • Rewrite - 6

      Implement a custom redirect logic using lambda (similar functionality to Rewrite - 5). Require additional dependency of "Microsoft.AspNetCore.StaticFiles": "1.1.0" to serve images.

      This custom redirection logic allows us to simply specify the image file names without worrying about their exact path e.g.'xx.jpg' and 'yy.png'.

  • Compression (1)

    Enable the ability to compress ASP.NET Core responses. These samples takes a dependency of Microsoft.AspNetCore.ResponseCompression": "2.1.0.

    • Default Gzip Output Compression

      Compress everything using the default Gzip compression.

      Everything means the following MIME output

      • text/plain
      • text/css
      • application/javascript
      • text/html
      • application/xml
      • text/xml
      • application/json
      • text/json
  • Diagnostics(6)

    These samples take a dependency of "Microsoft.AspNetCore.Diagnostics":"1.1.1".

    • Welcome Page

      Simply show a welcome page to indicate that the app is working properly. This sample does not use a startup class simply because it's just a one line code.

    • Developer Exception Page

      Show any unhandled exception in a nicely formatted page with error details. Only use this in development environment!

    • Custom Global Exception Page

      Use IExceptionHandlerFeature feature provided by Microsoft.AspNetCore.Diagnostics.Abstractions to create custom global exception page.

    • Custom Global Exception Page - 2

      Similar to the previous one except that that we use the custom error page defined in separate path.

    • Status Pages

      Use UseStatusCodePagesWithRedirects. Beware: This extension method handles your 5xx return status code by redirecting it to a specific url. It will not handle your application exception in general (for this use UseExceptionHandler - check previous samples).

    • Middleware Analysis

      Here we go into the weeds of analysing middlewares in your request pipeline. This is a bit complicated. It requires the following packages:

      • Microsoft.AspNetCore.MiddlewareAnalysis
      • Microsoft.Extensions.DiagnosticAdapter
      • Microsoft.Extensions.Logging.Console
  • Static Files(6)

    This additional dependency is required to enable the functionality "Microsoft.AspNetCore.StaticFiles": "1.1.0".

    • Serve static files

      Simply serve static files (html, css, images, etc).

      There are two static files being served in this project, index.html and hello.css. They are stored under wwwroot folder, which is the default folder location for this library.

      To access them you have to refer them directly e.g. localhost:5000/index.html and localhost:5000/hello.css.

    • Allow Directory Browsing

      Allow listing and browsing of your wwwroot folder.

    • Use File Server

      Combines the functionality of UseStaticFiles, UseDefaultFiles, and UseDirectoryBrowser.

    • Custom Directory Formatter

      Customize the way Directory Browsing is displayed. In this sample the custom view only handles flat directory. We will deal with more complex scenario in the next sample.

    • Custom Directory Formatter - 2

      Show custom Directory Browsing and handle directory listing as well as files.

    • Allow Directory Browsing

      Use Directory Browsing on a certain path using DirectoryBrowserOptions.RequestPath, e.g. /browse.

  • Web Sockets (5)

    We are going to explore websocket functionality provided by ASP.NET Core. All the samples here require Microsoft.AspNetCore.WebSockets.

    Warning: These samples are low level websocket code. For production, use SignalR. Yes I will work on SignalR samples soon.

    • Echo Server

      This is the simplest web socket code you can write. It simply returns what you sent. It does not handle the closing of the connection. It does not handle data that is larger than buffer. It only handles text payload.

    • Echo Server 2

      We improve upon the previous sample by adding console logging (requiring Microsoft.Extensions.Logging.Console package) and handling data larger than the buffer. I set the buffer to be very small (4 bytes) so you can see how it works.

    • Echo Server 3

      We improve upon the previous sample by enabling broadcast. What you see here is a very crude chat functionality.

    • Echo Server 4

      We improve upon the previous sample by handling closing event intiated by the web client.

    • Chat Server

      Implement a rudimentary single channel chat server.

  • Server Side Events (1)

    • Forever Server

      This server will send a 'hello world' greeting forever.

  • Syndications (2)

    We are using the brand new Microsoft.SyndicationFeed.ReaderWriter package to read RSS and ATOM feeds.

  • Misc (3)

    • Markdown server

      Serve markdown file as html file. You will see how you can create useful app using a few basic facilities in aspnetcore.

      We take "Markdig" : "0.15.1" as dependency.

    • Markdown server - implemented as middleware component

      Serve markdown file as html file. It has the same exact functionality as Markdown server but implemented using middleware component.

      We take "Markdig" : "0.15.1" as dependency.

      Check out the documentation on how to write your own middleware.

    • Password Hasher server

      Give it a string and it will generate a secure hash for you, e.g. localhost:5000?password=mypassword.

      We add dependency "Microsoft.AspNetCore.Identity": "2.1.0" to enable this functionality.

  • Web Utilities(2)

    This section shows various functions avaiable at Microsoft.AspNetCore.WebUtilities.

  • Trimming (1)

    This section shows the various way on how to trim the size of your application by using Microsoft.Packagin.Tools.Trimming

    • Trimming Microsoft.AspNetCore.All hello world application

      Run dotnet publish or dotnet build and read the output in your terminal. It will read something similar to Trimmed 115 out of 168 files for a savings of 18.93 MB Final app size is 3.07 MB. You can turn off the trimming by setting <TrimUnusedDependencies>true</TrimUnusedDependencies> to false at the project file.

  • Modules (2)

    This section shows how to create pluggable and extensible web system using Orchardcore Modules system.

    • Modular Hello World

      Run dotnet watch run at the web folder. This example shows a module that just writes "hello world".

      The module1 project requires OrchardCore.Module.Targets and the host web project requires OrchardCore.Application.Targets and OrchardCore.Modules.

    • Keeping track of anonymous users

      Keep track of anonymous user in your ASP.NET Core (useful in scenario such as keeping track of shopping cart) using ReturnTrue.AspNetCore.Identity.Anonymous library.

  • Middleware (1)

    • Response Buffering

      Use Microsoft.AspNetCore.Buffering 0.2.2 middleware to implement response buffering facility. This will allow you to change your response after you write them.

  • Device Detection (1)

    The samples in this section rely on Wangkanai.Detection library.

    • Device Detection

      This is the most basic device detection. You will be able to detect whether the client is a desktop or a mobile client.

  • Owin (1)

    All these samples require Microsoft.AspNetCore.Owin package. These are low level samples and in most cases are not relevant to your day to day ASP.NET Core development.

    • Owin

      Hello world the hard way.

  • Image Sharp (1)

    All these samples require SixLabors.ImageSharp.Web middleware package. This middleware is an excelent tool to process your day to day image processing need.

    • Image-Sharp

      This example shows how to enable image resizing functionality to your site. It's super easy and the middleware takes care of caching, etc.

Other resources

These are other aspnetcore resources with code samples

Misc

About

Practical samples of aspnet core 2.1 projects you can use. Readme contains explanations on all projects.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C# 99.1%
  • Other 0.9%