ServiceStack v4.0.54

We have another massive release in store with big updates to AutoQuery, Server Events and TypeScript support as well as greater flexibility for Multitenancy scenarios and around Service Clients.

AutoQuery Viewer

If you've yet to try Auto Query we encourage you to check it out, it lets you effortlessly create high-performance, fully-queryable, self-descriptive services with just a single, typed Request DTO definition. As they're just normal ServiceStack Services they also benefit from ServiceStack's surrounding feature ecosystem, including native support in .NET PCL Service Clients and multi-language Add ServiceStack Reference clients. We're excited to announce even more new features for AutoQuery in this release - making it more capable and productive than ever!

AutoQuery Viewer is an exciting new feature providing an automatic UI to quickly browse and query all your AutoQuery Services!

YouTube Demo

AutoQuery Viewer is a React App that's bundled within a single ServiceStack.Admin.dll that's available from NuGet at:

Install ServiceStack.Admin

PM> Install-Package ServiceStack.Admin

Signed Version also available from NuGet at ServiceStack.Admin.Signed

Then to add it to your project, just register the Plugin:

Plugins.Add(new AdminFeature());

Which requires AutoQuery, if not already registered:

Plugins.Add(new AutoQueryFeature { MaxLimit = 100 });

Once enabled a link to the AutoQuery Viewer will appear under Plugin Links in your Metadata Page:

Or you can navigate to it directly at /ss_admin/

As it's quick to add, we've already enabled it in a number of existing Live Demo's containing AutoQuery Services:

Live Examples

Default Minimal UI

By default AutoQuery Services start with a minimal UI that uses the Request DTO name to identify the Query. An example of this can be seen with the Northwind AutoQuery Services below:

[Route("/query/customers")]
public class QueryCustomers : QueryBase<Customer> {}

[Route("/query/orders")]
public class QueryOrders : QueryBase<Order> {}

Which renders a UI with the default query and initial fields unpopulated:

Marking up AutoQuery Services

To provide a more useful experience to end users you can also markup your AutoQuery Services by annotating them with the [AutoQueryViewer] attribute, as seen in GitHub QueryRepos:

[Route("/repos")]
[AutoQueryViewer(IconUrl = "octicon:repo",    
    Title = "ServiceStack Repositories", 
    Description = "Browse different ServiceStack repos",
    DefaultSearchField = "Language", DefaultSearchType = "=", DefaultSearchText = "C#",
    DefaultFields = "Id,Name,Language,Description:500,Homepage,Has_Wiki")]
public class QueryRepos : QueryBase<GithubRepo> {}

The additional metadata is then used to customize the UI at the following locations:

Where Title, Description, DefaultSearchField, DefaultSearchType and DefaultSearchText is a straight forward placeholder replacement.

IconUrl

Can either be an url to a 24x24 icon or preferably to avoid relying on any external resources, Admin UI embeds both Google's Material Design Icons and GitHub's Octicon fonts which can be referenced using the custom octicon: and material-icons: schemes, e.g:

  • octicon:icon
  • material-icons:cast

DefaultFields

Can hold a subset list of fields from the AutoQuery Response Type in the order you want them displayed. By default fields have a max-width of 300px but we can override this default with a : suffix as seen with Description:500 which changes the Description column width to 500px. Any text longer than its width is automatically clipped, but you can still see the full-text by hovering over the field or by clicking the AutoQuery generated link, calling the AutoQuery Service and viewing the entire results.

For more, see Advanced Customizations

Filter AutoQuery Services

The filter textbox can be used to quickly find and browse to AutoQuery Services:

Authorized Only Queries

Users only see Queries they have access to, this lets you further tailor the UI for users by using the [Authenticate], Required Role or Permission attributes to ensure different users only see relevant queries, e.g.

[RequiredRole("Sales")]
public class QueryOrders : QueryBase<Order> {}

Since the Auth attributes are Request Filter Attributes with a server dependency to ServiceStack.dll, in order to maintain and share a dependency-free ServiceModel.dll you should instead define a custom AutoQuery in your Service implementations which will inherit any Service or Action filter attributes as normal:

public class QueryOrders : QueryBase<Order> {}

[RequiredRole("Sales")]
public class SalesServices : Service
{
    public IAutoQuery AutoQuery { get; set; }

    public object Any(QueryOrders query)
    {
        return AutoQuery.Execute(query, AutoQuery.CreateQuery(query, Request));
    }
}

Updated in Real-time

To enable a fast and productive UX, the generated AutoQuery link and query results are refreshed as-you-type, in addition any change to a any query immediately saves the App's state to localStorage so users queries are kept across page refreshes and browser restarts.

The generated AutoQuery Url is kept in-sync and captures the state of the current query and serves as a good source for learning how to construct AutoQuery requests that can be used as-is in client applications.

Multiple Conditions

Queries can be constructed with multiple conditions by hitting Enter or clicking on the green (+) button (activated when a condition is valid), adding it to the conditions list and clearing the search text:

Clicking the red remove icon removes the condition.

Change Content-Type

You can force a query to return a specific Content-Type response by clicking on one of the format links. E.g clicking on json link will add the .json extension to the generated url, overriding the browser's default Content-Type to specify a JSON response:

Customize Columns

Results can further customized to show only the columns you're interested in by clicking on the show/hide columns icon and selecting the columns you want to see in the order you want them added:

Sorting Columns and Paging Results

Results can be sorted in descending or ascending order by clicking on the column headers:

Clicking the back/forward navigation icons on the left will page through the results in the order specified.

AutoQuery Enhancements

We've also added a number of new features to AutoQuery that improves performance and enables greater flexibility for your AutoQuery Services:

Parameterized AutoQuery

AutoQuery now generates parameterized sql for all queries where the {Value} placeholder in the AutoQuery Templates have been changed to use db parameters.

Customizable Fields

You can now customize which fields you want returned using the new Fields property available on all AutoQuery Services, e.g:

?Fields=Id,Name,Description,JoinTableId

The Fields still need to be defined on the Response DTO as this feature doesn't change the Response DTO Schema, only which fields are populated. This does change the underlying RDBMS SELECT that's executed, also benefiting from reduced bandwidth between your RDBMS and App Server.

A useful JSON customization that you can add when specifying custom fields is ExcludeDefaultValues, e.g:

/query?Fields=Id,Name,Description,JoinTableId&jsconfig=ExcludeDefaultValues

Multiple Conditions

Previously unsupported, AutoQuery now allows specifying multiple conditions with the same name, e.g:

?DescriptionContains=Service&DescriptionContains=Stack

Named Connection

Related to our improved support for multi-tenancy applications, AutoQuery can easily be used to query any number of different databases registered in your AppHost.

In the example below we configure our main RDBMS to use SQL Server and register a Named Connection to point to a Reporting PostgreSQL RDBMS:

var dbFactory = new OrmLiteConnectionFactory(connString, SqlServer2012Dialect.Provider);
container.Register<IDbConnectionFactory>(dbFactory);

dbFactory.RegisterConnection("Reporting", pgConnString, PostgreSqlDialect.Provider);

Any normal AutoQuery Services like QueryOrders will use the default SQL Server connection whilst QuerySales will execute its query on the PostgreSQL Reporting Database instead:

public class QueryOrders : QueryBase<Order> {}

[NamedConnection("Reporting")]
public class QuerySales : QueryBase<Sales> {}

Generate AutoQuery Services from OrmLite T4 Templates

Richard Safier from the ServiceStack community has extended OrmLite's T4 Templates to include support for generating AutoQuery Services for each Table POCO model using the new opt-in CreateAutoQueryTypes option whilst the new AddNamedConnection option can be used to generate [NamedConnection] annotations.

With this feature Richard was able to generate thousands of fully-queryable AutoQuery Services spanning multiple databases in a single ServiceStack instance with just the T4 templates and configuration.

AdminFeature Rich UI Implementation

We'd like to make a special mention of how AdminFeature was built and deployed as ServiceStack makes it really easy to package and deploy rich plugins with complex UI and behavior encapsulated within a single plugin - which we hope spurs the creation of even richer community Plugins!

Development of AdminFeature is maintained in a TypeScript 1.8 + JSPM + React ServiceStack.Admin.WebHost project where it's structured to provide an optimal iterative development experience. To re-package the App we just call on JSPM to create our app.js bundle by pointing it to the React App's main entry point:

jspm bundle -m src\main ..\ServiceStack.Admin\ss_admin\app.js

Then each of the static resources are copied into the Plugins ServiceStack.Admin project with their Build Action set to Embedded Resource so they're embedded in the ServiceStack.Admin.dll.

To add the Embedded Resources to the Virtual File System the AdminFeature just adds it to Config.EmbeddedResourceBaseTypes (also making it safe to ILMerge).

The entire server implementation for the AdminFeature is contained below, most of which is dedicated to supporting when ServiceStack is mounted at both root / or a custom path (e.g. /api) - which it supports by rewriting the embedded index.html with the HandlerFactoryPath before returning it:

public class AdminFeature : IPlugin, IPreInitPlugin
{
    public void Configure(IAppHost appHost)
    {
        //Register ServiceStack.Admin.dll as an Embedded Resource to VirtualFiles
        appHost.Config.EmbeddedResourceBaseTypes.Add(typeof(AdminFeature));
    }

    public void Register(IAppHost appHost)
    {
        var indexHtml = appHost.VirtualFileSources.GetFile("ss_admin/index.html").ReadAllText();
        if (appHost.Config.HandlerFactoryPath != null) //Inject HandlerFactoryPath if mounted at /custom path
            indexHtml = indexHtml.Replace("/ss_admin", "/{0}/ss_admin".Fmt(appHost.Config.HandlerFactoryPath));

        appHost.CatchAllHandlers.Add((httpMethod, pathInfo, filePath) => 
            pathInfo.StartsWith("/ss_admin") 
                ? (pathInfo == "/ss_admin/index.html" || !appHost.VirtualFileSources.FileExists(pathInfo)
                    ? new StaticContentHandler(indexHtml, MimeTypes.Html) as IHttpHandler
                    : new StaticFileHandler(appHost.VirtualFileSources.GetFile(pathInfo)))
                : null);

        appHost.GetPlugin<MetadataFeature>()
            .AddPluginLink("/ss_admin/autoquery/", "AutoQuery Viewer"); //Add link to /metadata page
    }
}

To power most of its UI, AutoQuery Viewer makes use of the existing Metadata service in AutoQuery.

Code-first POCO Simplicity

Other classes worth reviewing is the GitHubTasks.cs and StackOverflowTasks.cs containing the NUnit tests used to create the test sqlite database on-the-fly, directly from the GitHub and StackOverflow JSON APIs, the ease of which speaks to the simplicity of ServiceStack's code-first POCO approach.

Server Events

We've published a couple of new examples projects showing how easy it is to create rich, interactive native mobile and web apps using Server Events.

Xamarin.Android Chat

Xamarin.Android Chat utilizes the .NET PCL Server Events Client to create an Android Chat App connecting to the existing chat.netcore.io back-end where it's able to communicate with existing Ajax clients and other connected Android Chat Apps. The example shows how to enable a native integrated experience by translating the existing cmd.announce message into an Android notification as well shows how to use Xamarin.Auth to authenticate with ServiceStack using Twitter Auth.

Click the video below to see a quick demo of it in action:

YouTube Video

For a deeper dive, checkout the feature list and source code from the AndroidXamarinChat GitHub repo.

Networked Time Traveller Shape Creator

We've also added Server Events to convert a stand-alone Time Traveller Shape Creator into a networked one where users can connect to and watch other users using the App in real-time similar to how users can use Remote Desktop to watch another computer's screen:

Live demo at: http://redux.servicestack.net

Surprisingly most of the client code required to enable this is encapsulated within a single React Connect component.

The networked Shape Creator makes use of 2 back-end Services that lets users publish their actions to a channel and another Service to send a direct message to a User. The implementation for both services is contained below:

//Services Contract
[Route("/publish-channel/{Channel}")]
public class PublishToChannel : IReturnVoid, IRequiresRequestStream
{
    public string Channel { get; set; }
    public string Selector { get; set; }
    public Stream RequestStream { get; set; }
}

[Route("/send-user/{To}")]
public class SendUser : IReturnVoid, IRequiresRequestStream
{
    public string To { get; set; }
    public string Selector { get; set; }
    public Stream RequestStream { get; set; }
}

//Services Implementation
public class ReduxServices : Service
{
    public IServerEvents ServerEvents { get; set; }

    public void Any(PublishToChannel request)
    {
        var msg = request.RequestStream.ReadFully().FromUtf8Bytes();
        ServerEvents.NotifyChannel(request.Channel, request.Selector, msg);
    }

    public void Any(SendUser request)
    {
        var msg = request.RequestStream.ReadFully().FromUtf8Bytes();
        ServerEvents.NotifyUserId(request.To, request.Selector, msg);
    }
}

Essentially just calling IServerEvents to forward the raw JSON Request Body to the specified channel or user.

Updating Channels on Live Subscriptions

Previously to change Server Event channel subscriptions you would need to create a new connection with the channels you wanted to join. You can now update a live Server Events connection with Channels you want to Join or Leave using the new built-in ServerEvents UpdateEventSubscriber Service:

[Route("/event-subscribers/{Id}", "POST")]
public class UpdateEventSubscriber : IReturn<UpdateEventSubscriberResponse>
{
    public string Id { get; set; }
    public string[] SubscribeChannels { get; set; }
    public string[] UnsubscribeChannels { get; set; }
}

This lets you modify your active subscription with channels you want to join or leave with a HTTP POST Request, e.g:

POST /event-subscribers/{subId}
SubscribeChannels=chan1,chan2&UnsubscribeChannels=chan3,chan4

New onUpdate Notification

As this modifies the active subscription it also publishes a new onUpdate notification to all channel subscribers so they're able to maintain up-to-date info on each subscriber.

In C# ServerEventsClient this can be handled together with onJoin and onLeave events using OnCommand:

client.OnCommand = msg => ...; //= ServerEventJoin, ServerEventLeave or ServerEventUpdate

In the ss-utils JavaScript Client this can be handled with a Global Event Handler, e.g:

$(source).handleServerEvents({
    handlers: {
        onConnect: connectedUserInfo => { ... },
        onJoin: userInfo => { ... },
        onLeave: userInfo => { ... },
        onUpdate: userInfo => { ... }
    }
});

.NET UpdateSubscriber APIs

Typed versions of this API is built into the C# ServerEventsClient in both sync/async versions:

client.UpdateSubscriber(new UpdateEventSubscriber { 
    SubscribeChannels = new[]{ "chan1", "chan2" },
    UnsubscribeChannels = new[]{ "chan3", "chan4" },
});

client.SubscribeToChannels("chan1", "chan2");
client.UnsubscribeFromChannels("chan3", "chan4");

await client.SubscribeToChannelsAsync("chan1", "chan2");
await client.UnsubscribeFromChannelsAsync("chan3", "chan4");

JavaScript UpdateSubscriber APIs

As well as in ServiceStack's ss-utils JavaScript library:

$.ss.updateSubscriber({ 
    SubscribeChannels: "chan1,chan2",
    UnsubscribeChannels: "chan3,chan4"
});

$.ss.subscribeToChannels(["chan1","chan2"], response => ..., error => ...);
$.ss.unsubscribeFromChannels(["chan3","chan4"], response => ..., error => ...);

ServerEvents Update Channel APIs

Whilst internally, from within ServiceStack you can update a channel's subscription using the new IServerEvents APIs:

public interface IServerEvents 
{
    ...
    void SubscribeToChannels(string subscriptionId, string[] channels);
    void UnsubscribeFromChannels(string subscriptionId, string[] channels);
}

TypeScript React App (beta)

We've spent a fair amount of time researching the JavaScript ecosystem to discover what we believe offers VS.NET developers the most optimal balance of power, simplicity and tooling to build and maintain large JavaScript Apps. ES6 offers a number of language improvements to ES5-compatible JavaScript making it much more enjoyable to develop modern applications with, which we believe justifies the additional tooling needed to transpile it to support down-level ES5 browsers. Given the lack of support for Babel/ES6 in VS.NET, the best option to access ES6 features is to use TypeScript which also offers its own benefits over and beyond ES6.

The decision to use TypeScript also meant revisiting other tools used in our Single Page App templates. One of the most productive features in ES6/TypeScript is being able to easily use modules to modularize your code which provides an optimal development experience for maintaining large and complex code-bases. For this to work seamlessly we needed to integrate TypeScript modules with our front-end JavaScript package manager which is why we've replaced bower with JSPM and configured TypeScript to use the Universal SystemJS module format.

Finally to minimize JavaScript fatigue, we've removed as much complexity and moving parts as we could and have removed Grunt in favor of leaving only a Gulp JS build system without any loss of functionality.

With these changes we've hand picked what we believe is the current Gold Standard for developing modern JavaScript Apps in VS.NET with the just released TypeScript 1.8, React, JSPM, Gulp and typings (the successor to TSD). We're also greatly benefiting from this Technology Stack ourselves with the development our latest AutoQuery Viewer TypeScript App.

We've integrated these powerful combinations of technologies and packaged it in the new TypeScript React App (Beta) VS.NET template that's now available in the updated ServiceStackVS VS.NET Extension:

To learn more about this template and explore its different features. please see the in-depth typescript-react-template guide.

TypeScript Redux

To help developers familiarize themselves with these technologies we've also published an in-depth step-by-step guide for beginners that starts off building the simplest HelloWorld TypeScript React App from scratch then slowly growing with each example explaining how TypeScript, React and Redux can be used to easily create a more complex networked Time Travelling Shape Creator as seen in the final Example:

Live Demo: http://redux.servicestack.net

Except for the final demo above, all other examples are pure client-side only demos, i.e. without any server dependencies and can be previewed directly from the static GitHub website below:

ss-utils

ss-utils now available on npm and DefinitelyTyped

To make it easier to develop with ss-utils in any of the npm-based Single Page Apps templates we're maintaining a copy of ss-utils in npm and have also added it to JSPM and DefinitelyTyped registry so you can now add it to your project like any other external dependency using JSPM:

C:\> jspm install ss-utils

If you're using TypeScript, you can also download the accompanying TypeScript definition from:

C:\>typings install ss-utils --ambient --save

Or if you're using the older tsd package manager: tsd install ss-utils --save.

New ss-utils API's

We've added new core utils to make it easier to create paths, urls, normalize JSON responses and send POST JSON Requests:

combinePaths and createUrl

The new combinePaths and createUrl API's help with constructing urls, e.g:

$.ss.combinePaths("path","to","..","join")   //= path/join
$.ss.createPath("path/{foo}", {foo:1,bar:2}) //= path/1

$.ss.createUrl("http://host/path/{foo}",{foo:1,bar:2}) //= http://host/path/1?bar=2

This is a change from previous release where createUrl() behaved like createPath().

normalize and normalizeKey

The new normalizeKey and normalize APIs helps with normalizing JSON responses with different naming conventions by converting each property into lowercase with any _ separators removed - normalizeKey() converts a single string whilst normalize() converts an entire object graph, e.g:

$.ss.normalizeKey("THE_KEY") //= thekey

JSON.stringify(
    $.ss.normalize({THE_KEY:"key",Foo:"foo",bar:{A:1}‎})
)   //= {"thekey":"key","foo":"foo","bar":{"A":1}‎}

const deep = true;
JSON.stringify(
    $.ss.normalize({THE_KEY:"key",Foo:"foo",bar:{A:1}‎}, deep) 
)   //= {"thekey":"key","foo":"foo","bar":{"a":1}‎}

postJSON

Finally postJSON is jQuery's missing equivalent to $.getJSON, but for POST's, eg:

$.ss.postJSON(url, {data:1}, response => ..., error => ...);

Customize JSON Responses on-the-fly

The JSON and JSV Responses for all Services (inc. AutoQuery Services) can now be further customized with the new ?jsconfig QueryString param which lets your Service consumers customize the returned JSON Response to their preference. This works similar to having wrapped your Service response in a HttpResult with a Custom ResultScope in the Service implementation to enable non-default customization of a Services response, e.g:

/service?jsconfig=EmitLowercaseUnderscoreNames,ExcludeDefaultValues

Works similarly to:

return new HttpResult(new { TheKey = "value", Foo=0 }) {
    ResultScope = () => JsConfig.With(
        emitLowercaseUnderscoreNames:true, excludeDefaultValues:true)
};

Which results in lowercase_underscore key names with any properties with default values removed:

{"the_key":"value"}

It also supports cascading server and client ResultScopes, with the client ?jsconfig taking precedence.

Nearly all JsConfig scope options are supported other than delegates and complex type configuration properties.

Camel Humps Notation

JsConfig also supports Camel Humps notation letting you target a configuration by just using the Uppercase Letters in the property name which is also case-insensitive so an equivalent shorter version of the above config can be:

?jsconfig=ELUN,edv

Camel Humps also works with Enum Values so both these two configurations are the same:

?jsconfig=DateHandler:UnixTime
?jsconfig=dh:ut

Custom JSON Live Example

AutoQuery Viewer makes use of this feature in order to return human readable dates using the new ISO8601DateOnly DateHandler Enum Value as well as appending ExcludeDefaultValues when specifying custom fields so that any unpopulated value type properties with default values are excluded from the JSON Response.

Custom JSON Settings

The presence of a bool configuration property will be set to true unless they have a false or 0 value in which case they will be set to false, e.g:

?jsconfig=ExcludeDefaultValues:false

For a quick reference the following bool customizations are supported:

NameAlias
EmitCamelCaseNameseccn
EmitLowercaseUnderscoreNameselun
IncludeNullValuesinv
IncludeNullValuesInDictionariesinvid
IncludeDefaultEnumside
IncludePublicFieldsipf
IncludeTypeInfoiti
ExcludeTypeInfoeti
ConvertObjectTypesIntoStringDictionarycotisd
TreatEnumAsIntegerteai
TryToParsePrimitiveTypeValuesttpptv
TryToParseNumericTypettpnt
ThrowOnDeserializationErrortode
EscapeUnicodeeu
PreferInterfacespi
SkipDateTimeConversionsdtc
AlwaysUseUtcauu
AssumeUtcau
AppendUtcOffsetauo
DateHandler (dh)
TimestampOffsetto
DCJSCompatibledcjsc
ISO8601iso8601
ISO8601DateOnlyiso8601do
ISO8601DateTimeiso8601dt
RFC1123rfc1123
UnixTimeut
UnixTimeMsutm
TimeSpanHandler (tsh)
DurationFormatdf
StandardFormatsf
PropertyConvention (pc)
Stricts
Lenientl

You can also create a scope from a string manually using the new JsConfig.CreateScope(), e.g:

using (JsConfig.CreateScope("EmitLowercaseUnderscoreNames,ExcludeDefaultValues,dh:ut")) 
{
    var json = dto.ToJson();
}

If you don't wish for consumers to be able to customize JSON responses this feature can be disabled with Config.AllowJsConfig=false.

Improved support for Multitenancy

All built-in dependencies available from Service base class, AutoQuery, Razor View pages, etc are now resolved in a central overridable location in your AppHost.

This now lets you control which dependency is used based on the incoming Request for each Service by overriding any of the AppHost methods below, e.g. to change the DB Connection your Service uses you can override GetDbConnection(IRequest) in your AppHost.

public virtual IDbConnection Db
{
    get { return db ?? (db = HostContext.AppHost.GetDbConnection(Request)); }
}

public virtual ICacheClient Cache
{
    get { return cache ?? (cache = HostContext.AppHost.GetCacheClient(Request)); }
}

public virtual MemoryCacheClient LocalCache //New
{
    get { return localCache ?? (localCache = HostContext.AppHost.GetMemoryCacheClient(Request)); }
}

public virtual IRedisClient Redis
{
    get { return redis ?? (redis = HostContext.AppHost.GetRedisClient(Request)); }
}

public virtual IMessageProducer MessageProducer
{
    get { return messageProducer ?? (messageProducer = HostContext.AppHost.GetMessageProducer(Request)); }
}

Change Database Connection at Runtime

The default implementation of GetDbConnection(IRequest) includes an easy way to change the DB Connection that can be done by populating the ConnectionInfo POCO in any Request Filter in the Request Pipeline:

req.Items[Keywords.DbInfo] = new ConnectionInfo {
    NamedConnection  = ... //Use a registered NamedConnection for this Request
    ConnectionString = ... //Use a different DB connection for this Request
    ProviderName     = ... //Use a different Dialect Provider for this Request
};

To illustrate how this works we'll go through a simple example showing how to create an AutoQuery Service that lets the user change which DB the Query is run on. We'll control which of the Services we want to allow the user to change the DB it's run on by having them implement the interface below:

public interface IChangeDb
{
    string NamedConnection { get; set; }
    string ConnectionString { get; set; }
    string ProviderName { get; set; }
}

We'll create one such AutoQuery Service, implementing the above interface:

[Route("/rockstars")]
public class QueryRockstars : QueryBase<Rockstar>, IChangeDb
{
    public string NamedConnection { get; set; }
    public string ConnectionString { get; set; }
    public string ProviderName { get; set; }
}

For this example we'll configure our Database to use a default SQL Server 2012 database, register an optional named connection looking at a "Reporting" PostgreSQL database and register an alternative Sqlite RDBMS Dialect that we also want the user to be able to use:

ChangeDB AppHost Registration

container.Register<IDbConnectionFactory>(c => 
    new OrmLiteConnectionFactory(defaultDbConn, SqlServer2012Dialect.Provider));

var dbFactory = container.Resolve<IDbConnectionFactory>();

//Register NamedConnection
dbFactory.RegisterConnection("Reporting", ReportingConnString, PostgreSqlDialect.Provider);

//Register DialectProvider
dbFactory.RegisterDialectProvider("Sqlite", SqliteDialect.Provider);

ChangeDB Request Filter

To enable this feature we just need to add a Request Filter that populates the ConnectionInfo with properties from the Request DTO:

GlobalRequestFilters.Add((req, res, dto) => {
   var changeDb = dto as IChangeDb;
   if (changeDb == null) return;

   req.Items[Keywords.DbInfo] = new ConnectionInfo {
       NamedConnection = changeDb.NamedConnection,
       ConnectionString = changeDb.ConnectionString,
       ProviderName = changeDb.ProviderName,
   };
});

Since our IChangeDb interface shares the same property names as ConnectionInfo, the above code can be further condensed using a Typed Request Filter and ServiceStack's built-in AutoMapping down to just:

RegisterTypedRequestFilter<IChangeDb>((req, res, dto) =>
    req.Items[Keywords.DbInfo] = dto.ConvertTo<ConnectionInfo>());

Change Databases via QueryString

With the above configuration the user can now change which database they want to execute the query on, e.g:

var response = client.Get(new QueryRockstars()); //SQL Server

var response = client.Get(new QueryRockstars {   //Reporting PostgreSQL DB
    NamedConnection = "Reporting"
}); 

var response = client.Get(new QueryRockstars {   //Alternative SQL Server Database
    ConnectionString = "Server=alt-host;Database=Rockstars;User Id=test;Password=test;"
}); 

var response = client.Get(new QueryRockstars {   //Alternative SQLite Database
    ConnectionString = "C:\backups\2016-01-01.sqlite",
    ProviderName = "Sqlite"
}); 

ConnectionInfo Attribute

To make it even easier to use we've also wrapped this feature in a simple ConnectionInfoAttribute.cs which allows you to declaratively specify which database a Service should be configured to use, e.g we can configure the Db connection in the Service below to use the PostgreSQL Reporting database with:

[ConnectionInfo(NamedConnection = "Reporting")]
public class ReportingServices : Service
{
    public object Any(Sales request)
    {
        return new SalesResponse { Results = Db.Select<Sales>() };
    }
}

Multi Tenancy Example

To show how much easier it is to implement a Multi Tenancy Service with this feature we've updated the Multi Tenancy AppHost Example comparing it with the previous approach of implementing a Custom IDbConnectionFactory.

New CreateQuery overloads for Custom AutoQuery

In order for AutoQuery to pass the current IRequest into the new AppHost.GetDbConnection(IRequest) method it needs to be passed when calling CreateQuery. 2 new API's have been added that now does this:

public class MyServices : Service
{
    public IAutoQuery AutoQuery { get; set; }

    public object Any(Request dto)
    {
        var q = AutoQuery.CreateQuery(dto, base.Request);
        //Calls:
        //var q = AutoQuery.CreateQuery(dto, base.Request.GetRequestParams(), base.Request);
        return AutoQuery.Execute(request, q);
    }
}

ServiceClient URL Resolvers

The urls used in all .NET Service Clients are now customizable with the new UrlResolver and TypedUrlResolver delegates.

E.g. you can use this feature to rewrite the URL used with the Request DTO Type Name used as the subdomain by:

[Route("/test")] 
class Request {}

var client = JsonServiceClient("http://example.org/api") {
    TypedUrlResolver =  (meta, httpMethod, dto) => 
        meta.BaseUri.Replace("example.org", dto.GetType().Name + ".example.org")
            .CombineWith(dto.ToUrl(httpMethod, meta.Format)));
};

var res = client.Get(new Request());  //= http://Request.example.org/api/test
var res = client.Post(new Request()); //= http://Request.example.org/api/test

This feature is also implemented in JsonHttpClient, examples below shows rewriting APIs that use custom urls:

var client = JsonHttpClient("http://example.org/api") {
    UrlResolver = (meta, httpMethod, url) => 
        meta.BaseUri.Replace("example.org", "111.111.111.111").CombineWith(url))
};

await client.DeleteAsync<MockResponse>("/dummy"); 
//=http://111.111.111.111/api/dummy

await client.PutAsync<MockResponse>("/dummy", new Request()); 
//=http://111.111.111.111/api/dummy

ServiceStack.Discovery.Consul

This feature was added to make it easier to support the new ServiceStack.Discovery.Consul plugin by Scott Mackay which enables external RequestDTO endpoint discovery by integrating with Consul.io to provide automatic service registration and health checking.

RequestDTO Service Discovery

To use the plugin install it from NuGet:

Install-Package ServiceStack.Discovery.Consul

Then configure your AppHost specifying the external WebHostUrl for this Service as well as registering the ConsulFeature plugin:

public class AppHost : AppSelfHostBase
{
    public AppHost() : base("MyService", typeof(MyService).Assembly) {}

    public override void Configure(Container container)
    {
        SetConfig(new HostConfig {
            // the url:port that other services will use to access this one
            WebHostUrl = "http://api.acme.com:1234"
            ApiVersion = "2.0" // optional
        });

        // Pass in any ServiceClient and it will be autowired with Func
        Plugins.Add(new ConsulFeature(new JsonServiceClient()));
    }
}

You'll also need to install and start a Consul agent after which once the AppHost is initialized you should see it automatically appear in the Consul UI and disappear after the AppHost is shutdown:

Automatic Service Registration

In your Services you'll then be able to use the Consul-injected IServiceClient to call any external services and it will automatically send the request to an active Service endpoint that handles each Request DTO, e.g:

public class MyService : Service
{
    public IServiceClient Client { get; set; }

    public void Any(RequestDTO dto)
    {
        // the client will resolve the correct uri for the external dto using consul
        var response = Client.Post(new ExternalDTO { Custom = "bob" });
    }
}

Health checks

Default Health Checks

By default the plugin creates 2 health checks used to filter out failing instances of your services:

  1. Heartbeat: Creates an endpoint in your service http://locahost:1234/reply/json/heartbeat that expects a 200 response
  2. If Redis has been configured in the AppHost, it will check if Redis is responding

For more info checkout servicestack-discovery-consul GitHub Repo.

Multiple File Uploads

New .NET APIs have been added to all .NET Service Clients that allow you to easily upload multiple streams within a single HTTP request. It supports populating Request DTO with any combination of QueryString and POST'ed FormData in addition to multiple file upload data streams:

using (var stream1 = uploadFile1.OpenRead())
using (var stream2 = uploadFile2.OpenRead())
{
    var client = new JsonServiceClient(baseUrl);
    var response = client.PostFilesWithRequest<MultipleFileUploadResponse>(
        "/multi-fileuploads?CustomerId=123",
        new MultipleFileUpload { CustomerName = "Foo,Bar" },
        new[] {
            new UploadFile("upload1.png", stream1),
            new UploadFile("upload2.png", stream2),
        });
}

Or using only a Typed Request DTO. The JsonHttpClient also includes async equivalents for each of the new PostFilesWithRequest APIs:

using (var stream1 = uploadFile1.OpenRead())
using (var stream2 = uploadFile2.OpenRead())
{
    var client = new JsonHttpClient(baseUrl);
    var response = await client.PostFilesWithRequestAsync<MultipleFileUploadResponse>(
        new MultipleFileUpload { CustomerId = 123, CustomerName = "Foo,Bar" },
        new[] {
            new UploadFile("upload1.png", stream1),
            new UploadFile("upload2.png", stream2),
        });
}

Special thanks to @rsafier for contributing support for Multiple File Uploads.

PCL WinStore Client retargeted to 8.1

Following a VS.NET Update, we've upgraded the WinStore PCL ServiceStack.Client to target 8.1.

Local MemoryCacheClient

As it sometimes beneficial to have access to a local in-memory Cache in addition to your registered ICacheClient Caching Provider we've pre-registered a MemoryCacheClient that all your Services now have access to from the LocalCache property, i.e:

    MemoryCacheClient LocalCache { get; }

This doesn't affect any existing functionality that utilizes a cache like Sessions which continue to use your registered ICacheClient, but it does let you change which cache you want different responses to use, e.g:

var cacheKey = "unique_key_for_this_request";
return base.Request.ToOptimizedResultUsingCache(LocalCache, cacheKey, () => {
    //Delegate is executed if item doesn't exist in cache 
});

If you don't register a ICacheClient ServiceStack automatically registers a MemoryCacheClient for you which will also refer to the same instance registered for LocalCache.

Cookies

If you're using a Custom AuthProvider that doesn't rely on Session Cookies you can disable them from being created with Config.AllowSessionCookies=false. The Cookie behavior can be further customized by overriding AllowSetCookie() in your AppHost, E.g. you can disable all cookies with:

public override bool AllowSetCookie(IRequest req, string cookieName)
{
    return false;
}

Redis

OrmLite

New [EnumAsInt] attribute as an alternative to [Flags] for storing Enums as ints in OrmLite but still have them serialized as strings in Service responses.

Free-text SQL Expressions are now converted to Parameterized Statements, e.g:

var q = db.From<Rockstar>()
    .Where("Id < {0} AND Age = {1}", 3, 27);

var results = db.Select(q);

Select Fields

The new Select API on SqlExpression enables a resilient way to select custom fields matching the first column it finds (from primary table then joined tables). It also fully qualifies field names to avoid ambiguous columns, allows matching of joined tables with {Table}{Column} convention and ignores any non-matching fields, e.g:

var q = db.From<Rockstar>()
    .Join<RockstarAlbum>((r,a) => r.Id == a.RockstarId)
    .Select(new[] { "Id", "FirstName", "Age", "RockstarAlbumName", "_unknown_" });

Other OrmLite Changes

  • All db.Exists() APIs have been optimized to only query a single column and row.
  • New db.SelectLazy() API added that accepts an SqlExpression
  • Max String column definition for MySQL now uses LONGTEXT

ServiceStack.Text

  • New JsConfig.SkipDateTimeConversion to skip built-in Conversion of DateTime's.
  • New ISO8601DateOnly and ISO8601DateTime DateHandler formats to emit only the Date or Date and Time

Stripe Gateway

Added support for serializing nested complex entities using Stripe's unconventional object notation and the new CreateStripeAccount requiring it, e.g:

var response = gateway.Post(new CreateStripeAccount
{
    Country = "US",
    Email = "test@email.com",
    Managed = true,
    LegalEntity = new StripeLegalEntity
    {
        Address = new StripeAddress
        {
            Line1 = "1 Highway Rd",
            City = "Brooklyn",
            State = "NY",
            Country = "US",
            PostalCode = "90210",
        },
        Dob = new StripeDate(1980, 1, 1),
        BusinessName = "Business Name",
        FirstName = "First",
        LastName = "Last",
    }
});

Which sends a POST Form Data request that serializes the nested Dob into the object notation Stripe expects, e.g:

&legal_entity[dob][year]=1970&legal_entity[dob][month]=1&legal_entity[dob][day]=1 

Minor ServiceStack Features

  • Old Session removed and invalided when generating new session ids for a new AuthRequest
  • New ResourcesResponseFilter, ApiDeclarationFilter and OperationFilter added to SwaggerFeature to modify response
  • Name property added to IHttpFiles in Response.Files collection
  • HostType, RootDirectoryPath, RequestAttributes, Ipv4Addresses and Ipv6Addresses added to ?debug=requestinfo
  • StaticFileHandler now has IVirtualFile and IVirtualDirectory constructor overloads
  • New StaticContentHandler for returning custom text or binary responses in RawHttpHandlers

Changes

IRequest.GetRequestParams() Dictionary now returns any duplicate fields with a hash + number suffix, e.g: #1. To retain existing behavior where duplicate values are merged into a , delimited string use IRequest.GetFlattenedRequestParams()

HttpListener now returns the RemoteEndPoint as the UserHostAddress matching the UserHostAddress returned in ASP.NET Web Applications.

WARNING .NET 4.0 builds will cease after August 1, 2016

Microsoft has discontinued supporting .NET 4.0, 4.5 and 4.5.1 as of January 12th, 2016. We've already started seeing a number of 3rd Party NuGet packages already drop support for .NET 4.0 builds which has kept us referencing old versions. As a result we intend to follow and stop providing .NET 4.0 builds ourselves after August 1st, 2016. If you absolutely need access to .NET 4.0 builds after this date please leave a comment on this UserVoice entry.

v4.0.52 Release Notes