.NET Core Overview

Most of ServiceStack's features are also available on .NET Core, where it's all maintained within a single code-base enabling excellent source-code compatibility to maximize existing knowledge and code-reuse and reducing portability efforts, and released within the same suite of NuGet packages, all without breaking changes to existing .NET 4.5 Customers.

.NET Core - the future of .NET on Linux

.NET Core enables an exciting era of .NET Web and Server App development - the kind .NET hasn't seen before. The existing Windows hosting and VS.NET restraints have been freed, now anyone can develop using .NET's productive expertly-designed and statically-typed mainstream C#/F# languages in their preferred editor and host it on the most popular server Operating Systems, in either an all-Linux, all-Windows or mixed ecosystem. Not only does this flexibility increase the value of existing .NET investments but it also makes .NET appeal to the wider and highly productive developer ecosystem who've previously disregarded .NET as an option.

.NET Core offers significant performance and stability improvements over Mono that's derived from a shared cross-platform code-base and supported by a well-resourced, active and responsive team. If you're currently running ServiceStack on Mono, we strongly recommend upgrading to .NET Core to take advantage of its superior performance, stability and its top-to-bottom supported Technology Stack.

and with that let's jump into seeing some ServiceStack Live Demos running on .NET Core in Linux...

ServiceStack .NET Core Apps running in Docker

Hosting .NET Core Apps immediately exposes us to the benefits of .NET Core. We've ported the Live Demos using the most productive IDE and tooling combination we've found for us - which is still VS.NET with ReSharper. But for deployments and hosting we now have an array of options at our disposal, including joining the thriving state-of-the-art ecosystem around building Linux Docker images and deploying them to the cloud.

For .NET Core Live Demos we've settled on the popular power combo of:

  • Using travis-ci.org (free for OSS projects) for running CI scripts to rebuild Docker App Images on every check-in
  • Using AWS EC2 Container Service for managing Docker images and instance deployments
  • Using nginx-proxy setting up an nginx reverse proxy and automatically bind virtual hosts to Docker Instances

You can checkout our Deploy .NET Core with Docker to AWS ECS Guide for the details on how we've deployed the .NET Core Live Demos, but ultimately packaging .NET Core Apps inside Docker images enables a higher-level of abstraction letting you define your entire App Server Instance with a repeatable recipe that lets you treat and deploy instances like opaque self-contained units.

With our .NET 4.5 Windows Live Demos we're effectively mutating a static Windows Server VM that required pre-configuring with IIS Virtual Hosts. Any infrastructure Servers each Live Demo needs, are set up out-of-band and to minimize the System administration burden, all Demos share the same Redis server instance.

Repeatable, Isolated, no-touch automated Deployments

But for our .NET Core Docker deployments we have proper isolation and repeatable no-touch deployments where any infrastructure services each App needs are declared in configuration and deployed in a separate Docker container along side each App to an ECS cluster - decoupling your deployments from static EC2 instances. This lets you treat your server infrastructure and deployment automation story like code, where it's checked-in with your Repo and run with your CI who packages it in a Docker Container, publishes it as an opaque Image and deploys it to the AWS EC2 Container Service.

Linux Cost Savings

In addition to the thriving ecosystem and superior automation, another benefit of hosting .NET Core Apps on Linux is the considerable cost savings of hosting on a Linux infrastructure. Docker instances enable isolation with considerably more efficiency than VM's allowing you to pack them with greater density. For .NET Core Live Demos the single T2 medium instance ($25 /month) is hosting 15 Docker Images whilst running at ~50% Memory Utilization and <1% CPU Utilization in its current idle state.

Exceptional Code reuse

Thanks to ServiceStack's high-level host agnostic API and our approach to decouple from concrete HTTP abstractions behind lightweight IRequest interfaces, ServiceStack projects enjoy near perfect code reuse, which allows the same ServiceStack Services to be able to run on ASP.NET, HttpListener SelfHosts, SOAP Endpoints, multiple MQ Hosts and .NET Core Apps. The HelloMobile Server Hosts shows an example of this where the same AppHost Configuration and WebServices implementation is used in all:

  • .NET 6.0 Server
  • ASP.NET Core running on .NET Framework
  • ASP.NET Web App (.NET Framework)
  • HttpListener Self-Host (.NET Framework)

The primary advantage of this is simplicity, in both effort and cognitive overhead for creating Services that target multiple platforms, reuse of existing knowledge and investments in using ServiceStack libraries and features as well as significantly reduced migration efforts for porting existing .NET Framework code-bases to run on .NET Core where it enjoys near perfect source code compatibility.

ServiceStack's exceptional source compatibility is visible in our new .NET 6.0 and .NET Framework project templates where all templates utilize the same recommended Physical Project Structure, reference the same NuGet packages, share the same source code for its Server and Client App implementations as well as Client and Server Unit and Integration Tests.

The primary difference between the .NET Core and .NET Framework project templates is how ServiceStack's AppHost is initialized, in ASP.NET it's done in Global.asax whilst for .NET Core it's registered in .NET Core's pipeline as standard. The .csproj are also different with .NET Core using MSBuild's new and minimal human-friendly format and the ASP.NET Framework templates continuing to use VS.NET's classic project format for compatibility with older VS .NET versions.

New .NET 6.0 Project Templates

There are 11 .NET 6.0 project templates for each of ServiceStack's most popular starting templates. Each .NET 6.0 template has an equivalent .NET Framework template except for ServiceStack Sharp Apps which is itself a pre-built .NET 6.0 App that lets you develop Web Applications and HTTP APIs on-the-fly without any compilation.

All .NET 6.0 Templates can be developed using your preferred choice of either VS Code, VS.NET or JetBrains Project Rider on your preferred Desktop OS. Given the diverse ecosystem used to develop .NET Core Applications, the new Project Templates are being maintained on GitHub and made available via our new x new command-line utility, installable from npm with:

dotnet tool install --global x

This makes the x .NET Core tool globally available which can be run without arguments to view all templates available:

.NET Core C# Templates
angular-spa .NET 8 Angular 15 App with Bootstrap
blazor .NET 8 Blazor Tailwind App Template
blazor-vue .NET 8 Blazor Static Rendered Vue interactivity App with Tailwind
blazor-wasm .NET 8 Blazor WASM App with Bootstrap
empty .NET 8 Empty Single Project App
grpc .NET 8 gRPC Services
mvc .NET 8 MVC Identity Auth App with Tailwind
mvc-bootstrap .NET 8 MVC Identity Auth App with Bootstrap
mvcauth .NET 8 MVC App with ServiceStack Auth and Bootstrap
mvcidentityserver .NET 8 MVC App with ServiceStack and IdentityServer4 Auth
nextjs .NET 8 Jamstack Next.js SSG React App with Tailwind
razor .NET 8 Razor Pages App with Tailwind
razor-bootstrap .NET 8 Razor Pages Identity Auth App with Bootstrap
razor-pages .NET 8 Razor Pages App with ServiceStack Auth and Bootstrap
razor-press .NET 8 Statically Generated, CDN hostable Razor Pages Documentation
razor-ssg .NET 8 Statically Generated, CDN hostable Razor Pages Website
react-spa .NET 8 React Create App with Bootstrap
script .NET 8 #Script Pages App with Bootstrap
selfhost .NET 8 self-hosting Console App
svelte-spa .NET 8 Svelte v3 Rollup App with Bootstrap
vue-desktop .NET 8 Chromium Vue Desktop App
vue-mjs .NET 8 Simple, Modern Vue ServiceStack Auth App with Tailwind
vue-nuxt .NET 8 Nuxt.js SPA App with Bootstrap
vue-spa .NET 8 Vue App with Bootstrap
vue-ssg .NET 8 Jamstack Vue SSG App with Tailwind
vue-vite .NET 8 Jamstack Vue Vite App with Tailwind
web .NET 8 Empty App
web-tailwind .NET 8 Empty App with Tailwind
worker-rabbitmq .NET 8 Rabbit MQ Worker Service
worker-redismq .NET 8 Redis MQ Worker Service
worker-servicebus .NET 8 Azure Service Bus MQ Worker Service
worker-sqs .NET 8 AWS SQS MQ Worker Service

Usage

That can be used to create new projects with:

x new <template-name> <project-name>

Example of creating a new Vue SPA project called Acme:

x new vue-spa Acme

The resulting Acme.sln can be opened in VS 2017 which will automatically restore and install both the .NET and npm packages upon first load and build. This can take a while to install all client and server dependencies, once finished the wwwroot folder will be populated with your generated Webpack App contained within a /dist folder alongside a generated index.html page. After these are generated you can run your App with F5 to run your project as normal:

If using JetBrains Rider the npm packages can be installed by opening package.json and clicking on the "npm install" tooltip on the bottom right. In VS Code you'll need to run npm install manually from the command-line.

.NET Core Live Demos

To showcase ServiceStack features running on .NET Core we've forked several of our existing Live Demos and ported them to .NET Core and listed them side-by-side with their original ASP.NET 4.5 code-bases so they can be easily compared.

The Live Demos cover a broad spectrum of ServiceStack features including:

Multi-stage Docker Builds

The .NET Core Apps deployed using Docker use ASP.NET Team's recommended multi-stage Docker Builds where the App is built inside an aspnetcore-build Docker container with its published output copied inside a new aspnetcore runtime Docker container:

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /app

# copy csproj and restore as distinct layers
COPY src/*.sln .
COPY src/Chat/*.csproj ./Chat/
RUN dotnet restore

# copy everything else and build app
COPY src/Chat/. ./Chat/
WORKDIR /app/Chat
RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS runtime
WORKDIR /app
COPY --from=build /app/Chat/out ./
ENV ASPNETCORE_URLS http://*:5000
ENTRYPOINT ["dotnet", "Chat.dll"]

The smaller footprint required by the aspnetcore runtime reduced the footprint of .NET Core Chat from 567MB to 126MB whilst continuing to run flawlessly in AWS ECS at chat.netcore.io.

.NET Core Web Apps

.NET 6.0 is also used to enable Sharp Apps which is a new approach to dramatically simplify .NET Wep App development and provide the most productive development experience possible whilst maximizing reuse and component sharing.

Web Apps let you develop dynamic websites without needing to write any C# code or perform any app builds which dramatically reduces the cognitive overhead and conceptual knowledge required for development where the only thing front-end Web developers need to know is ServiceStack #Script Syntax and what scripts are available to call. Because of #Script's high-fidelity with JavaScript, developing a Website with Templates will be instantly familiar to JavaScript devs despite calling and binding directly to .NET APIs behind the scenes.

Web App Examples

To illustrate the various features available we've developed a number of Web Apps examples to showcase the different kind of Apps that can easily be developed. The source code for each app is available from github.com/NetCoreWebApps. Each app runs the same unmodified Web App Binary that's also used in the Bare Web App project above.

You can quickly get started by creating a Web App from the project template:

dotnet-new bare-app ProjectName

Run ASP.NET Core Apps on the .NET Framework

A primary use-case prevented from having unified NuGet packages containing both .NET Standard and .NET Framework builds is being able to run ASP.NET Core Apps on the .NET Framework which stems from:

  • net45 - Contains support for running ASP.NET Web or Self-Hosting HttpListener App Hosts
  • netstandard2.0 - Contains support for only running on ASP.NET Core App Hosts

Where the net45 builds always get used when they're added to any .NET Framework project. To support running ASP.NET Core Apps on the .NET Framework you can use the .Core NuGet packages which contains only the .NET Standard 2.0 builds in order to force .NET Framework projects to use .NET Standard 2.0 builds. Currently the complete list of .Core packages which contains only .NET Standard 2.0 builds include:

  • ServiceStack.Text.Core
  • ServiceStack.Interfaces.Core
  • ServiceStack.Client.Core
  • ServiceStack.HttpClient.Core
  • ServiceStack.Core
  • ServiceStack.Common.Core
  • ServiceStack.Mvc.Core
  • ServiceStack.Server.Core
  • ServiceStack.Redis.Core
  • ServiceStack.OrmLite.Core
  • ServiceStack.OrmLite.Sqlite.Core
  • ServiceStack.OrmLite.SqlServer.Core
  • ServiceStack.OrmLite.PostgreSQL.Core
  • ServiceStack.OrmLite.MySql.Core
  • ServiceStack.OrmLite.MySqlConnector.Core
  • ServiceStack.Aws.Core
  • ServiceStack.Azure.Core
  • ServiceStack.RabbitMq.Core
  • ServiceStack.Api.OpenApi.Core
  • ServiceStack.Admin.Core
  • ServiceStack.Stripe.Core
  • ServiceStack.Kestrel

WARNING

Ultimately support for whether a .NET Standard 2.0 library will run on the .NET Framework depends on whether external dependencies also support this scenario which as it's a more niche use-case, will be a less tested scenario

Other issues from being a less popular scenario is not being able to reference the Microsoft.AspNetCore.All meta package which only supports .NET Core 2.1 projects, instead ASP.NET .NET Standard packages will need to be referenced individually.

To make it as easy as possible to get started you can use the NetFrameworkCoreTemplates containing popular starting templates for running ASP.NET Core Apps on .NET Framework (default v4.7) which as a convention all have the -corefx suffix:

  • web-corefx - .NET Framework ASP.NET Core Website
  • empty-corefx - .NET Framework ASP.NET Core Single Project Website
  • selfhost-corefx - .NET Framework ASP.NET Core self-hosting Console App
  • mvc-corefx - .NET Framework ASP.NET Core MVC Website
  • razor-corefx - .NET Framework ASP.NET Core Website with ServiceStack.Razor
  • templates-corefx - .NET Framework ASP.NET Core Templates Bootstrap Website

This will let you create an ASP.NET Core App running on the .NET Framework v4.7 with the dotnet tool:

dotnet tool install --global x

Then create a new project with:

x new web-corefx AcmeNetFx

Which can then be opened in your preferred VS.NET or Project Rider C# IDE.

ServiceStack features that won't be supported in .NET Core

Whilst we were able to make most of ServiceStack's features available in .NET Core there are a number of features that we're not able to support, these include:

  • HttpListener inc. all .NET 4.5 Self Host AppHosts - replaced with .NET Core's Kestrel
  • SOAP Support inc. WSDLs, XSDs - missing WCF implementations in .NET Core
  • Mini Profiler - tightly coupled to System.Web
  • Markdown Razor - CodeDom not available in .NET Core (vanilla Markdown still supported)
  • ServiceStack.Authentication.OAuth2 - DotNetOpenAuth dependency not available in .NET Core
  • ServiceStack.Authentication.OpenId - DotNetOpenAuth dependency not available in .NET Core
  • MVC FluentValidation Validators - tightly coupled to old System.Web MVC
  • ServiceStack.Razor inc all existing Html.* helpers - tightly coupled to System.Web Razor

Whilst we lost our beloved ServiceStack.Razor support we developed a completely new implementation backed by .NET Core MVC where we were able to implement most of ServiceStack.Razor user-facing features so porting should still be relatively straightforward with some minor syntax and configuration changes needed. This new implementation is available in ServiceStack.Mvc package and can be seen in action in the Razor Rockstars .NET Core demo.

AppSelfHostBase Source-compatible Self-Host

The ServiceStack.Kestrel NuGet package encapsulates .NET Core's Kestrel HTTP Server dependency behind a source-compatible AppSelfHostBase which can be used to create source-compatible Self Hosted Apps and is what enables the exact same .NET Framework Template's Integration Tests to be used in .NET Core Template's Integration Tests.

AppHostBase .NET Core Module

Whilst AppSelfHostBase enables the same development experience for developing Self-Hosted ServiceStack Solutions, when developing .NET Core-only Web Apps we instead recommend inheriting from AppHostBase and registering ServiceStack as a .NET Core module in order to remain consistent with all other .NET Core solutions.

In ASP.NET 4.5, AppHostBase is used to create an ASP.NET ServiceStack Host, but in .NET Core all Web Apps are Console Apps, AppHostBase in this case just refers to a normal ServiceStack AppHost you'll use to idiomatically register your ServiceStack AppHost into .NET Core's IApplicationBuilder pipeline as a standard .NET Core Module.

Binding to .NET Core

To see how ServiceStack integrates with .NET Core we'll walk through porting the stand-alone Todos Live Demo which contains the entire implementation of a functional Todos Web App back-end in a single Startup.cs that we created using the ASP.NET Core Web Application (.NET Core) Empty VS.NET Template.

The Program class remains unchanged from the template and defines the entry-point for your Console Application that just Configures and Starts a Kestrel HTTP Server behind an IIS Reverse Proxy via the AspNetCoreModule HTTP Handler configured in your web.config:

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        host.Run();
    }
}

.NET Core Startup

The Startup class is what you'll use to configure your .NET App. The only difference from the default VS.NET Template is the single line to Register your ServiceStack AppHost in .NET Core's IApplicationBuilder pipeline:

public class Startup
{
    public IConfiguration Configuration { get; }
    public Startup(IConfiguration configuration) => Configuration = configuration;

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        //Register your ServiceStack AppHost as a .NET Core module
        app.UseServiceStack(new AppHost { 
            AppSettings = new NetCoreAppSettings(Configuration) // Use **appsettings.json** and config sources
        }); 

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }
}

This shows the minimum code required to configure ServiceStack to run in .NET Core. It works similarly to the Wildcard HttpHandler configuration in your ASP.NET Web.config telling ASP.NET to route all requests to ServiceStack. The difference here is that ServiceStack is registered in a pipeline and only receives requests that weren't handled in any of the preceding modules. Likewise ServiceStack will just call the next Module in the pipeline for any Requests that it's not configured to handle, this is in contrast to ASP.NET 4.5 where ServiceStack was designed to handle all requests it receives, so it instead returns a NotFoundHandler and responds with a 404 Response.

In the above configuration any Request that ServiceStack doesn't handle gets passed on to the next module registered, which in this case will return a Hello World! plain text response.

.NET Core AppHost Integration

Once inside your AppHost you're back in ServiceStack-land where it's business as usual and your AppHost configuration remains the same as before:

// Create your ServiceStack Web Service with a singleton AppHost
public class AppHost : AppHostBase
{
    // Initializes your AppHost Instance, with the Service Name and assembly containing the Services
    public AppHost() : base("Backbone.js TODO", typeof(TodoService).Assembly) { }

    // Configure your AppHost with the necessary configuration and dependencies your App needs
    public override void Configure(Container container)
    {
        //Register Redis Client Manager singleton in ServiceStack's built-in Func IOC
        container.Register<IRedisClientsManager>(new BasicRedisClientManager("localhost"));
    }
}

This was all it took to port the Todos back-end Services to run on .NET Core which was able to reuse the entire existing Service implementation as-is.

The other change needed outside the Todos ServiceStack implementation was to match .NET Core's default convention of serving static files from the WebRootPath which just required moving all static resources into the /wwwroot folder.

And with that the Todos port was complete, which you can view from the deployed location below:

Seamless Integration with .NET Core

In addition to running flawlessly on .NET Core we're also actively striving to find how we can best integrate with and leverage the surrounding .NET Core ecosystem and have made several changes to that end:

CamelCase

The JSON and JSV Text serializers are following .NET Core's default convention to use camelCase properties by default. This can be reverted back to PascalCase with:

SetConfig(new HostConfig { UseCamelCase = false })

We also agree with this default, .NET Core seems to be centered around embracing the surrounding developer ecosystem where .NET's default PascalCase protrudes in a sea of camelCase and snake_case JSON APIs. This won't have an impact on .NET Service Clients or Text Serialization which supports case-insensitive properties, however Ajax and JS clients will need to be updated to use matching properties. You can use ss-utils normalize() methods to help with handling both conventions by recursively normalizing and converting all properties to lowercase.

.NET Core Container Adapter

Like ServiceStack, .NET Core now has a built-in IOC where you can register any dependencies you need in your Startup ConfigureServices(), e.g:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IFoo, Foo>()
                .AddScoped<IScoped, Scoped>()
                .AddSingleton<IBar>(c => new Bar { Name = "bar" });
    }
}

Scoped Dependencies

In .NET Core ServiceStack is pre-configured to use a NetCoreContainerAdapter where it will also resolve any dependencies declared in your .NET Core Startup using app.ApplicationServices. One side-effect of this is that when resolving Scoped dependencies it resolves them in a Singleton scope instead of the Request Scope had they instead been resolved from context.RequestServices.GetService<T>().

One way to be able to inject scoped dependencies into your Services is to register the IHttpContextAccessor where they'll be resolved from ASP.NET Core's RequestServices context:

public void ConfigureServices(IServiceCollection services)
{
     services.AddHttpContextAccessor();
     services.AddScoped<IScoped, Scoped>();
}

Otherwise if you need to resolve Request Scoped .NET Core dependencies you can resolve them from IRequest, e.g:

public object Any(MyRequest request)
{
    var requestScope = base.Request.TryResolve<IScoped>();
}

Alternatively you can register the dependencies in ServiceStack's IOC instead, e.g:

public override void Configure(Container container)
{
    services.RegisterAutoWiredAs<Scoped,IScoped>()
        .ReusedWithin(ReuseScope.Request);
}

Where they'd be resolved within ServiceStack's Request Scope instead of via ASP.NET Core's RequestServices.

ASP.NET Core IServiceProvider APIs

Registering dependencies in ServiceStack's IOC are only available within ServiceStack, you can access scoped ASP.NET Core dependencies and create Custom IOC Scopes using the IRequest APIs below:

  • IRequest.TryResolveScoped<T>()
  • IRequest.TryResolveScoped()
  • IRequest.ResolveScoped<T>()
  • IRequest.ResolveScoped()
  • IRequest.CreateScope()
  • IRequest.GetServices()
  • IRequest.GetServices<T>()

Which can be used to create custom scopes that utilizes ASP.NET Entity Framework Identity classes in your ServiceStack Services:

using (var scope = Request.CreateScope())
{
    var RoleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
    var UserManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();

    var managerUser = await UserManager.FindByEmailAsync("manager@gmail.com");
    if (managerUser == null)
    {
        assertResult(await UserManager.CreateAsync(new ApplicationUser {
            DisplayName = "Test Manager",
            Email = "manager@gmail.com",
            UserName = "manager@gmail.com",
            FirstName = "Test",
            LastName = "Manager",
        }, "p@55wOrd"));
        
        managerUser = await UserManager.FindByEmailAsync("manager@gmail.com");
        await UserManager.AddToRoleAsync(managerUser, "Manager");
    }
}

Register ASP.NET Core dependencies in AppHost

Any dependencies registered .NET Core Startup are also available to ServiceStack but dependencies registered in ServiceStack's IOC are only visible to ServiceStack.

This is due to the limitation of ASP.NET Core requiring all dependencies needing to be registered in ConfigureServices() before any App Modules are loaded and why dependencies registered in ServiceStack's AppHost Configure() are only accessible from ServiceStack and not the rest of ASP.NET Core.

But in Modular Startup Apps you can override ASP.NET Core's builder.ConfigureServices(IServiceCollection) method in your AppHost Configure(IWebHostBuilder builder) to register IOC dependencies where they'll now be accessible to both ServiceStack and the rest of your ASP.NET Core App, e.g:

[assembly: HostingStartup(typeof(MyApp.AppHost))]

namespace MyApp;

public class AppHost : AppHostBase, IHostingStartup
{
    public void Configure(IWebHostBuilder builder) => builder
        .ConfigureServices(services => {
            // Configure ASP .NET Core IOC Dependencies
        })
        .Configure(app => {
            // Configure ASP .NET Core App
            if (!HasInit)
                app.UseServiceStack(new AppHost());
        });

    public AppHost() : base("MyApp", typeof(MyServices).Assembly) {}

    public override void Configure(Container container)
    {
        // Configure ServiceStack only IOC, Config & Plugins
        SetConfig(new HostConfig {
            UseSameSiteCookies = true,
        });
    }
}

The IWebHostBuilder can also be used to hook into the Configure(IApplicationBuilder) to inject ASP.NET Core middleware, including ServiceStack and the AppHost itself.

INFO

Reason for only conditionally registering ServiceStack with if (!HasInit) is to allow other plugins (like Auth) the opportunity to precisely control where ServiceStack is registered within its preferred ASP .NET Core's pipeline

This will let you drop-in your custom AppHost into a ModularStartup enabled ASP.NET Core App to enable the same "no-touch" auto-registration.

.NET Core IAppSettings Adapter

Most .NET Core Templates are also configured to use the new NetCoreAppSettings adapter to utilize .NET Core's new IConfiguration config model in ServiceStack by initializing the AppHost with .NET Core's pre-configured IConfiguration that's injected into the Startup.cs constructor, e.g:

public class Startup
{
    public IConfiguration Configuration { get; }
    public Startup(IConfiguration configuration) => Configuration = configuration;

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseServiceStack(new AppHost {
            AppSettings = new NetCoreAppSettings(Configuration)
        });
    }
}

This lets you use appsettings.json and .NET Core's other Configuration Sources from ServiceStack's IAppSettings API where it continues to resolve both primitive values and complex Types, e.g:

bool debug = AppSettings.Get<bool>("DebugMode", false);
MyConfig myConfig = AppSettings.Get<MyConfig>();
List<string>  ghScopes = AppSettings.Get<List<string>>("oauth.github.Scopes");
IList<string> fbScopes = AppSettings.GetList("oauth.facebook.Permissions");

But instead of a single JSV string value, you'll need to use the appropriate JSON data type, e.g:

{
    "DebugMode": true,    
    "MyConfig": {
        "Name": "Kurt",
        "Age": 27
    },
    "oauth.facebook.Permissions": ["email"],
    "oauth.github.Scopes": ["user"]
}

Consistent Registration APIs

To retain the same nomenclature that .NET Core uses to register dependencies we've added several Overloads to ServiceStack's IOC letting you use a single consistent API to register dependencies in both ServiceStack and .NET Core IOC's making it easy to move registrations between the two, e.g:

public void Configure(Container container)
{
    container.AddTransient<IFoo, Foo>()
             .AddScoped<IScoped, Scoped>()
             .AddSingleton<IBar>(c => new Bar { Name = "bar" });
}

ServiceStack's Container also implements .NET Core's IServiceProvider interface giving it access to .NET Core's convenience extension methods for resolving dependencies, e.g:

var foo = container.GetService<IFoo>();

ServiceStack.Logging Adapters

The default Logging is an example of an abstraction which didn't make sense to maintain a separate implementation as any adapter would only be able to support logging within ServiceStack. With logging you'll want to configure it one place and have it apply to your whole application, so we've instead pre-configured ServiceStack.Logging to proxy all messages to .NET Core's logging abstraction where you'll only need to configure logging once in Startup and have it handle all logging solution-wide.

.NET Standard 2.0 Logging Providers

Whilst our recommendation is to use .NET Core's Logging Abstraction, if you prefer you can avoid this abstraction and configure logging with ServiceStack directly with the logging providers below which maintains .NET Standard 2.0 versions:

  • ServiceStack.Logging.Serilog
  • ServiceStack.Logging.Slack

WebRootPath and ContentRootPath

Classic ASP.NET serves static resources from your Host project's folder but in .NET Core this has been moved to your App's /wwwroot folder, separated from your App's non-public assets which remain in your projects root folder.

To match this convention we've configured the read-only VirtualFileSources to point to the /wwwroot WebRootPath whilst the read/write IVirtualFiles is configured to your projects ContentRootPath folder.

MapProjectPath

As ServiceStack can be hosted in variety of different platforms encompassing several AppHost's, we've added a new IAppHost.MapProjectPath() API that can be used to consistently resolve an absolute file path using a virtualPath from your Host Project root folder, e.g:

var filePath = appHost.MapProjectPath("~/path/to/settings.txt");

Hosting ASP.NET Core Apps on Custom Path

Use the PathBase property on AppHost for hosting a ServiceStack .NET Core App at a custom path, e.g:

app.UseServiceStack(new AppHost {
    PathBase = "/api",
});

Resulting in both Config.PathBase and Config.HandlerFactoryPath getting populated with and without the / suffix:

Config.PathBase           //= /api
Config.HandlerFactoryPath //= api

When necessary the PathBase property is available in both server rendered views:

  • {‎{PathBase}‎} variable in #Script Pages
  • PathBase in Razor Views

HostContext.TryGetCurrentRequest()

ASP.NET Web Applications let you resolve the IRequest of the current executing HTTP Request with:

IRequest req = HostContext.TryGetCurrentRequest();

Which is a wrapper over accessing the HttpContext.Current singleton but as there's no equivalent in HttpListener self hosts this returns null. .NET Core also doesn't enable singleton access to the current HttpRequest by default but can be enabled by registering:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Although this should only be registered if needed as it has non-trivial performance costs.

OrmLite LIKE Queries

One of our primary goals with ServiceStack providers is compatibility which lets you easily switch providers without having to change any call-site logic using the abstraction.

One case where this has an impact on performance is in OrmLite LIKE queries where some RDBMS providers will perform case-sensitive LIKE queries so in order to retain consistent behavior across all RDBMS providers OrmLite generates queries using UPPER(Field) to ensure case-insensitive searches.

However given .NET Core's strong focus on performance we've removed this feature and reverted to the behavior of the underlying RDBMS so it no longer invalidates any DB Indexes on Fields. A more efficient alternative to get case-insensitive LIKE queries (where it's not the default) is to use a case-insensitive collation.

This behavior also applies to AutoQuery LIKE queries and can be reverted with:

OrmLiteConfig.StripUpperInLike = false;

Register ServiceStack HttpHandlers as .NET Core Modules

Under the hood ServiceStack's functionality is split into different HTTP Handlers that implements ASP.NET's IHttpAsyncHandler and is also adapted to support HttpListener self-hosts behind ServiceStack's IRequest abstractions.

We're happy to report that ServiceStack's HTTP Handlers can also be registered as a .NET Core module in the IApplicationBuilder pipeline. This lets you for instance return the same information as ServiceStack's ?debug=requestinfo route for any unhandled requests by registering the RequestInfoHandler as the last module in .NET Core's IApplicationBuilder pipeline, e.g:

public class Startup
{
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseServiceStack(new AppHost());

        app.Use(new RequestInfoHandler());
    }
}

Some other examples of HTTP Handlers you could use is returning an image by registering a StaticFileHandler configured with the virtualPath of the image (from ContentRootPath):

app.Use(new StaticFileHandler("wwwroot/img/404.png"));

Or you can even render an MVC Razor View by returning it in a RazorHandler, e.g:

app.Use(new RazorHandler("/login"));

Which will render the /wwwroot/login.cshtml Razor Page using the MVC Smart Razor Pages support

Community Resources