Agatha–Getting Started–First Application
Introduction
The advent of WCF has made life simpler for web service developers . Passing complex types, quick serialization, write once run with multiple bindings (and hence multiple consumers) etc. – have contributed to its popularity. However, larger WCF projects start suffering from the following symptoms:
The Proxy Dependence
Each time a parameter is modified (on a service request), the service definition needs to change. This requires an update of the proxy on each client consuming the service.
Agatha’s Solution – Standardize the request and response – so there is no change in the single contract that all services expose. This single contract looks like :
public abstract Response Handle(TRequest request);
Services and Operations Bloat
Large number of Services, each with multiple Operation Contracts.
Services start overflowing into other services – and lack boundaries. Each service starts to have too many operations. When adding a new operation, one would be faced with a decision : whether to add it to an existing service or to create a brand new service.
Agatha’s solution – Define custom request types for each operation (e.g. if your operation is to GetEmployeeDetails, you would have a request called GetEmployeeDetailsRequest – and a response GetEmployeeDetailsResponse). Each operation then is simply a Handler for these custom request types. So – GetEmployeeDetailsHandler – would have a single Handle() method to contain your business logic.
This enforces granularity on your service operations – and makes them easier to maintain. You will never have to worry about WHERE to place a new operation. It just gets its own handler, its own request and response types.
No More Initialization, No More XML
A WCF client would need to initialize an instance of each proxy – and then invoke the operations on it. In addition, it would need to ensure that the proxy was up to date (by updating its service reference).
Agatha’s solution – Use an IoC container to discover and initialize all your services (i.e. just a single service the WcfRequestProcessor service).
private static void InitializeAgatha()
{
new ClientConfiguration(typeof(AgathaGettingStartedRequest).Assembly, typeof(Agatha.Castle.Container)).Initialize();
}
Single service, Single endpoint, Common contract
Since all the requests and responses derive from a common agatha Request and Response typesT, all services exchange the same types. Their contract is the same. Only a single endpoint is needed – regardless of how many requests and responses (operations) you may have in your project.
<services>
<service behaviorConfiguration="RequestProcessorBehavior" name="Agatha.ServiceLayer.WCF.WcfRequestProcessor">
<endpoint address="" binding="customBinding" bindingConfiguration="binaryXmlOverHttp" contract="Agatha.Common.WCF.IWcfRequestProcessor" />
</service>
</services>
Getting Started
Having discussed the need for something like Agatha, here is a sample project that shows how to use it (source code towards the bottom).
Step 1 , Getting Started – Defining Request Types and Response Types
A base Request type is provided by Agatha – to which you can add your request fields. For example, suppose your service operation is one that Creates a new Account. You would define a new Request Type called CreateAccountRequest which would be of (a base) type Request (agatha’s base request type).
public class CreateAccountRequest : Request { public AccountDTO accountDTO{ get; set; }
} |
namespace Agatha.Common
{ public abstract class Request { protected Request();
} } |
Step 2, Getting Started – Adding Service Operations
Step 3a – Registering Agatha Services , Client
Normally, in your WCF client, you would ‘Add a Service Reference’ from within your client project to access a known WCF service. If your client is using Agatha, you can use an IoC container to ‘discover’ the services for you – so you never have to ‘Add a Service Reference’. All you need in your client code is:
private static void InitializeAgatha()
{
new ClientConfiguration(typeof(AgathaGettingStartedRequest).Assembly, typeof(Agatha.Castle.Container)).Initialize();
}
Step 3b – Registering Agatha Services , Server (service)
An IoC container can be used to register and initialize the service within the WCF runtime. Notice that you do not have to worry about any XML configuration (contract name, bindings etc.). All you need is to use the provided ServiceLayerConfiguration class – and initialize it when the web app initializes (Global.asax, application_start).
void Application_Start(object sender, EventArgs e)
{
// Agatha initialization can happen here
Bootstrapper.RegisterAgatha();
}
public static void RegisterAgatha()
{
new ServiceLayerConfiguration(Assembly.GetExecutingAssembly(), typeof(AgathaGettingStartedRequest).Assembly, typeof(Agatha.Castle.Container)).Initialize();
}
What are some other things Agatha can do?
Call your services asynchronously
Just as in WCF (or any other async pattern), the actual service itself does not need to be asynchronous. Only the client needs to invoke the service asynchronously from its side. This is accomplished in regular web services using the BeginInvoke and EndInvoke (or BeginServiceOperation and EndServiceOperation).
In Agatha, similar constructs exist to enable calling the same service asynchronously from a client. An AsyncRequestDispatcher implementation contains a ProcessRequest method to help with async client requests.
/// <summary>
/// ProcessRequests is the Asynchronous version of requestDispatcher.Get
/// </summary>
private static void CallTheServiceAsynchronously()
{
var requestDispatcher = IoC.Container.Resolve<IAsyncRequestDispatcher>();
requestDispatcher.Add(new AgathaGettingStartedRequest());
// ProcessRequests is the Asynchronous version of requestDispatcher.Get
requestDispatcher.ProcessRequests(ResponsesReceived, e => Console.WriteLine(e.ToString()));
}
private static void ResponsesReceived(ReceivedResponses receivedResponses)
{
Console.WriteLine(receivedResponses.Get<AgathaGettingStartedResponse>().Message);
}
private static void InitializeAgatha()
{
new ClientConfiguration(typeof(AgathaGettingStartedRequest).Assembly, typeof(Agatha.Castle.Container)).Initialize();
}
Customize your Request object (e.g. Custom Authentication)
What if you wanted only specific users to access certain services? You would need to pass in a userId with each request. This is easily accomplished by defining a custom base request class – that can be used as the parent for all subsequent requests.
/// <summary>
/// A single, centralized request allows passing custom Authentication parameters - e.g. userId.
/// </summary>
class CustomizedAuthRequest : Request
{
public long userId { get; set; }
}
OverRide beforeHandle and AfterHandle for custom processing
Agatha provides a base RequestHandler class ( RequestHandler<TRequest, TResponse>) – with several useful template methods. These include Handle, BeforeHandle and AfterHandle methods to provide custom processing. If you wanted all your requests (containing a userId) to be authenticated (against sql server or ldap etc.), you would just need to do it in the BeforeHandle method. The template pattern used in Agatha, is a life-saver here as well, as in other OO codebases.
public override void BeforeHandle(TRequest request)
{
// e.g. - do custom authentication (LDAP, ActiveDirectory etc.)
base.BeforeHandle(request);
}
public override void AfterHandle(TRequest request)
{
// e.g. - do custom logging etc.
base.AfterHandle(request);
}
Single WCF Performance Counter
Remember that Agatha is a generic request response processing layer – and does not need WCF. However, a WCF processor is provided with Agatha – since it is commonly used with WCF. If you look in your performance counters, under ServiceModelService 4.0.0.0 , you should see the WcfRequestProcessor counter that Agatha registers on your local PC. This can be monitored during your testing effort.
What do I need on the client? Agatha Client Side Requirements
Agatha relies on your defining custom request and response classes for each of your operations. The client needs to know what these types are. You DO need to share the assembly containing the common types between the server and the client. And you DO need common Agatha libraries – in particular Agatha.common and Agatha.castle (for Castle Windsor IoC).
However, note that agatha services CAN consumed by non-Agatha aware clients. It is possible to consume an Agatha service just like any WCF service, as described here.
What about NServiceBus (or service buses in general)?
A service bus does not (typically) do ‘request-response’ well. It relies on a message publishing (and subscribing) – which is not (necessarily) instantaneous and not (necessarily) synchronous. It is, however, fault tolerant (ensuring that messages DO get delivered) – and highly scalable (since instantaneous responses are not mandatory).
For a typical web user, a request-response pattern is more important than a message publishing pattern.
Having said all that, NServiceBus supposedly supports a request-response pattern – in addition to its core message publish subscribe pattern
The sample Hello World project
Project 1 –SharedCommonTypes
The types that are common between the client and the Service Layer
Project 2 – ServiceLayer
The Service Layer itself. This inherits from Agatha’s base RequestHandler class. It overrides the Handle method in the base class. This is all that is REQUIRED in the services layer. Notice that the Service Layer knows nothing of WCF or any other web service technology. All it is – is a pure request – response handling layer.
A static class that provides an initialization method is a good idea – it makes the code on the client much easier.
Project 3 – Agatha Host Site
The blank WCF service that acts as a container. This will allow us to host it within IIS. There is no logic, no operations etc. within this WCF service. It is just a blank service. All service operations are moved to the ServiceLayer – which has a custom Handle method for each operation.
Project 4 – Console App (Client)
Contains the only default config needed to call Agatha services – and methods to call the service synchronously as well as asynchronously.
Running the sample
Just open it up in two different instances of VS – one for running your service layer – and the other for the client.
VS Instance 1 – In the first one, set the WCF service (AgathaHostSite) as the startup project. It should start up on a localhost – browse to localhost: port/Service.svc – and you should see a WcfRequestProcessor Service page (WcfRequestProcessor is the processor inside Agatha for handling WCF requests).
VS Instance 2 – Set the Console App (client) as the startup project. Start it to see your client – and initialize and invoke the service.
VS Instance 1 | VS Instance 2 | Started up WebService |
Summary
For larger WCF projects, a layer such as Agatha becomes more than a convenience. Apart from saving tons of maintenance, it provides cleaner, fine grained operations. These operations utilize a base RequestHandler class that contains a Handle operation. This operation is all you need to override for each of your custom service operations.
With IoC support, you no longer have to worry about initializing services (on the service layer or the client). With a single endpoint, you no longer have to worry about large XML web.configs.
Can you give the source code? the link is not working Thanks!