Scott Hanselman

WinAppDriver - Test any app with Appium's Selenium-like tests on Windows

November 16, '16 Comments [12] Posted in Open Source | Win10
Sponsored By
WinAppDriver - Appium testing Windows Apps

I've found blog posts on my site where I'm using the Selenium Web Testing Framework as far back as 2007! Today there's Selenium Drivers for every web browser including Microsoft Edge. You can write Selenium tests in nearly any language these days including Ruby, Python, Java, and C#.

I'm a big Selenium fan. I like using it with systems like BrowserStack to automate across many different browser on many operating systems.

"Appium" is a great Selenium-like testing framework that implements the "WebDriver" protocol - formerly JsonWireProtocol.

WebDriver is a remote control interface that enables introspection and control of user agents. It provides a platform- and language-neutral wire protocol as a way for out-of-process programs to remotely instruct the behavior of web browsers.

From the Appium website, "Appium is 'cross-platform': it allows you to write tests against multiple platforms (iOS, Android, Windows), using the same API. This enables code reuse between iOS, Android, and Windows testsuites"

Appium is a webserver that exposes a REST API. The WinAppDriver enables Appium by using new APIs that were added in Windows 10 Anniversary Edition that allow you to test any Windows app. That means ANY Windows App. Win32, VB6, WPF, UWP, anything. Not only can you put any app in the Windows Store, you can do full and complete UI testing of those apps with a tool that is already familiar to Web Developers like myself.

Your preferred language, your preferred test runner, the Appium Server, and your app

You can write tests in C# and run them from Visual Studio's Test Runner. You can press any button and basically totally control your apps.

// Launch the calculator app
DesiredCapabilities appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App");
CalculatorSession = new RemoteWebDriver(new Uri(WindowsApplicationDriverUrl), appCapabilities);
Assert.IsNotNull(CalculatorSession);
CalculatorSession.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(2));
// Make sure we're in standard mode
CalculatorSession.FindElementByXPath("//Button[starts-with(@Name, \"Menu\")]").Click();
OriginalCalculatorMode = CalculatorSession.FindElementByXPath("//List[@AutomationId=\"FlyoutNav\"]//ListItem[@IsSelected=\"True\"]").Text;
CalculatorSession.FindElementByXPath("//ListItem[@Name=\"Standard Calculator\"]").Click();

It's surprisingly easy once you get started.

public void Addition()
{
CalculatorSession.FindElementByName("One").Click();
CalculatorSession.FindElementByName("Plus").Click();
CalculatorSession.FindElementByName("Seven").Click();
CalculatorSession.FindElementByName("Equals").Click();
Assert.AreEqual("Display is 8 ", CalculatorResult.Text);
}

You can automate any part of Windows, even the Start Menu or Cortana.

var searchBox = CortanaSession.FindElementByAccessibilityId("SearchTextBox");
Assert.IsNotNull(searchBox);
searchBox.SendKeys("What is eight times eleven");

var bingPane = CortanaSession.FindElementByName("Bing");
Assert.IsNotNull(bingPane);

var bingResult = bingPane.FindElementByName("88");
Assert.IsNotNull(bingResult);

If you use "AccessibiltyIds" and refer to native controls in a non-locale specific way you can even reuse test code across platforms. For example, you could write sign in code for Windows, iOS, your web app, and even a VB6 Win32 app. ;)

Testing a VB6 app with WinAppDriver

Appium and WebAppDriver a nice alternative to "CodedUI Tests." CodedUI tests are great but just for Windows apps. If you're a web developer or you are writing cross platform or mobile apps you should check it out.


Sponsor: Help your team write better, shareable SQL faster! Discover how your whole team can write better, shareable SQL faster with a free trial of SQL Prompt. Write, refactor and share SQL effortlessly, try it now.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Stateless 3.0 - A State Machine library for .NET Core

November 11, '16 Comments [38] Posted in DotNetCore | Open Source
Sponsored By

.NET StandardState Machines and business processes that describe a series of states seem like they'll be easy to code but you'll eventually regret trying to do it yourself. Sure, you'll start with a boolean, then two, then you'll need to manage three states and there will be an invalid state to avoid then you'll just consider quitting all together. ;)

"Stateless" is a simple library for creating state machines in C# code. It's recently been updated to support .NET Core 1.0. They achieved this not by targeting .NET Core but by writing to the .NET Standard. Just like API levels in Android abstract away the many underlying versions of Android, .NET Standard is a set of APIs that all .NET platforms have to implement. Even better, the folks who wrote Stateless 3.0 targeted .NET Standard 1.0, which is the broadest and most compatible standard - it basically works everywhere and is portable across the .NET Framework on Windows, .NET Core on Windows, Mac, and LInux, as well as Windows Store apps and all phones.

Sure, there's Windows Workflow, but it may be overkill for some projects. In Nicholas Blumhardt's words:

...over time, the logic that decided which actions were allowed in each state, and what the state resulting from an action should be, grew into a tangle of if and switch. Inspired by Simple State Machine, I eventually refactored this out into a little state machine class that was configured declaratively: in this state, allow this trigger, transition to this other state, and so-on.

A state machine diagram describing the states a Bug can go throughYou can use state machines for anything. You can certainly describe high-level business state machines, but you can also easily model IoT device state, user interfaces, and more.

Even better, Stateless also serialize your state machine to a standard text-based "DOT Graph" format that can then be generated into an SVG or PNG like this with http://www.webgraphviz.com. It's super nice to be able to visualize state machines at runtime.

Modeling a Simple State Machine with Stateless

Let's look at a few code examples. You start by describing some finite states as an enum, and some finite "triggers" that cause a state to change. Like a switch could have On and Off as states and Toggle as a trigger.

A more useful example is the Bug Tracker included in the Stateless source on GitHub. To start with here are the states of a Bug and the Triggers that cause state to change:

enum State { Open, Assigned, Deferred, Resolved, Closed }
enum Trigger { Assign, Defer, Resolve, Close }

You then have your initial state, define your StateMachine, and if you like, you can pass Parameters when a state is trigger. For example, if a Bug is triggered with Assign you can pass in "Scott" so the bug goes into the Assigned state - assigned to Scott.

State _state = State.Open;
StateMachine<State, Trigger> _machine;
StateMachine<State, Trigger>.TriggerWithParameters<string> _assignTrigger;

string _title;
string _assignee;

Then, in this example, the Bug constructor describes the state machine using a fluent interface that reads rather nicely.

public Bug(string title)
{
_title = title;

_machine = new StateMachine<State, Trigger>(() => _state, s => _state = s);

_assignTrigger = _machine.SetTriggerParameters<string>(Trigger.Assign);

_machine.Configure(State.Open)
.Permit(Trigger.Assign, State.Assigned);

_machine.Configure(State.Assigned)
.SubstateOf(State.Open)
.OnEntryFrom(_assignTrigger, assignee => OnAssigned(assignee))
.PermitReentry(Trigger.Assign)
.Permit(Trigger.Close, State.Closed)
.Permit(Trigger.Defer, State.Deferred)
.OnExit(() => OnDeassigned());

_machine.Configure(State.Deferred)
.OnEntry(() => _assignee = null)
.Permit(Trigger.Assign, State.Assigned);
}

For example, when the State is Open, it can be Assigned. But as this is written (you can change it) you can't close a Bug that is Open but not Assigned. Make sense?

When the Bug is Assigned, you can Close it, Defer it, or Assign it again. That's PermitReentry(). Also, notice that Assigned is a Substate of Open.

You can have events that are fired as states change. Those events can take actions as you like.

void OnAssigned(string assignee)
{
if (_assignee != null && assignee != _assignee)
SendEmailToAssignee("Don't forget to help the new employee.");

_assignee = assignee;
SendEmailToAssignee("You own it.");
}

void OnDeassigned()
{
SendEmailToAssignee("You're off the hook.");
}

void SendEmailToAssignee(string message)
{
Console.WriteLine("{0}, RE {1}: {2}", _assignee, _title, message);
}

With a nice State Machine library like Stateless you can quickly model states that you'd ordinarily do with a "big ol' switch statement."

What have you used for state machines like this in your projects?


Sponsor: Big thanks to Telerik! They recently published a comprehensive whitepaper on The State of C#, discussing the history of C#, what’s new in C# 7 and whether C# is still a viable language. Check it out!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

ASP.NET Core RESTful Web API versioning made easy

November 2, '16 Comments [30] Posted in ASP.NET Web API | Open Source
Sponsored By

Pic by WoCTechChat used under Creative Commons There's a LOT of interesting and intense arguments that have been made around how you should version your Web API. As soon as you say RESTful it turns into a religious argument where folks may just well quote from the original text. ;)

Regardless of how you personally version your Web APIs, and side-stepping any arguments one way or the other, there's great new repository by Chris Martinez that Jon Galloway turned me on to at https://github.com/Microsoft/aspnet-api-versioning. There's ASP.NET 4.x Web API, ODATA with ASP.NET Web APIs, and now ASP.NET Core 1.x. Fortunately Chris has assembled a nicely factored set of libraries called "ASP.NET API Versioning" that add service API versioning in a very convenient way.

As Chris points out:

The default API versioning configuration is compliant with the versioning semantics outlined by the Microsoft REST Guidelines. There are also a number of customization and extension points available to support transitioning services that may not have supported API versioning in the past or supported API versioning with semantics that are different from the Microsoft REST versioning guidelines.

It's also worth pointing out how great the documentation is given it's mostly a one-contributor project. I'm sure Chris would appreciate your help though, even if you're a first timer.

Chris has NuGet packages for three flavors of Web APIs on ASP.NET:

But you should really clone the repo and check out his excellent samples.

When versioning services there's a few schools of thought and with ASP.NET Core it's super easy to get started:

public void ConfigureServices( IServiceCollection services )
{
services.AddMvc();
services.AddApiVersioning();

// remaining other stuff omitted for brevity
}

Oh, but you already have an API that's not versioned yet?

services.AddApiVersioning(
o =>
{
o.AssumeDefaultVersionWhenUnspecified = true );
o.DefaultApiVersion = new ApiVersion( new DateTime( 2016, 7, 1 ) );
} );

Your versions can look however'd you like them to:

  • /api/foo?api-version=1.0
  • /api/foo?api-version=2.0-Alpha
  • /api/foo?api-version=2015-05-01.3.0
  • /api/v1/foo
  • /api/v2.0-Alpha/foo
  • /api/v2015-05-01.3.0/foo

QueryString Parameter Versioning

I'm not a fan of this one, but here's the general idea:

[ApiVersion( "2.0" )]
[Route( "api/helloworld" )]
public class HelloWorld2Controller : Controller {
[HttpGet]
public string Get() => "Hello world!";
}

So this means to get 2.0 over 1.0 in another Controller with the same route, you'd go here:

/api/helloworld?api-version=2.0

Also, don't worry, you can use namespaces to have multiple HelloWorldControllers without having to have any numbers in the class names. ;)

URL Path Segment Versioning

This happens to be my first choice (yes I know Headers are "better," more on that later). You put the version in the route like this.

Here we're throwing in a little curveball. There's three versions but just two controllers.

[ApiVersion( "1.0" )]
[Route( "api/v{version:apiVersion}/[controller]" )]
public class HelloWorldController : Controller {
public string Get() => "Hello world!";
}

[ApiVersion( "2.0" )]
[ApiVersion( "3.0" )]
[Route( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorld2Controller : Controller {
[HttpGet]
public string Get() => "Hello world v2!";

[HttpGet, MapToApiVersion( "3.0" )]
public string GetV3() => "Hello world v3!";
}

To be clear, you have total control, but the result from the outside is quite clean with /api/v[1|2|3]/helloworld. In fact, you can see with this example where this is more sophisticated than what you can do with routing out of the box. (I know some of you are thinking "meh I'll just make a route table." I think the semantics are much clearer and cleaner this way.

Header Versioning

Or, the hardest way (and the one that a lot of people this is best, but I disagree) is HTTP Headers. Set this up in ConfigureServices for ASP.NET Core:

public void ConfigureServices( IServiceCollection services )
{
services.AddMvc();
services.AddApiVersioning(o => o.ApiVersionReader = new HeaderApiVersionReader("api-version"));
}

When you do HeaderApiVersioning you won't be able to just do a GET in your browser, so I'll use Postman to add the header (or I could use Curl, or WGet, or PowerShell, or a Unit Test):

image

Deprecating

Speaking of semantics, here's a nice one. Let's say an API is going away in the next 6 months.

[ApiVersion( "2.0" )]
[ApiVersion( "1.0", Deprecated = true )]

This advertises that 1.0 is going away soon and that folks should consider 2.0. Where does it advertise this fact? The response headers!

api-supported-versions: 2.0, api-deprecated-versions: 1.0

All in all, this is very useful stuff and I'm happy to add it to my personal toolbox. Should this be built in? I don't know but I sure appreciate that it exists.

SIDE NOTE: There is/was the start of a VersionRoute over in the AspNet.Mvc repo. Maybe these folks need to join forces?

How do YOU version your Web APIs and Services?


Sponsor: Big thanks to Telerik! They recently launched their UI toolset for ASP.NET Core so feel free to check it out or learn more about ASP.NET Core development in their recent whitepaper.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Using dotnet watch test for continuous testing with .NET Core and XUnit.net

October 29, '16 Comments [30] Posted in Open Source
Sponsored By

When teaching .NET Core I do a lot of "dotnet new" Hello World demos to folks who've never seen it before. That has it's place, but I also wanted to show how easy it is to get setup with Unit Testing on .NET Core.

For this blog post I'm going to use the command line so you know there's nothing hidden, but you can also use Visual Studio or Visual Studio Code, of course. I'll start the command prompt then briefly move to Code.

Starting from an empty folder, I'll make a SomeApp folder and a SomeTests folder.

C:\example\someapp> dotnet new
C:\example\someapp> md ..\sometests && cd ..\sometests
C:\example\sometests> dotnet new -t xunittest

At this point I've got a HelloWorld app and a basic test but the two aren't related - They aren't attached and nothing real is being tested.

Tests are run with dotnet test, not dotnet run. Tests are libraries and don't have an entry point, so dotnet run isn't what you want.

c:\example>dotnet test SomeTests
Project SomeTests (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
xUnit.net .NET CLI test runner (64-bit win10-x64)
Discovering: SomeTests
Discovered: SomeTests
Starting: SomeTests
Finished: SomeTests
=== TEST EXECUTION SUMMARY ===
SomeTests Total: 1, Errors: 0, Failed: 0, Skipped: 0, Time: 0.197s
SUMMARY: Total: 1 targets, Passed: 1, Failed: 0.

I'll open my test project's project.json and add a reference to my other project.

{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable"
},
"dependencies": {
"System.Runtime.Serialization.Primitives": "4.1.1",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-rc2-*"
},
"testRunner": "xunit",
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.1"
},
"SomeApp": "1.0.0-*"
},
"imports": [
"dotnet5.4",
"portable-net451+win8"
]
}
}
}

I'll make a little thing to test in my App.

public class Calc {
public int Add(int x, int y) => x + y;
}

And add some tests.

public class Tests
{
[Fact]
public void TwoAndTwoIsFour()
{
var c = new Calc();
Assert.Equal(4, c.Add(2, 2));
}

[Fact]
public void TwoAndThreeIsFive()
{
var c = new Calc();
Assert.Equal(4, c.Add(2, 3));
}
}

Because the Test app references the other app/library, I can just make changes and run "dotnet test" from the command line. It will build both dependencies and run the tests all at once.

Here's the full output inclding both build and test.

c:\example> dotnet test SomeTests
Project SomeApp (.NETCoreApp,Version=v1.0) will be compiled because inputs were modified
Compiling SomeApp for .NETCoreApp,Version=v1.0

Compilation succeeded.
0 Warning(s)
0 Error(s)

Time elapsed 00:00:00.9814887
Project SomeTests (.NETCoreApp,Version=v1.0) will be compiled because dependencies changed
Compiling SomeTests for .NETCoreApp,Version=v1.0

Compilation succeeded.
0 Warning(s)
0 Error(s)

Time elapsed 00:00:01.0266293


xUnit.net .NET CLI test runner (64-bit win10-x64)
Discovering: SomeTests
Discovered: SomeTests
Starting: SomeTests
Tests.Tests.TwoAndThreeIsFive [FAIL]
Assert.Equal() Failure
Expected: 4
Actual: 5
Stack Trace:
c:\Users\scott\Desktop\testtest\SomeTests\Tests.cs(20,0): at Tests.Tests.TwoAndThreeIsFive()
Finished: SomeTests
=== TEST EXECUTION SUMMARY ===
SomeTests Total: 2, Errors: 0, Failed: 1, Skipped: 0, Time: 0.177s
SUMMARY: Total: 1 targets, Passed: 0, Failed: 1.

Oops, I made a mistake. I'll fix that test and run "dotnet test" again.

c:\example> dotnet test SomeTests
xUnit.net .NET CLI test runner (64-bit .NET Core win10-x64)
Discovering: SomeTests
Discovered: SomeTests
Starting: SomeTests
Finished: SomeTests
=== TEST EXECUTION SUMMARY ===
SomeTests Total: 2, Errors: 0, Failed: 0, Skipped: 0, Time: 0.145s
SUMMARY: Total: 1 targets, Passed: 1, Failed: 0.

I can keep changing code and running "dotnet test" but that's tedious. I'll add dotnet watch as a tool in my Test project's project.json.

{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable"
},
"dependencies": {
"System.Runtime.Serialization.Primitives": "4.1.1",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-rc2-*"
},
"tools": {
"Microsoft.DotNet.Watcher.Tools": "1.0.0-preview2-final"
},
"testRunner": "xunit",
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.1"
},
"SomeApp": "1.0.0-*"
},

"imports": [
"dotnet5.4",
"portable-net451+win8"
]
}
}
}

Then I'll go back and rather than typing  "dotnet test" I'll type "dotnet watch test."

c:\example> dotnet watch test
[DotNetWatcher] info: Running dotnet with the following arguments: test
[DotNetWatcher] info: dotnet process id: 14064
Project SomeApp (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
Project SomeTests (.NETCoreApp,Version=v1.0) will be compiled because inputs were modified
Compiling SomeTests for .NETCoreApp,Version=v1.0
Compilation succeeded.
0 Warning(s)
0 Error(s)
Time elapsed 00:00:01.1479348

xUnit.net .NET CLI test runner (64-bit .NET Core win10-x64)
Discovering: SomeTests
Discovered: SomeTests
Starting: SomeTests
Finished: SomeTests
=== TEST EXECUTION SUMMARY ===
SomeTests Total: 2, Errors: 0, Failed: 0, Skipped: 0, Time: 0.146s
SUMMARY: Total: 1 targets, Passed: 1, Failed: 0.
[DotNetWatcher] info: dotnet exit code: 0
[DotNetWatcher] info: Waiting for a file to change before restarting dotnet...

Now if I make a change to either the Tests or the projects under test it will automatically recompile and run the tests!

[DotNetWatcher] info: File changed: c:\example\SomeApp\Program.cs
[DotNetWatcher] info: Running dotnet with the following arguments: test
[DotNetWatcher] info: dotnet process id: 5492
Project SomeApp (.NETCoreApp,Version=v1.0) will be compiled because inputs were modified
Compiling SomeApp for .NETCoreApp,Version=v1.0

I'm able to do all of this with any text editor and a command prompt.

How do YOU test?


Sponsor: Do you deploy the same application multiple times for each of your end customers? The team at Octopus have taken the pain out of multi-tenant deployments. Check out their latest 3.4 release!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Exploring ServiceStack's simple and fast web services on .NET Core

October 23, '16 Comments [33] Posted in Open Source | Web Services
Sponsored By

Northwind - ServiceStack styleI've been doing .NET Open Source since the beginning. Trying to get patches into log4net was hard without things like GitHub and Twitter. We emailed .patch files around and hoped for the best. It was a good time.

There's been a lot of feelings around .NET Open Source over the last decade or so - some positive, some negative. There's been some shining lights though and I'm going to do a few blog posts to call them out. I think having .NET Core be cross platform and open source will be a boon for the .NET Community. However, the community needs to also help out by using non-Microsoft OSS, supporting it, doing PRs, helping with docs, giving talks on new tech and spreading the word.

While some OSS projects are purely volunteer projects, ServiceStack has found some balance with a per-developer pricing model. They also support free usage for small projects. They've got deep integration with all major IDEs and support everything from VS, Xcode, IntelliJ IDEA, and the commandline.

ServiceStack Logo

One major announcement in the least few days as been ServiceStack 4.5.2 on .NET Core! Effectively one year to the day from the feature request and they did it! Their announcement paragraph says it best, emphasis mine.

Whilst the development and tooling experience is still in a transitionary period we believe .NET Core puts .NET Web and Server App development on the cusp of an exciting future - 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.

Many folks ran (and run) ServiceStack on Mono, but it's time to move forward. While Mono is still a fantastic stack on many platforms that .NET Core doesn't support, for mainstream Linux, .NET Core is likely the better choice.

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.

I also want to call out ServiceStack's amazing Release Notes. Frankly, we could all learn from Release Note this good - Microsoft absolutely included. These release notes are the now Gold Standard as far as I'm concerned. Additionally, ServiceStack's Live Demos are unmatched.

Enough gushing. What IS ServiceStack? It's a different .NET way for creating web services. I say you should give it a hard look if you're making Web Services today. They say this:

Service Stack provides an alternate, cleaner POCO-driven way of creating web services.

  • Simplicity
  • Speed
  • Best Practices
  • Model-driven, code-first, friction-free development
  • No XML config, no code-gen, conventional defaults
  • Smart - Infers intelligence from strongly typed DTOs
  • .NET and Mono
  • Highly testable - services are completely decoupled from HTTP
  • Mature - over 5+ years of development
  • Commercially supported and Continually Improved
  • and most importantly - with AutoQuery you get instant queryable APIs. Take a look at what AutoQuery does for a basic Northwind sample.

They've plugged into .NET Core and ASP.NET Core exactly as it was design. They've got sophisticated middleware and fits in cleanly and feels natural. Even more, if you have existing ServiceStack code running on .NET 4.x, they've designed their "AppHost" such that moving over the .NET Core is extremely simple.

ServiceStack has the standard "Todo" application running in both .NET Full Framework and .NET Core. Here's two sites, both .NET and both ServiceStack, but look what's underneath them:

Getting Started with Service Stack

There's a million great demos as I mentioned above with source at https://github.com/NetCoreApps, but I love that ServiceStack has a Northwind Database demo here https://github.com/NetCoreApps/Northwind. It even includes a Dockerfile. Let's check it out. I was able to get it running in Docker in seconds.

>git clone https://github.com/NetCoreApps/Northwind
>cd Northwind
>docker build -t "northwindss/latest" .
>docker run northwindss/latest
Project Northwind.ServiceModel (.NETStandard,Version=v1.6) was previously compiled. Skipping compilation.
Project Northwind.ServiceInterface (.NETStandard,Version=v1.6) was previously compiled. Skipping compilation.
Project Northwind (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
Hosting environment: Production
Content root path: /app/Northwind
Now listening on: https://*:5000
Application started. Press Ctrl+C to shut down.

Let's briefly look at the code, though. It is a great sample and showcases a couple cool features and also is nicely RESTful.

There's some cool techniques in here. It uses SqLITE for the database and the database itself is created with this Unit Test. Here's the ServiceStack AppHost (AppHost is their concept)

public class AppHost : AppHostBase
{
public AppHost() : base("Northwind Web Services", typeof(CustomersService).GetAssembly()) { }

public override void Configure(Container container)
{
container.Register<IDbConnectionFactory>(
new OrmLiteConnectionFactory(MapProjectPath("~/App_Data/Northwind.sqlite"), SqliteDialect.Provider));

//Use Redis Cache
//container.Register<ICacheClient>(new PooledRedisClientManager());

VCardFormat.Register(this);

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

Plugins.Add(new CorsFeature());
}
}

Note host the AppHost base references the Assembly that contains the CustomersService type. That's the assembly that is the ServiceInterface. There's a number of Services in there - CustomersService just happens to be a simple one:

public class CustomersService : Service
{
public object Get(Customers request) =>
new CustomersResponse { Customers = Db.Select<Customer>() };
}

The response for /customers is just the response and a list of Customers:

[DataContract]
[Route("/customers")]
public class Customers : IReturn<CustomersResponse> {}

[DataContract]
public class CustomersResponse : IHasResponseStatus
{
public CustomersResponse()
{
this.ResponseStatus = new ResponseStatus();
this.Customers = new List<Customer>();
}

[DataMember]
public List<Customer> Customers { get; set; }

[DataMember]
public ResponseStatus ResponseStatus { get; set; }
}

Customers has a lovely clean GET that you can see live here: http://northwind.netcore.io/customers. Compare its timestamp to the cached one at http://northwind.netcore.io/cached/customers.

[CacheResponse(Duration = 60 * 60, MaxAge = 30 * 60)]
public class CachedServices : Service
{
public object Get(CachedCustomers request) =>
Gateway.Send(new Customers());

public object Get(CachedCustomerDetails request) =>
Gateway.Send(new CustomerDetails { Id = request.Id });

public object Get(CachedOrders request) =>
Gateway.Send(new Orders { CustomerId = request.CustomerId, Page = request.Page });
}

You may find yourself looking at the source for the Northwind sample and wondering "where's the rest?" (no pun intended!) Turns out ServiceStack will do a LOT for you if you just let it!

The Northwind project is also an example of how much can be achieved with a minimal amount of effort and code. This entire website literally just consists of these three classes . Everything else seen here is automatically provided by ServiceStack using a code-first, convention-based approach. ServiceStack can infer a richer intelligence about your services to better able to provide more generic and re-usable functionality for free!

ServiceStack is an alternative to ASP.NET's Web API. It's a different perspective and a different architecture than what Microsoft provides out of the box. It's important and useful to explore other points of view when designing your systems. It's especially nice when the systems are so thoughtfully factored, well-documented and designed as ServiceStack. In fact, years ago I wrote their tagline: "Thoughtfully architected, obscenely fast, thoroughly enjoyable web services for all."

Have you used ServiceStack? Have you used other open source .NET Web Service/API frameworks? Share your experience in the comments!


Sponsor: Big thanks to Telerik! 60+ ASP.NET Core controls for every need. The most complete UI toolset for x-platform responsive web and cloud development. Try now 30 days for free!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Previous Page Page 2 of 42 in the Open Source category Next Page

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.