Getting started

Getting started

The design principle of Footloose is to be easy to use while Footloose itself is a powerful framework. This quick start will help you to understand on how to use Footloose in a simple RPC style weather service example.

The weather example consists of three parts:

  • The client application
  • The shared library that contains the service interface and request/response DTOs
  • The service application

First lets create a new Solution called „WeatherInfo“ and add three C# projects:

  • “WeatherInfo.Client” as console application
  • “WeatherInfo.Shared” as c# library project
  • “WeatherInfo.Service” as console application
Then reference the Footloose Assembly via the NuGet Package Manager for the client and service projects.

Implementing the shared components

Then create the request/response DTOs in the „WeatherInfo.Shared“ project:

namespace WeatherInfo.Shared
{
    [Serializable]
    public class WeatherInfoRequest
    {
        public string City { get; set; }
    }
}

namespace WeatherInfo.Shared
{
    [Serializable]
    public class WeatherInfoResponse
    {
        public string City { get; set; }
        public string Temperature { get; set; }
    }
}

Now lets implement the contract of the service. Add a new Interface to the shared project:

namespace WeatherInfo.Shared
{
    public interface IWeatherInfoService
    {
        WeatherInfoResponse HandleRequest(WeatherInfoRequest request);
    }
}

Implementing the WeatherInfo Service

The next step is to create the service application. First we need to implement the new “IWeatherInfoService” interface. So add a project reference to our shared project and add the following implementation:

namespace WeatherInfo.Service
{
    public class WeatherInfoService : IWeatherInfoService
    {
        #region Implementation of IWeatherInfoService

        public WeatherInfoResponse HandleRequest(WeatherInfoRequest request)
        {
            return new WeatherInfoResponse()
                {
                    City = request.City,
                    Temperature = "25° C"
                };
        }

        #endregion
    }
}

This implementation will always return 25° C to keep this example clean and easy. As you can see neither the request/response DTOs nor the service interface and implementation required special attributes or base classes and hence they’re not coupled to Footloose.

And how does Footloose know which implementation of the “IWeatherInfoService” interface it should use on incoming requests? It’ll use the Common Service Locator library. Since our small example do not use an IoC/DI container we have to implement a small dummy adapter that instance the correct implementation of the “IWeatherInfoService” interface and returns it to Footloose.

So add a new class to the service project with the following implementation:

namespace WeatherInfo.Service
{
    public class ServiceLocatorDummy : Microsoft.Practices.ServiceLocation.IServiceLocator
    {
        #region Implementation of IServiceLocator

        public object GetInstance(Type serviceType)
        {
            if (serviceType == typeof(IWeatherInfoService))
                return new WeatherInfoService();

            throw new Exception("ServiceLocator is unable to find implementation of " + serviceType.FullName);
        }

        // other methods of IServiceLocator …

        #endregion
    }
}

Footloose only uses the “public object GetInstance(Type serviceType)” method of the IServiceLocator interface so all other methods just throw a NotImplementedException to keep this example clean.

Now we can open a IConnection that is created during application startup of the service project. This connection will expose the “IWeatherInfoService” interface and is listening to incoming requests.

First lets define where Footloose will find its license file:

private static readonly FileInfo licenseFile = new FileInfo("Footloose.lic");

and create an instance of our “ServiceLocatorDummy” class:

var serviceLocator = new ServiceLocatorDummy();

Now, as all external dependencies are available, we can create and configure the IConnection:

var footlooseConnection = Footloose.Fluently.Configure()
    .SerializerOfType<Footloose.Serialization.TextSerializer>()
    .ServiceLocator(serviceLocator)
    .ServiceContracts(contracts => contracts.ServiceContract.RegisterOfType<IWeatherInfoService>())
    .TransportChannel(
        Footloose.Configuration.Fluent.IpcTransportChannelConfiguration.Standard
            .EndpointIdentifier("footloose-weatherinfoservice")
    )
    .CreateConnection(licenseFile);

We’re using the Fluent way of configuring Footloose.

In the second line we specify the serializer which is used to serialize the request/response DTOs. In this case we’re using the “TextSerializer” which is very fast, has a small payload and perfectly fits for POCO DTOs. In the third line we specify the instance of the common service locator to use to resolve the correct implementation of each service we expose. The next line tells Footloose which service contracts we want to expose. In line 5 we’re configuring the transport channel to use. In this example we use the IPC Transport Channel.

Now lets subscribe to the “ExceptionOccurred” event handler to receive exceptions that occurred in asynchronous code:

footlooseConnection.ExceptionOccurred +=
    (sender, eventArgs) => Console.WriteLine("Exception occurred: {0}", eventArgs.Exception);

Now lets open the connection and listen for incoming requests:

footlooseConnection.Open();

Console.WriteLine("Footloose Connection is now listening on: {0}",
                    footlooseConnection.EndpointIdentityManager.SelfEndpointIdentity.Uri);

Console.WriteLine("Press ENTER to exit...");
Console.ReadLine();

footlooseConnection.Close();
footlooseConnection.Dispose();

Implementing the WeatherInfo Client

The next step is to create the client application. Here we currently do not need the Service Locator because we do not expose a service.

First lets define where Footloose will find its license file:

private static readonly FileInfo licenseFile = new FileInfo("Footloose.lic");</pre>

<p>And then create the IConnection for the client:</p>

<pre class="brush: c-sharp">
var footlooseConnection = Footloose.Fluently.Configure()
    .SerializerOfType<Footloose.Serialization.TextSerializer>()
    .TransportChannel(Footloose.Configuration.Fluent.IpcTransportChannelConfiguration.Standard)
    .CreateConnection(licenseFile);</pre>

<p>In this case we don’t specify a service locator nor we exposing a service contract.</p>

<p>Now lets subscribe to the „ExceptionOccurred“ event handler to receive exceptions that occurred in asynchronous code:</p>

<pre class="brush: c-sharp">
footlooseConnection.ExceptionOccurred +=
    (sender, eventArgs) => Console.WriteLine("Exception occurred: {0}", eventArgs.Exception);

and open the connection:

footlooseConnection.Open();

In this example we don’t use service discovery so we need to generate the uri of the weather service ourselves. Footloose provides an IUriBuilder interface that helps to create uris for each transport channel:

var userName = Environment.UserName;
var mashineName = Environment.MachineName;
var serviceEndpointIdentifier = "footloose-weatherinfoservice";
var serviceUri = footlooseConnection.UriBuilder.BuildEndpointUri(userName, mashineName, serviceEndpointIdentifier);

The endpoint identifier is the same we defined in the service application. So now we have the Uri (address) of the service we can create our request:

var weatherInfoRequest = new WeatherInfoRequest() {City = "Berlin"};

and invoke the service:

footlooseConnection
    .CallMethod<IWeatherInfoService, WeatherInfoResponse>(service => service.HandleRequest(weatherInfoRequest),

    result => 
        Console.WriteLine("Received result: It is {0} in {1}!",
        result.ReturnValue.Temperature, result.ReturnValue.City),

    serviceUri);

This statement will enqueue the call provided by the lambda expression to be send to the remote service specified by the 3 parameter „serviceUri“. We also provided a callback that will handle the response.

Now we can wait for the incoming response:

Console.WriteLine("Waiting for incoming result... Press ENTER to exit...");
Console.ReadLine();

footlooseConnection.Close();
footlooseConnection.Dispose();

Download the example

The full example is available in the Examples Project at: http://github.com/dnauck/FootlooseExamples

Trial License

You can request a Footloose trial license online here.