ServiceStack v4.0.46

React Desktop Apps!

We're super excited to announce React Desktop Apps which lets you re-use your Web Development skills to develop multi-platform Desktop Apps with React, ServiceStack and .NET.

React Desktop Apps take advantage of the adaptability, navigation and deep-linking benefits of a Web-based UI, the productivity and responsiveness of the React framework, the performance, rich features and functionality contained in ServiceStack and the .NET Framework combined with the native experience and OS Integration possible from a Native Desktop App - all within a single VS .NET template!

React Desktop Apps

The new React Desktop Apps template in ServiceStackVS provides everything you need to package your ServiceStack ASP.NET Web App into a native Windows Winforms App, an OSX Cocoa App or cross-platform Windows/OSX/Linux Console App which instead of being embedded inside a Native UI, runs "headless" and launches the User's prefered Web Browser for its Web UI.

This Hybrid model of developing Desktop Apps with modern WebKit technologies offers a more productive and reusable alternative with greater development effort ROI than existing bespoke WPF Apps in XAML or Cocoa OSX Apps with Xcode. It enables full code reuse of your Web App whilst still allowing for platform specific .js, .css and C# specialization when needed. These advantages are also why GitHub also adopted a similar approach for their new cross-platform UI in their flagship Windows and OSX Desktop Apps.

Single Installer-less Executable

Each application is compiled into a single xcopy-able executable that's runnable directly without a Software install. The only pre-requisite is the .NET 4.5 Framework on Windows (pre-installed on recent versions) or Mono for Linux. The OSX Cocoa Xamarin.Mac App has the option to bundle the Mono runtime alleviating the need for users to have an existing install of Mono.

The default template includes ServiceStack.Server NuGet packages which includes all of ServiceStack as well as Redis, OrmLite and other high-level functionality depending on OrmLite and Redis including AutoQuery, Redis Server Events, Redis MQ, etc. Despite its features, ServiceStack is super lean where the working Empty Template Console App project which includes jQuery, Bootstrap, React and its CSS, JS and Font resources compiles down into a single .NET .exe that only weighs 1.5 MB zipped or 4.7 MB uncompressed.

Modern Web Application Development

The React Desktop Template is structured for optimal developer productivity, fast iterations, maximum re-use, easy customizability and optimal runtime performance that's driven by a pre-configured automated workflow. It also maximizes skill re-use where most development time will be spent developing a normal ASP.NET React Web Application without any consideration for the different platforms the tooling creates.

The template follows the same Modern React Apps with .NET as ServiceStack's other Single Page App templates which uses node's rich npm ecosystem to enable access to its premier Web technologies including bower for client dependencies and pre-configured Grunt and Gulp tasks to take care of website bundling, optimization, application packaging and ASP.NET Website deployemnts.

The entire React application is hosted within a single static default.html which is itself only used to structure the websites resources into logical groups where 3rd Party JavaScript libraries and CSS are kept isolated from your own Application's source code. The groups are defined by HTML comments which instruct Gulps userref plugin on how to minify and optimize your Apps resources.

React Desktop App VS.NET Template

The React Desktop Apps template is pre-configured with the necessary tools to package your Web Application into multiple platforms using the provided Grunt build tasks. The Desktop Apps are also debuggable allowing for a simplified and iterative dev workflow by running any of the Host Projects:

  • Web - ASP.NET Web Application
  • Windows - Native Windows application embedded in a CefSharp Chromium browser
  • OSX - Native OS X Cocoa App embedded in a WebView control (requires Xamarin.Mac)
  • Console - Single portable, cross platform executable that launches the user's prefered browser

Project Structure

The resulting project structure is the same as the React App VS.NET Template, but with 3 additional projects for hosting the new Desktop and Console Apps and a Common Resources project shared by Host projects containing all the ASP.NET resources (e.g. .css, .js, images, etc) as embedded resources.

It's kept in-sync with the primary DefaultApp project with the 01-bundle-all (or default) Grunt task.

DefaultApp.sln

  • DefaultApp - Complete Web application, inc. all Web App's .js, .css, images, etc.
  • DefaultApp.AppConsole - Console Host Project
  • DefaultApp.AppWinForms - WinForms Host Project
  • DefaultApp.Resources - Shared Embedded resources sourced from DefaultApp
  • DefaultApp.ServiceInterface - ServiceStack Service Implementations
  • DefaultApp.ServiceModel - Request and Response DTO's
  • DefaultApp.Tests - NUnit tests

DefaultAppMac.sln

  • DefaultApp.AppMac - OSX Cocoa Host project

This is a Xamarin Studio project which can be built with Xamarin.Mac and uses the compiled embedded resources lib\DefaultApp.Resources.dll created by the 01-bundle-all Grunt task.

DefaultApp Project

The primary DefaultApp project contains the complete React Web App hosted in an ASP.NET Project. It includes gruntfile.js which provides the necessary Grunt tasks to bundle and optimize the Wep Application ready for deployment as well as Grunt tasks to minify the Web Applications assets and publishes them embedded resources into the shared DefaultApp.Resources project. This project is how the React WebApp is made available to the alternative Desktop and Console Apps.

The primary Grunt Tasks you'll use to package and deploy your App are contained in Alias Tasks group which is easily runnable from VS .NET's Task Runner Explorer which is built into VS 2015:

  • default - Runs 01-bundle-all and creates packages for 02-package-console and 03-package-winforms
  • 01-bundle-all - optimizes and packages Web App the into wwwroot and Resources project
  • 02-package-console - Packages the Console App in wwwroot_build\apps
  • 03-package-winforms - Packages the Winforms App in wwwroot_build\apps
  • 04-deploy-app - deploys the Web App in wwwroot with MS WebDeploy to any IIS Server using config wwwroot_build\publish\config.json

The template also includes the ILMerge tool to merge all .NET .dlls (inc. Resources.dll) into a single, cross-platform Console Application .exe that's runnable as-is on any Windows, OSX or Linux server with .NET or Mono pre-installed.

Incredible Reuse and Highly Customizable

Customizations for each platform is available by modifying the individual platform.css and project.js files at the base of each Host folder for adding unique platform-specific JavaScript or CSS.

In addition, an easy way to limit which HTML elements are displayed is to use the platform class to initially hide the element, then specify which platforms it should be displayed in by listing the specific platforms, e.g:

<ul className="nav navbar-nav pull-right">
    <li><a onClick={this.handleAbout}>About</a></li>
    <li className="platform winforms">
        <a onClick={this.handleToggleWindow}>Toggle Window</a>
    </li>
    <li className="platform winforms mac">
        <a onClick={this.handleQuit}>Close</a>
    </li>
</ul>

In this example the About link is shown on every platform, Toggle Window is specific to Windows and Close is available in both Winforms or OSX Cocoa Desktop Applications.

Since each host is just a normal C# project you also have complete freedom in enhancing each platform with specific functionality native to that platform. E.g. you can add 3rd party dependencies or create Services that are only available to that platform.

Downloads for the DefaultApp VS.NET Template

Windows Winforms App:

DefaultApp-winforms.exe (23.7 MB)

OSX Cocoa App:

DefaultApp.AppMac.app.zip (4.1 MB)

Console App (Windows/OSX/Linux):

DefaultApp-console.exe (4.8 MB) or DefaultApp-console.zip (1.5 MB)

React Chat Desktop App

To illustrate the potential of React Desktop Apps we've developed a couple of Basic Examples to show how quick and easy it is to create highly-interactive Desktop Applications for every major Operating System.

WinForms application with loading splash screen

React Chat shows the features and interactivity possible when you have all of ServiceStack available in a Desktop App. React Chat uses Server Events for its real-time notifications allowing ServiceStack Services to notify the client of events instantly. In React Chat each command is sent by Ajax to a normal ServiceStack Service which effectively just relays it back to the client via a Server Event.

After the Server Event reaches the client it calls the registered JavaScript handler, which in the case of /cmd.toggleFormBorder calls nativeHost.toggleFormBorder().

In Winforms, nativeHost is registered a C# object courtesy of CefSharp's JavaScript Interop feature where JavaScript can call C# directly, which for ToggleFormBorder() just toggles the Window's Chrome on/off:

ChromiumBrowser.RegisterJsObject("nativeHost", new NativeHost(this));

public class NativeHost
{
    //...
    public void ToggleFormBorder()
    {
        formMain.InvokeOnUiThreadIfRequired(() => {
            formMain.FormBorderStyle = formMain.FormBorderStyle == FormBorderStyle.None
                ? FormBorderStyle.Sizable
                : FormBorderStyle.None;
        });
    }
}

This is also an example of a Windows only feature that only appears when the React Web App hosted in Winforms.

Controlling multiple Windows with Server Events

A nice benefit for using Server Events for real-time communication with JavaScript is that you're able to control multiple window clients naturally just by having each Windows Application subscribe to the same remote /event-stream url. You can do in React Chat just by opening multiple windows as all subesquent Windows Apps opened listen to the self-hosting listener of the first one that was opened.

The /windows.dance chat message provides a nice demonstration of this in action :)

YouTube Live Demo

In addition to the Default template, ReactChat also has Razor enabled to also generate dynamic server pages

You can play around with React Chat with the download for your Operating System below:

Downloads for the React Chat

Windows Winforms App:

ReactChat-winforms.exe (23.6 MB)

OSX Cocoa App:

ReactChat.AppMac.mono.app.zip (16.9 MB) or without Mono ReactChat.AppMac.app.zip (4.51 MB)

Console App (Windows/OSX/Linux):

ReactChat-console.exe (5.33 MB) or DefaultApp-console.zip (1.93MB)

Introducing Redis React!

We're also excited to announce Redis React, which we believe is a good example showing an ideal use-case for React Desktop Apps:

Redis React is a simple user-friendly UI for browsing data in Redis servers that leverages the navigation and deep-linking benefits of a Web-based UI, the productivity and responsiveness of the React framework and the deep Integration possible from a Native App.

Live Demo

The Redis React App has been packaged for multiple platforms inc. the ASP.NET Live Demo redisreact.servicestack.net deployed on AWS which you can use to preview Redis React browsing a redis server populated with the Northwind Dataset persisted as JSON following the Complex Type Conventions built into the C# ServiceStack.Redis Client.

Download

Use Redis React to browse your internal Redis Server by downloading the appropriate download for your platform:

Windows

To run on Windows, download the self-extracting Winforms App:

RedisReact-winforms.exe (23.9MB)

Windows requires .NET 4.5 installed which is pre-installed on recent version of Windows

OSX

To run on OSX, download the Cocoa OSX App:

RedisReact.AppMac.mono.app.zip (16.5 MB) or without mono RedisReact.AppMac.app.zip (4.1 MB)

The Cocoa OSX App was built with Xamarin.Mac and includes an embedded version of Mono which doesn't require an existing install of Mono

Linux

To run on Linux, download the cross-platform Console App:

RedisReact-console.exe (5.4MB) or RedisReact-console.exe.zip (1.7MB)

RedisReact-console.exe is a headless Console Application that can run on Windows, OSX and Linux platforms with .NET or Mono installed.

See the instructions for Installing Mono on Linux. If installing via apt-get, it needs the mono-complete package to run.

Rich support for JSON

Redis React is especially useful for browsing JSON values which includes a human friendly view of JSON data and the ability to view multiple related keys together in a tabular data grid enabling fast inspection of redis data.

At anytime you can click on the JSON preview to reveal the raw JSON string, or use the Global t shortcut key to toggle between preview mode and raw mode of JSON data.

It also takes advantages of the POCO conventions built into the C# ServiceStack.Redis Client where it will automatically display any related entities for the current value, as seen with the related Customer the Order was for and the Employee who created it.

It works by scanning the JSON fields for names ending with Id then taking the prefix and using it to predict the referenced key, e.g:

CustomerId:FRANR => urn:customer:FRANR 

It then fetches all the values with the calculated key and displays them below the selected Order. Clicking the Customer or Employee Key will navigate to that record, providing nice navigation for quickly viewing a record and its related entities.

View as Grid

When keys share the same schema, clicking on the view as grid link lets you see multiple search results displayed in a tabular data grid, e.g:

Web Console

The built-in Console takes advantage of a Web Based UI to provide some nice enhancements. E.g. each command is displayed on top of the result it returns, where clicking the command populates the text box making it easy to execute or modify existing commands. Any OK Success responses are in green, whilst any error responses are in red. Also just like JSON values above, it shows a human-friendly view for JSON data which can be clicked to toggle on/off individually:

Redis React is packed with a number of other features, checkout the project home page for an Overview and try it out today!

Swift 2.0 Support!

We're also happy to announce support for the new and much improved Swift 2.0 that's now shipping in Xcode 7 which is now available as a free download for everyone to enjoy.

Swift iOS, XCode and OSX Banner

We're happy to report that Swift has improved substantially and has also had a positive impact on our Swift Client library, e.g. Error Handling is more familiar thanks to Swift 2.0 new Error Handling utilizing a do/try/catch block where you can now access ServiceStack Service Exceptions inside a catch block, e.g:

let client = JsonServiceClient(baseUrl: "https://test.servicestack.net")

let request = ThrowValidation()
request.email = "invalidemail"

do {
    let response = try client.post(request)
} catch let responseError as NSError {
    
    let status:ResponseStatus = responseError.convertUserInfo()!
    status.errors.count //= 3
    
    status.errors[0].errorCode! //= InclusiveBetween
    status.errors[0].fieldName! //= Age
    status.errors[0].message!   //= 'Age' must be between 1 and 120. You entered 0.
}

The addition of the new catch keyword means the the previous Async promise error handling has now been renamed to error which now looks like:

let request = ThrowValidation()
request.email = "invalidemail"

client.postAsync(request)
    .error { responseError in
        let status:ResponseStatus = responseError.convertUserInfo()!
        status.errors.count //= 3
        //...
    }

There's improved type inference where before you had to include the full type signature in the closure continuation:

client.getAsync(AppOverview())
    .then(body:{(r:AppOverviewResponse) -> Void in 
        r.topTechnologies.count //= 100
        ... 
    })

In Swift 2.0 this has now been reduced to the absolute minimum code required:

client.getAsync(AppOverview())
    .then { 
        $0.topTechnologies.count //= 100
        //... 
    }

If preferred you can continue marking it up with as much additional Type information or optional syntax as you'd like, e.g:

client.getAsync(AppOverview())
    .then({ r in
        r.topTechnologies.count //= 100
        //... 
    })

Download ServiceStack Xcode 7 Plugin

ServiceStackXCode.dmg download

Once opened, the ServiceStack XCode Plugin can be installed by dragging it to the XCode Plugins directory:

ServiceStackXCode.dmg Installer

Swift 2.0 Changes

Swift 2.0 is a major upgrade that introduced a number of breaking changes which required updating both Server generated DTO's as well as JsonServiceClient client library. Whilst the POCO DTO definition remained exactly the same, i.e:

public class AllCollectionTypes
{
    required public init(){}
    public var intArray:[Int] = []
    public var intList:[Int] = []
    public var stringArray:[String] = []
    public var stringList:[String] = []
    public var pocoArray:[Poco] = []
    public var pocoList:[Poco] = []
    public var pocoLookup:[String:[Poco]] = [:]
    public var pocoLookupMap:[String:[String:Poco]] = [:]
}

Old Type Extensions

In Swift 1.x the amount of boilerplate required for transparent JSON serialization without being able to use Swift's incomplete reflection support required this amount of boilerplate repeated for every type:

extension AllCollectionTypes : JsonSerializable
{
    public class var typeName:String { return "AllCollectionTypes" }
    public class func reflect() -> Type<AllCollectionTypes> {
        return TypeConfig.config() ?? TypeConfig.configure(Type<AllCollectionTypes>(
            properties: [
                Type<AllCollectionTypes>.arrayProperty("intArray", get: { $0.intArray }, set: { $0.intArray = $1 }),
                Type<AllCollectionTypes>.arrayProperty("intList", get: { $0.intList }, set: { $0.intList = $1 }),
                Type<AllCollectionTypes>.arrayProperty("stringArray", get: { $0.stringArray }, set: { $0.stringArray = $1 }),
                Type<AllCollectionTypes>.arrayProperty("stringList", get: { $0.stringList }, set: { $0.stringList = $1 }),
                Type<AllCollectionTypes>.arrayProperty("pocoArray", get: { $0.pocoArray }, set: { $0.pocoArray = $1 }),
                Type<AllCollectionTypes>.arrayProperty("pocoList", get: { $0.pocoList }, set: { $0.pocoList = $1 }),
                Type<AllCollectionTypes>.objectProperty("pocoLookup", get: { $0.pocoLookup }, set: { $0.pocoLookup = $1 }),
                Type<AllCollectionTypes>.objectProperty("pocoLookupMap", get: { $0.pocoLookupMap }, set: { $0.pocoLookupMap = $1 }),
            ]))
    }
    public func toJson() -> String {
        return AllCollectionTypes.reflect().toJson(self)
    }
    public class func fromJson(json:String) -> AllCollectionTypes? {
        return AllCollectionTypes.reflect().fromJson(AllCollectionTypes(), json: json)
    }
    public class func fromObject(any:AnyObject) -> AllCollectionTypes? {
        return AllCollectionTypes.reflect().fromObject(AllCollectionTypes(), any:any)
    }
    public func toString() -> String {
        return AllCollectionTypes.reflect().toString(self)
    }
    public class func fromString(string:String) -> AllCollectionTypes? {
        return AllCollectionTypes.reflect().fromString(AllCollectionTypes(), string: string)
    }
}

New Type Extensions

This has now been greatly reduced in Swift 2.0 thanks to Protocol Extensions (aka traits) where it's down to just:

extension AllCollectionTypes : JsonSerializable
{
    public static var typeName:String { return "AllCollectionTypes" }
    public static var metadata = Metadata.create([
            Type<AllCollectionTypes>.arrayProperty("intArray", get: { $0.intArray }, set: { $0.intArray = $1 }),
            Type<AllCollectionTypes>.arrayProperty("intList", get: { $0.intList }, set: { $0.intList = $1 }),
            Type<AllCollectionTypes>.arrayProperty("stringArray", get: { $0.stringArray }, set: { $0.stringArray = $1 }),
            Type<AllCollectionTypes>.arrayProperty("stringList", get: { $0.stringList }, set: { $0.stringList = $1 }),
            Type<AllCollectionTypes>.arrayProperty("pocoArray", get: { $0.pocoArray }, set: { $0.pocoArray = $1 }),
            Type<AllCollectionTypes>.arrayProperty("pocoList", get: { $0.pocoList }, set: { $0.pocoList = $1 }),
            Type<AllCollectionTypes>.objectProperty("pocoLookup", get: { $0.pocoLookup }, set: { $0.pocoLookup = $1 }),
            Type<AllCollectionTypes>.objectProperty("pocoLookupMap", get: { $0.pocoLookupMap }, set: { $0.pocoLookupMap = $1 }),
        ])
}

This still doesn't make use any reflection so JSON serialization should continue to perform exceptionally well.

New Service Client Features

During the upgrade we've also added a number of new features to the JsonServiceClient where it's public ServiceClient protocol has been expanded to:

public protocol ServiceClient
{
    func get(request:T) throws -> T.Return
    func get(request:T) throws -> Void
    func get(request:T, query:[String:String]) throws -> T.Return
    func get(relativeUrl:String) throws -> T
    func getAsync(request:T) -> Promise<T.Return>
    func getAsync(request:T) -> Promise<Void>
    func getAsync(request:T, query:[String:String]) -> Promise<T.Return>
    func getAsync(relativeUrl:String) -> Promise<T>
    
    func post(request:T) throws -> T.Return
    func post(request:T) throws -> Void
    func post(relativeUrl:String, request:Request?) throws -> Response
    func postAsync(request:T) -> Promise<T.Return>
    func postAsync(request:T) -> Promise<Void>
    func postAsync(relativeUrl:String, request:Request?) -> Promise<Response>
    
    func put(request:T) throws -> T.Return
    func put(request:T) throws -> Void
    func put(relativeUrl:String, request:Request?) throws -> Response
    func putAsync(request:T) -> Promise<T.Return>
    func putAsync(request:T) -> Promise<Void>
    func putAsync(relativeUrl:String, request:Request?) -> Promise<Response>
    
    func delete(request:T) throws -> T.Return
    func delete(request:T) throws -> Void
    func delete(request:T, query:[String:String]) throws -> T.Return
    func delete(relativeUrl:String) throws -> T
    func deleteAsync(request:T) -> Promise<T.Return>
    func deleteAsync(request:T) -> Promise<Void>
    func deleteAsync(request:T, query:[String:String]) -> Promise<T.Return>
    func deleteAsync(relativeUrl:String) -> Promise<T>
    
    func patch(request:T) throws -> T.Return
    func patch(request:T) throws -> Void
    func patch(relativeUrl:String, request:Request?) throws -> Response
    func patchAsync(request:T) -> Promise<T.Return>
    func patchAsync(request:T) -> Promise<Void>
    func patchAsync(relativeUrl:String, request:Request?) -> Promise<Response>
    
    func send(request:T) throws -> T.Return
    func send(request:T) throws -> Void
    func send(intoResponse:T, request:NSMutableURLRequest) throws -> T
    func sendAsync(intoResponse:T, request:NSMutableURLRequest) -> Promise<T>
    
    func getData(url:String) throws -> NSData
    func getDataAsync(url:String) -> Promise<NSData>
}

Where new support has been added for IReturnVoid and PATCH Requests.

Swift HTTP Marker Interfaces

The new send* API's take advantage of the HTTP Verb Interface Markers described below to send the Request DTO using the annotated HTTP Method, e.g:

public class HelloByGet : IReturn, IGet 
{
    public typealias Return = HelloResponse
    public var name:String?
}
public class HelloByPut : IReturn, IPut 
{
    public typealias Return = HelloResponse
    public var name:String?
}

let response = try client.send(HelloByGet())  //GET

client.sendAsync(HelloByPut())                //PUT
    .then { }

ServiceStack's Error Response Status DTO's are now included in the JsonServiceClient.swift source instead of being repeated within each ServiceStack Reference added to your project, mitigating any duplicate DTO conflicts.

Java ServiceStack Reference

The Java Android Studio and Eclipse Plugins have also been improved in this release, the new versions which can be downloaded below:

Download ServiceStack IDEA Android Studio Plugin

Download ServiceStack Eclipse Plugin

Send Raw String or byte[] Requests

You can easily get the raw string Response from Request DTO's that return are annotated with IReturn<string>, e.g:

public static class HelloString implements IReturn<String> { ... }

String response = client.get(new HelloString().setName("World"));

You can also specify that you want the raw UTF-8 byte[] or String response instead of a the deserialized Response DTO by specifying the Response class you want returned, e.g:

byte[] response = client.get("/hello?Name=World", byte[].class);

Java HTTP Marker Interfaces

Like the .NET and Swift Service Clients, the HTTP Interface markers are also annotated on Java DTO's and let you use the same send API to send Requests via different HTTP Verbs, e.g:

public static class HelloByGet implements IReturn<HelloResponse>, IGet { ... }
public static class HelloByPut implements IReturn<HelloResponse>, IPut { ... }

HelloResponse response = client.send(new HelloByGet().setName("World")); //GET

client.sendAsync(new HelloByPut().setName("World"),                         //PUT
    new AsyncResult<HelloResponse>() {
        @Override
        public void success(HelloResponse response) { }
    });

IReturnVoid Support

New Sync/Async overloads have been added for IReturnVoid Request DTO's:

client.delete(new DeleteCustomer().setId(1));

Java Annotations

The built-in Java Annotations now have their metadata available at runtime as they're now annotated with:

@Retention(RetentionPolicy.RUNTIME)
public @interface Api { ... }

Add ServiceStack Reference

Export Types

By default ServiceStack's Native Type Feature doesn't emit any System types built into the .NET Framework, these can now be emitted for non-.NET Languages with the new ExportTypes list, e.g. if your DTO's exposes the DayOfWeek System Enum it can be exported by adding it to the pre-registered NativeTypesFeature's Plugin with:

 var nativeTypes = this.GetPlugin<NativeTypesFeature>();
nativeTypes.MetadataTypesConfig.ExportTypes.Add(typeof(DayOfWeek));

Now if any of your DTO's has a DayOfWeek property it will emitted in the generated DTO's, Java example:

public static enum DayOfWeek
{
    Sunday,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday;
}

Service Clients

HTTP Verb Interface Markers

You can decorate your Request DTO's using the IGet, IPost, IPut, IDelete and IPatch interface markers and the Send and SendAsync API's will use it to automatically send the Request using the selected HTTP Method. E.g:

public class HelloByGet : IReturn<HelloResponse>, IGet 
{
    public string Name { get; set; }
}
public class HelloByPut : IReturn<HelloResponse>, IPut 
{
    public string Name { get; set; }
}

var response = client.Send(new HelloByGet { Name = "World" }); //GET

await client.SendAsync(new HelloByPut { Name = "World" }); //PUT

This was feature was previously only implemented in StripeGateway, but is now available in all .NET Service Clients as well as the latest Java JsonServiceClient and Swift JsonServiceClient.

Whilst a simple feature, it enables treating your remote services as a message-based API yielding its many inherent advantages where your Application API's need only pass Request DTO models around to be able to invoke remote Services, decoupling the Service Request from its implementation which can be now easily managed by a high-level adapter that takes care of proxying the Request to the underlying Service Client. The adapter could also add high-level functionality of it's own including auto retrying of failed requests, generic error handling, logging/telemetrics, event notification, throttling, offline queuing/syncing, etc.

Async StripeGatway

Perhaps a clearer indication of the simplicity and generic functionality possible from a message-based API's is how it's possible to add Async support to all Stripe Gateway API's in <1 hour from initial feature request to implementation and published to our pre-release NuGet packages where it's available for immediate use:

var charge = await gateway.PostAsync(new ChargeStripeCustomer {
    Customer = customer.Id,
    Amount = 100,
    Currency = "usd",
    Description = "Test Charge Customer",
});

The Stripe Gateway provides a typed .NET message-based API to Stripe's REST Services which as it's inspired by Ruby conventions, uses a snake_case naming convention so it's a good example of viewing the benefits of a message-based API's in isolation, i.e. independent from the features ServiceStack adds around it's own .NET Services.

Unlike other Stripe Client implementations the StripeGateway.cs is

  • Small - Fits in a single class where the majority of the code-base contains Stripe's Typed DTO's and Currency Info
  • Simple - Its tiny code-base has great re-use, requiring less effort to create, maintain, extend and test
  • Highly Testable - Its small surface area implements the typed, mockable IRestGateway
  • Open Ended - Users can use their own Request DTO's to call new Stripe Services that StripeGateway has no knowledge about

Declarative Message-based APIs

The only custom code required to implement the ChargeStripeCustomer above is this single, clean, declarative Request DTO:

[Route("/charges")]
public class ChargeStripeCustomer : IPost, IReturn<StripeCharge>
{
    public int Amount { get; set; }
    public string Currency { get; set; }
    public string Customer { get; set; }
    public string Card { get; set; }
    public string Description { get; set; }
    public bool? Capture { get; set; }
    public int? ApplicationFee { get; set; }
}

Which contains all the information needed to call the Stripe Service including the /charges relative url, using the POST HTTP method and the typed StripeCharge DTO it returns. To charge a Customer the Request DTO can either use the explicit Post/PostAsync or universal Send/SendAsync StripeGateway methods.

We hope this example makes it clearer why we continue to encourage others to adopt a message-based API for accessing any remote Services and how we're able to continue to deliver rich generic functionality and value to all ServiceStack Services with minimal code, effort, friction and complexity as a result.

Debuggable Razor Views

Razor Views are now debuggable for Debug builds by default, it can also be explicitly specified on:

Plugins.Add(new RazorFormat {
    IncludeDebugInformation = true,
    CompileFilter = compileParams => ...
})

The new CompileFilter is an advanced option that lets modify the CompilerParameters used by the C# CodeDom provider to compile the Razor Views if needed.

ss-utils.js

$.fn.ajaxSubmit

ajaxSubmit has been decoupled from ServiceStack's auto bindForm ajax and validation helper so it can be used independently to submit a form on demand. This is used in the connections.jsx React Component of Redis React's Connections Page to auto submit the form via ajax to the specified /connection url with the populated Form INPUT values. It also disables the #btnConnect submit button and adds a .loading class to the form whilst it's in transit which is used to temporarily show the loading sprite:

var Connections = React.createClass({
    //...
    onSubmit: function (e) {
        e.preventDefault();

        var $this = this;
        $(e.target).ajaxSubmit({
            onSubmitDisable: $("#btnConnect"),
            success: function () {
                $this.setState({ successMessage: "Connection was changed" });
                Actions.loadConnection();
            }
        });
    },
    render: function () {
        var conn = this.state.connection;
        return (
          <div id="connections-page">
            <div className="content">
                <form id="formConnection" className="form-inline" onSubmit={this.onSubmit} action="/connection">
                    <h2>Redis Connection</h2>
                    <div className="form-group">
                        <input name="host" type="text" />
                        <input name="port" type="text" className="form-control" />
                        <input name="db" type="text" className="form-control" />
                    </div>
                    <p className="actions">
                        <img className="loader" src="/img/ajax-loader.gif" />
                        <button id="btnConnect" className="btn btn-default btn-primary">Change Connection</button>
                    </p>
                    <p className="bg-success">{this.state.successMessage}</p>
                    <p className="bg-danger error-summary"></p>
                </form>
            </div>
          </div>
        );
    }
}; 

$.ss.parseResponseStatus

Lets you easily parse the raw text of a Ajax Error Response into a responseStatus JavaScript object, example used in Redis React's Console Component:

.fail(function (jq, jqStatus, statusDesc) {
    var status = $.ss.parseResponseStatus(jq.responseText, statusDesc);
    Actions.logEntry({
        cmd: cmd,
        result: status.message,
        stackTrace: status.stackTrace,
        type: 'err',
    });
});

$.ss.bindAll

The new bindAll API is a simple helper for creating lightweight JavaScript objects by binding this for all functions of an object literal to the object instance, e.g:

var Greeting = $.ss.bindAll({
    name: "World",
    sayHello: function() {
        alert("Hello, " + this.name);
    }
});

var fn = Greeting.sayHello;
fn(); // Hello, World

Redis

Improved LUA support

The new ExecLua API lets you execute LUA Script on a Redis server and returns any result in a generic RedisText Type which can be easily be inspected to access the LUA Script's complex type response. A good example of when to use a server-side LUA script is to reduce the network latency of chatty multi-request API's.

A prime example of this is when using Redis's SCAN API which provides a streaming, non-blocking API to search through the entire Redis KeySet. The number of API calls that's required is bounded to the size of the Redis KeySet which could quickly result in a large number of Redis Operations yielding an unacceptable delay due to the latency of multiple dependent remote network calls.

An easy solution is to instead have the multiple SCAN calls performed in-process on the Redis Server, eliminating the network latency of multiple SCAN calls, e.g:

const string FastScanScript = @"
local limit = tonumber(ARGV[2])
local pattern = ARGV[1]
local cursor = 0
local len = 0
local results = {}
repeat
    local r = redis.call('scan', cursor, 'MATCH', pattern, 'COUNT', limit)
    cursor = tonumber(r[1])
    for k,v in ipairs(r[2]) do
        table.insert(results, v)
        len = len + 1
        if len == limit then break end
    end
until cursor == 0 or len == limit
return results";

RedisText r = redis.ExecLua(FastScanScript, "key:*", "10");
r.Children.Count.Print() //= 10

The ExecLua API returns this complex LUA table response in the Children collection of the RedisText Response.

This above API is equivalent to C# API below which returns the first 10 results matching the key:* pattern:

var keys = Redis.ScanAllKeys(pattern: "key:*", pageSize: 10)
    .Take(10).ToList();

However the C# Streaming API above would require an unknown number of Redis Operations to complete the request whereas only 1 call to Redis is required for the LUA Script. The number of SCAN calls can be reduced by choosing a higher pageSize to tell Redis to scan more keys.

ExecCachedLua

ExecCachedLua is a convenient high-level API that eliminates the bookkeeping required for executing high-performance server LUA Scripts which suffers from many of the problems that RDBMS stored procedures have which depends on pre-existing state in the RDBMS that needs to be updated with the latest version of the Stored Procedure.

With Redis LUA you either have the option to send, parse, load then execute the entire LUA script each time it's called or alternatively you could pre-load the LUA Script into Redis once on StartUp and then execute it using the Script's SHA1 hash. The issue with this is that if the Redis server is accidentally flushed you're left with a broken application relying on a pre-existing script that's no longer there. The new ExecCachedLua API provides the best of both worlds where it will always execute the compiled SHA1 script, saving bandwidth and CPU but will also re-create the LUA Script if it no longer exists.

You can now execute the compiled LUA script above by its SHA1 identifier, which continues to work regardless if it never existed or was removed at runtime, e.g:

// #1: Loads LUA script and caches SHA1 hash in Redis Client
r = redis.ExecCachedLua(FastScanScript, sha1 =>
    redis.ExecLuaSha(sha1, "key:*", "10"));

// #2: Executes using cached SHA1 hash
r = redis.ExecCachedLua(FastScanScript, sha1 =>
    redis.ExecLuaSha(sha1, "key:*", "10"));

// Deletes all existing compiled LUA scripts 
redis.ScriptFlush();

// #3: Executes using cached SHA1 hash, gets NOSCRIPT Error, re-creates and re-executes with SHA1 hash
r = redis.ExecCachedLua(FastScanScript, sha1 =>
    redis.ExecLuaSha(sha1, "key:*", "10"));

New Redis Client APIs

All new APIs added in this release:

IRedisClient

  • Type - returns key Type as string that can be used in pipelines
  • GetStringCount - returns STRLEN in a consistent API to fetch size of value, i.e Get{DataType}Count()
  • SetValues - Set multiple String values, alias for SetAll
  • ExecLua - Execute Lua Script that returns a generic RedisText result
  • ExecLuaSha - Execute Lua Script by SHA1 which returns a generic RedisText result
  • ExecCachedLua - Execute Lua Script by SHA1, re-creating it if it no longer exists

IRedisNativeClient

  • Type - returns key Type as string that can be used in pipelines
  • EvalCommand - Execute Lua Script that returns a generic RedisData byte[] result
  • EvalShaCommand - Execute Lua Script by SHA1 which returns a generic RedisData byte[] result

Caching

The ICacheClientExtended API is used to to provide additional non-core functionality to our most popular Caching providers:

  • Redis
  • OrmLite RDBMS
  • In Memory
  • AWS

The new API's are added as Extension methods on ICacheClient so they're easily accessible without casting, the new API's available include:

  • GetKeysByPattern(pattern) - return keys matching a wildcard pattern
  • GetAllKeys() - return all keys in the caching provider
  • GetKeysStartingWith() - Streaming API to return all keys Starting with a prefix

With these new API's you can now easily get all active User Sessions using any of the supported Caching providers above with:

var sessionPattern = IdUtils.CreateUrn<IAuthSession>(""); //= urn:iauthsession:
var sessionKeys = Cache.GetKeysStartingWith(sessionPattern).ToList();

var allSessions = Cache.GetAll<IAuthSession>(sessionKeys);

Minor Features

  • All 3rd Party NuGet dependencies updated to latest stable version
  • C# FormatException return 400 by default
  • New AuthServerFilter and AuthClientFilter available to all DotNetOpenAuth OAuth2 Providers
  • Patch added on JavaScript JsonServiceClient by @DeonHeyns
  • Added UnsafeOrderBy() to OrmLite SqlExpression to allow unchecked ordering by results by Custom SQL