In the ServiceStack v8.1 release, we have introduced a way to better incorporate your ServiceStack APIs into the larger ASP.NET Core ecosystem by mapping your ServiceStack APIs to the standard ASP.NET Core Endpoints. This enables you to have your ServiceStack APIs integrate with your larger ASP.NET Core application in the same way other middle ware does, opening up more opportunities for reuse of your ServiceStack APIs.
Migrating to this new way to initialize your ServiceStack AppHost
is not required, but it does open up the ability to use common third party tooling. A good example of this is adding OpenAPI v3 specification generation for your endpoints offered by the Swashbuckle
package.
We have even included a wrapper package ServiceStack.AspNetCore.OpenApi
to make this integration as easy as possible, and incorporate additional information from your ServiceStack APIs into Swagger metadata.
Changes Required​
There are four main changes required to migrate to the new Endpoints:
- AppHost Initialization
- Moving from Funq to ASP.NET Core's built in Dependency Injection system
- Plugin Initialization
- Service Dependency Resolution
AppHost Initialization​
To use ServiceStack APIs as mapped Endpoints, the way ServiceStack is initialized in your ASPNET Core application needs to be updated.
Previously, the following was used to initialize your ServiceStack AppHost
:
Program.cs​
app.UseServiceStack(new AppHost());
The app
in this example is a WebApplication
resulting from an IHostApplicationBuilder
calling builder.Build()
. Whilst we still need to call app.UseServiceStack()
, we also need to move the discovery of your ServiceStack APIs to earlier in the setup.
var builder = WebApplication.CreateBuilder(args);
// Add ASP.NET dependencies using `builder.Services`
// ...
// Register all services
builder.Services.AddServiceStack(typeof(MyServices).Assembly);
You can also configure options within the AddServiceStack
call, such as adding Swagger support:
// Register all services
services.AddServiceStack(typeof(MyServices).Assembly, c => {
c.AddSwagger(o => {
//o.AddJwtBearer();
o.AddBasicAuth();
});
});
Then you can call app.UseServiceStack()
as before, but this time configure it with Mapped Endpoints:
app.UseServiceStack(new AppHost(), options =>
{
options.MapEndpoints();
});
NOTE
This configuration is required and is an opt-in feature. If you do not call options.MapEndpoints()
, your ServiceStack APIs will not be mapped to standard ASP.NET Core Endpoints.
Moving from Funq to ASP.NET Core's built in Dependency Injection system​
Since ServiceStack services are registered earlier in the setup, we need to move the discovery of your ServiceStack APIs to earlier in the setup as well.
This means that we need to change any uses of the Funq
IoC container to use ASP.NET Core's built in Dependency Injection system.
Previously, the following was used to register your ServiceStack services:
Configure.AppHost.cs​
public class AppHost : AppHostBase, IHostingStartup
{
public AppHost() : base("MyApp", typeof(MyServices).Assembly) { }
public override void Configure(Container container)
{
SetConfig(new HostConfig {
});
// Register your dependencies
container.Register(new MyDependency());
}
}
This used the built in Funq
IoC container to register dependencies for your services. We now need to move this to the ConfigureServices
and use the IServiceCollection
to register your dependencies.
public class AppHost : AppHostBase, IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices((context, services) =>
{
// Configure ASP.NET Core IOC Dependencies
services.AddSingleton(new MyDependency());
});
public AppHost() : base("MyApp", typeof(MyServices).Assembly) { }
public override void Configure(Container container)
{
SetConfig(new HostConfig {
});
}
}
The Configure(IWebHostBuilder builder)
method is called by the WebHostBuilder
when it is building the WebApplication
. This is where we can register our dependencies using the IServiceCollection
provided by ASP.NET Core.
Plugin Initialization​
The way ServiceStack plugins are initialized also needs to be updated. Previously, the following was used to initialize your ServiceStack plugins:
Configure.AppHost.cs​
public class AppHost : AppHostBase, IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices((context, services) =>
{
// Configure ASP.NET Core IOC Dependencies
});
public AppHost() : base("MyApp", typeof(MyServices).Assembly) { }
public override void Configure(Container container)
{
SetConfig(new HostConfig {
});
Plugins.Add(new CorsFeature());
}
}
We now need to move the initialization of your ServiceStack plugins to the ConfigureServices
method as well and use the IServiceCollection
and the AddPlugin
extension method.
public class AppHost : AppHostBase, IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices((context, services) =>
{
// Configure ASP.NET Core IOC Dependencies
services.AddPlugin(new CorsFeature());
});
public AppHost() : base("MyApp", typeof(MyServices).Assembly) { }
// Configure your AppHost with the necessary configuration and dependencies your App needs
public override void Configure(Container container)
{
SetConfig(new HostConfig {
});
}
}
If you need to resolve dependencies during your own plugin or AppHost initialization, you can use a lambda to resolve the dependency from the IServiceProvider
.
Here is an example of configuring AutoQuery
which needs to resolve an IDbConnectionFactory
:
public class ConfigureAutoQuery : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services => {
// Enable Audit History
services.AddSingleton<ICrudEvents>(c =>
new OrmLiteCrudEvents(c.GetRequiredService<IDbConnectionFactory>()));
// For TodosService
services.AddPlugin(new AutoQueryDataFeature());
// For Bookings https://docs.servicestack.net/autoquery-crud-bookings
services.AddPlugin(new AutoQueryFeature
{
MaxLimit = 1000,
//IncludeTotal = true,
});
})
.ConfigureAppHost(appHost => {
appHost.Resolve<ICrudEvents>().InitSchema();
});
}
Service Dependency Resolution​
Since we are now using ASP.NET Core's built in Dependency Injection system, we need to change the way we resolve dependencies in our ServiceStack services. Previously, it was common to use property injection to resolve dependencies:
public class MyService : Service
{
public MyDependency MyDependency { get; set; }
public object Any(MyRequest request)
{
// Use MyDependency
}
}
However, this method of resolving dependencies is not supported in ASP.NET Core. Instead, we need to use constructor injection to resolve dependencies.
Construction injection can be very verbose, especially when you have a lot of dependencies.
Thankfully, C# 12 introduces a new feature called Primary Constructors
which can help reduce the verbosity of constructor injection.
public class MyService(MyDependency MyDependency) : Service
{
public object Any(MyRequest request)
{
// Use MyDependency
}
}
This is a much more concise way of injecting dependencies into your ServiceStack services.
Conclusion​
By bringing ServiceStack APIs onto the same level as other middleware in ASP.NET Core, we open up more opportunities for reuse of your ServiceStack APIs and the ability to use common third party tooling. This is a big step forward in making ServiceStack even more interoperable with the larger ASP.NET Core ecosystem, and hope it will make it easier for you to integrate your ServiceStack APIs into your larger ASP.NET Core applications.