Showing posts with label Wcf. Show all posts
Showing posts with label Wcf. Show all posts

Tuesday, 22 October 2013

Saturday, 6 July 2013

Calling a WCF service over SSL with a certificate

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="binding_Default" maxReceivedMessageSize="2147483647">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<clientCredentials>
<clientCertificate storeLocation="CurrentUser"
storeName="My"
findValue="28dfc90a0d22763ca41bb937e91925e10f9de7a4"
x509FindType="FindByThumbprint"/>
<serviceCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="https://SERVERNAME/SERVICENAME"
binding="basicHttpBinding"
bindingConfiguration="binding_Default"
contract="NAMESPACE.INTERFACE"
name="MyServiceEndpoint"
behaviorConfiguration="endpointBehavior">
</endpoint>
</client>
</system.serviceModel>
</configuration>

image

Monday, 7 January 2013

Async WCF interface with T4

T4 template that takes a DLL, pulls out all the WCF interfaces and gives your client development team a proxy class to code against.

namespace ServerInterfaces
{
[ServiceContract]
public interface ILoginFacade
{
[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
Method = "GET", UriTemplate = "/Login")]
GetLoginResponse GetLogin()

[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
Method = "PUT", UriTemplate = "/Login")]
UpdateLoginResponse UpdateLogin(UpdateLoginRequest request);

[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
Method = "GET", UriTemplate = "/Login/WriteToLog")]
void WriteToLog();
}

[ServiceContract]
public interface ITradeFacade
{
[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
Method = "PUT", UriTemplate = "/Trades")]
int UpdateTradesDatabase(string userId);
}
}

Generated Client Proxy

   
/***************************************************************************************
**** GENERATED CODE: Use t4toolbox & tangiblet4editor to update AsyncInterfaces.tt ****
****************************************************************************************/
using System;
using Newtonsoft.Json;

namespace WcfServiceDirectory
{
public partial class Services
{
public Services()
{
this.LoginFacade = new LoginFacade();
this.TradeFacade = new TradeFacade();
}

public LoginFacade LoginFacade { get; set; }
public TradeFacade TradeFacade { get; set; }
}

public class LoginFacade
{
private IServiceExecutor serviceExecutor;

public LoginFacade() { this.serviceExecutor = new ServiceExecutor(); }

public void GetLogin(Action<CallCompleteEventArgs<DTOs.GetLoginResponse>> callback)
{
serviceExecutor.Get<DTOs.GetLoginResponse>("/Login", callback);
}

public void UpdateLogin(DTOs.UpdateLoginRequest request, Action<CallCompleteEventArgs<DTOs.UpdateLoginResponse>> callback)
{
serviceExecutor.Put<DTOs.UpdateLoginResponse>("/Login", JsonConvert.SerializeObject(request), callback);
}

public void WriteToLog(Action<CallCompleteEventArgs<Object>> callback)
{
serviceExecutor.Get<Object>("/Login/WriteToLog", callback);
}
}

public class TradeFacade
{
private IServiceExecutor serviceExecutor;

public TradeFacade() { this.serviceExecutor = new ServiceExecutor(); }

public void UpdateTradesDatabase(System.String userId, Action<CallCompleteEventArgs<System.Int32>> callback)
{
serviceExecutor.Put<System.Int32>("/Trades", JsonConvert.SerializeObject(userId), callback);
}
}
}

Core REST service execution code

namespace ClientProxy
{
public interface IServiceExecutor
{
void Get<TResponse>(string uriTemplate, Action<CallCompleteEventArgs<TResponse>> callback);
void Put<TResponse>(string uriTemplate, string request, Action<CallCompleteEventArgs<TResponse>> callback);
}

public class ServiceExecutor : IServiceExecutor
{
ServiceEnvironment serviceEnvironment = new ServiceEnvironment() { UseHttpS = false, BaseAddress = "localhost", Port = 52802 };

public void Get<TResponse>(string uriTemplate, Action<CallCompleteEventArgs<TResponse>> callback)
{
var client = new WebClient();
var address = GetUri(uriTemplate);
client.DownloadStringCompleted += (sender, eventArgs) =>
{
if (callback == null) return;
var response = JsonConvert.DeserializeObject<TResponse>(eventArgs.Result);
callback(new CallCompleteEventArgs<TResponse>(response, eventArgs));
};
client.DownloadStringAsync(address);
}

public void Put<TResponse>(string uriTemplate, string request, Action<CallCompleteEventArgs<TResponse>> callback)
{
var client = new WebClient();
var address = GetUri(uriTemplate);
client.UploadStringCompleted += (sender, eventArgs) =>
{
if (callback == null) return;
var response = JsonConvert.DeserializeObject<TResponse>(eventArgs.Result);
callback(new CallCompleteEventArgs<TResponse>(response, eventArgs));
};
client.Headers[HttpRequestHeader.ContentType] = "application/json";
client.UploadStringAsync(address, "PUT", request);
}

private Uri GetUri(string uriTemplate)
{
var uriString = string.Format("http{0}://{1}:{2}/Facades{3}",
serviceEnvironment.UseHttpS ? "s" : "",
serviceEnvironment.BaseAddress,
serviceEnvironment.Port,
uriTemplate);
return new Uri(uriString);
}
}
}

Source code


The solution features:



  • DTOS:  A portable class library project containing DTOs
  • Interfaces:  A class library containing the WCF interfaces.  This project also contains the T4 template scripts
  • WCF ServiceDirectory:  A Silverlight project which calls the rest services , contains REST service calling code.
  • A website project with Web API (WCF) REST Services

https://github.com/stevenh77/AsyncServiceInterfaces

Sunday, 27 May 2012

Silverlight Async Rest Wcf

Here is a simple example application that shows off:

  • “Aync” and “await” within Silverlight 5 in Visual Studio 11
  • Calling a simple RESTful WCF interface hosted with no config or svc file

image

image

image

MainPage.xaml.cs (code behind)

private async void LoadDataButton_Click(object sender, RoutedEventArgs e)
{
var request = new WebClient();
var uri = new Uri("http://localhost:5349/services/cars");
var jsonString = await request.DownloadStringTaskAsync(uri);

using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(jsonString.ToCharArray())))
{
var serializer = new DataContractJsonSerializer(typeof (Car[]));
var cars = (Car[]) serializer.ReadObject(stream);
CarsListBox.ItemsSource = cars;
}
}

Service Interface

[ServiceContract]
interface IService<T>
{
[OperationContract]
IList<T> GetAll();

[OperationContract]
T Get(string id);

[OperationContract]
void Insert(T entity);

[OperationContract]
void Update(string id, T entity);

[OperationContract]
void Delete(string id);
}

Service Implementation

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class CarService : IService<Car>
{
private readonly IRepository<Car> _repo;

public CarService()
{
_repo = new FakeCarRepository();
}

public CarService(IRepository<Car> repo)
{
_repo = repo;
}

[WebGet(UriTemplate = "Cars", ResponseFormat = WebMessageFormat.Json)]
public IList<Car> GetAll()
{
var cars = _repo.GetAll();
return cars;
}

[WebGet(UriTemplate = "Car/{id}", ResponseFormat = WebMessageFormat.Json)]
public Car Get(string id)
{
var car = _repo.GetById(id);
return car;
}

[WebInvoke(UriTemplate = "Car", Method = "POST")]
public void Insert(Car car)
{
_repo.Insert(car);
}

[WebInvoke(UriTemplate = "Car/{id}", Method = "PUT")]
public void Update(string id, Car car)
{
_repo.Update(car);
}

[WebInvoke(UriTemplate = "Car/{id}", Method = "DELETE")]
public void Delete(string id)
{
_repo.Delete(id);
}
}

DTO

[DataContract]
public class Car
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Make { get; set; }
[DataMember]
public string Model { get; set; }
[DataMember]
public int Year { get; set; }
}

How come no config or svc file?


Global.asax

public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("services", new WebServiceHostFactory(), typeof(CarService)));
}
}

Web.config

<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
</configuration>

Source


http://stevenhollidge.com/blog-source-code/SilverlightAsyncRestWcf.zip

Wednesday, 23 May 2012

WCF Bindings

Carrying on from an excellent MSDN blog post by Rick Rainey comparing the performance of various WCF bindings here is an example including NetNamePipes and ProtoBuf-Net:

Test results

Original blog post:  http://blogs.msdn.com/b/rickrain/archive/2012/05/20/which-wcf-binding-is-best.aspx

protobuf-net

Is available via Nuget or its Google project hosting: http://code.google.com/p/protobuf-net/

AddressAccessDeniedException

If you come across the following exception:

HTTP could not register URL http://+:9001/. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details).

Either run Visual Studio using “Run as Administrator” or visit the following link for a more complete resolution:

http://blogs.msdn.com/b/amitlale/archive/2007/01/29/addressaccessdeniedexception-cause-and-solution.aspx

Source

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Diagnostics;
using ProtoBuf;
using ProtoBuf.ServiceModel;

namespace BindingTest
{
[ServiceContract]
public interface ITestService
{
[OperationContract]
void Execute(TestData data);
}

[DataContract]
[ProtoContract]
public class TestData
{
[DataMember]
[ProtoMember(1)]
public List<string> Data { get; set; }
}

public class TestService : ITestService
{
public void Execute(TestData data)
{
// empty service implementation
}
}

class Program
{
static void Main(string[] args)
{
var bindingElements = new BindingElement[] {
new BinaryMessageEncodingBindingElement(),
new ReliableSessionBindingElement(true),
new HttpTransportBindingElement()
};

var bindings = new Binding[]
{
new WSHttpBinding() { MaxReceivedMessageSize = Int32.MaxValue },
new NetTcpBinding
{
Name = "NetTcpBinding (SecurityMode.Message)",
Security = { Mode = SecurityMode.Message },
MaxReceivedMessageSize = Int32.MaxValue
},
new CustomBinding(bindingElements)
{
Name = "CustomBinding (HTTP/Binary/ReliableSession(ordered)/NoSecurity"
},
new NetTcpBinding(),
new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport),
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None)
};

var payloadSize = 15000;
var iterations = 5;

OutputTitle(payloadSize, iterations);

// Compare all the bindings in the bindings array.
CompareBindings(bindings, payloadSize, iterations, false);

// Compare all the bindings using protobuf-net
CompareBindings(bindings, payloadSize, iterations, true);

OutputNote();
}

public static void OutputTitle(int payloadSize, int iterations)
{
Console.WriteLine("Test parameters: {0} byte payload, {1} iterations",
payloadSize.ToString("N0"),
iterations);
Console.WriteLine();
}

private static void OutputNote()
{
Console.WriteLine();
Console.WriteLine("NOTE: NetNamedPipe is only used for same machine inter process communication");
Console.WriteLine();
}

public static string GenerateRandomStringData(int stringSize)
{
var r = new Random();
var sb = new StringBuilder(stringSize);
while (sb.Length < stringSize)
sb.Append((char)(r.Next(0, 256)));
return sb.ToString();
}

public static TestData GenerateTestData(int payloadSize)
{
const int itemDataSize = 4096;

var data = new TestData {Data = new List<string>()};

int numDataItems = payloadSize / itemDataSize;

for (int item = 0; item < numDataItems; item++)
data.Data.Add(GenerateRandomStringData(itemDataSize));

data.Data.Add(GenerateRandomStringData(payloadSize - (numDataItems * itemDataSize)));

return data;
}

public static void CompareBindings(Binding[] bindings, int payloadSize, int iterations, bool useProtoBuf)
{
Console.WriteLine("Using {0} serialisation", useProtoBuf ? "protobuf" : "normal");
var data = GenerateTestData(payloadSize);

foreach (var b in bindings)
{
try
{
// Run the test on this binding.
Stopwatch result = TestBinding(b, data, iterations, useProtoBuf);

// Output the results.
Console.Write(b.Name);
//Console.WriteLine(string.Format("Payload Size: {0}, Iterations: {1}", payloadSize, iterations));
//Console.WriteLine(" Binding Elements:");
//foreach (BindingElement be in b.CreateBindingElements())
// Console.WriteLine(" {0}", be.GetType());
Console.WriteLine(", time: {0} ms", result.ElapsedMilliseconds);
}
catch (Exception e)
{
Console.WriteLine("EXCEPTION: {0}", e.Message);
}
}
Console.WriteLine();
}

public static Stopwatch TestBinding(Binding binding, TestData data, int iterations, bool useProtoBuf)
{
var address = GetAddress(binding);

// Start the host using the binding provided
var host = new ServiceHost(typeof(TestService), new Uri(address));
var endpoint = host.AddServiceEndpoint(typeof(ITestService), binding, "");

if (useProtoBuf)
endpoint.Behaviors.Add(new ProtoEndpointBehavior());

host.Open();

var sw = new Stopwatch();
sw.Restart();

// Create a client proxy using the binding provided
var cf = new ChannelFactory<ITestService>(binding, address);
ITestService proxy = cf.CreateChannel();

// Call the service
for (int count = 0; count < iterations; count++)
proxy.Execute(data);

// Close the client proxy and host.
cf.Close();
sw.Stop();
host.Close();

return sw;
}

private static string GetAddress(Binding binding)
{
var machine = "localhost";
var port = (binding.Scheme == "net.pipe")
? ""
: ":9001";

return string.Concat(binding.Scheme, "://", machine, port, "/", Guid.NewGuid().ToString());
}
}
}

Download:  http://stevenhollidge.com/blog-source-code/BindingTestSolution.zip

Friday, 30 September 2011

WcfWebApi REST JSON Services

Overview

Here is an example of using WcfWebApi to produce a RESTful JSON data service.  We have three areas to consider:

  • Our data (model) is a simple Contact class
  • Our service is exposed via IContractService interface and implemented as ContractService
  • Our data store (repository) is exposed via IContactRepository interface and implemented as ContractRepository

diagram

RESTful Interface

Data Operation HTTP Verb Has Side Effects? Idempotent? Example Uri
Read All GET No Yes http://localhost:8000/contacts
Read Single GET No Yes http://localhost:8000/contacts/1
Insert POST Yes No http://localhost:8000/contacts
Update PUT Yes Yes http://localhost:8000/contacts
Delete DELETE Yes Yes http://localhost:8000/contacts/3

HTTP Status (Return) Codes

Http Code Description Examples
200 OK Successful (read, update or delete)
201 CREATED Successful (insert)
400 BAD REQUEST Passing id as “X” when an integer is expected
404 NOT FOUND Item not found (based on id)
500 SERVER ERROR Exception thrown within service on server with the message contained within response body.

Service Interface

[ServiceContract]
public interface IContactService
{
[OperationContract]
[WebGet(UriTemplate = "")]
HttpResponseMessage GetAll();

[OperationContract]
[WebGet(UriTemplate = "{id}")]
HttpResponseMessage Get(string id);

[OperationContract]
[WebInvoke(UriTemplate = "", Method = "POST")]
HttpResponseMessage Insert(Contact contact);

[OperationContract]
[WebInvoke(UriTemplate = "", Method = "PUT")]
HttpResponseMessage Update(Contact contact);

[OperationContract]
[WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
HttpResponseMessage Delete(string id);
}

Service Implementation

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using Microsoft.ApplicationServer.Http.Dispatcher;
using RestDemo.Data;
using RestDemo.Service.Helpers;
using RestDemo.Service.Repositories;

namespace RestDemo.Service
{
public class ContactService : IContactService
{
private const string ID_MUST_BE_INTEGER = "Id must be an integer";
private const string ITEM_NOT_FOUND = "Item not found";
private readonly IContactRepository _repo;

public ContactService() : this(new ContactRepository())
{
}

public ContactService(IContactRepository repository)
{
_repo = repository;
}

#region IContactService Members

public HttpResponseMessage GetAll()
{
IEnumerable<Contact> contacts;
DataOperationResponse dataResponse = _repo.GetAll(out contacts);
HttpContent httpContent = GetHttpContent(contacts);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse, httpContent);
return httpResponse;
}

public HttpResponseMessage Get(string id)
{
int intId;
if (!Int32.TryParse(id, out intId))
return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(ID_MUST_BE_INTEGER) };

Contact contact;
DataOperationResponse dataResponse = _repo.Get(intId, out contact);
HttpContent httpContent = GetHttpContent(contact);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse, httpContent);
return httpResponse;
}

public HttpResponseMessage Insert(Contact contact)
{
DataOperationResponse dataResponse = _repo.Insert(contact);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse);
return httpResponse;
}

public HttpResponseMessage Update(Contact contact)
{
DataOperationResponse dataResponse = _repo.Update(contact);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse);
return httpResponse;
}

public HttpResponseMessage Delete(string id)
{
int intId;
if (!Int32.TryParse(id, out intId))
return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(ID_MUST_BE_INTEGER) };

DataOperationResponse dataResponse = _repo.Delete(intId);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse);
return httpResponse;
}

#endregion

private static HttpContent GetHttpContent(int id)
{
HttpContent httpContent = new StringContent(Convertors.ToJson(id).ToString(), Encoding.UTF8, MediaTypes.JSON);
return httpContent;
}

private static HttpContent GetHttpContent(Contact contact)
{
HttpContent httpContent = contact
!= null
? new StringContent(Convertors.ToJson(contact).ToString(), Encoding.UTF8,
MediaTypes.JSON)
: null;
return httpContent;
}

private static HttpContent GetHttpContent(IEnumerable<Contact> contact)
{
HttpContent httpContent = contact
!= null
? new StringContent(Convertors.ToJson(contact).ToString(), Encoding.UTF8,
MediaTypes.JSON)
: null;
return httpContent;
}

private static HttpResponseMessage SetupHttpResponse(DataOperationResponse dataResponse, HttpContent content = null)
{
var response = new HttpResponseMessage();

switch (dataResponse.Status)
{
case DataOperationStatus.Success:

if (dataResponse.Type == OperationType.Insert)
{
response.StatusCode = HttpStatusCode.Created;
response.Content = GetHttpContent(dataResponse.InsertedId);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(30));
}
else
{
response.StatusCode = HttpStatusCode.OK;

if (content != null)
response.Content = content;
}

break;

case DataOperationStatus.NotFound:

// REST best practise on delete is to return OK if resource
// cannot be found but here we are explicitly returning Not Found
response.StatusCode = HttpStatusCode.NotFound;
response.Content = new StringContent(ITEM_NOT_FOUND);
break;

case DataOperationStatus.Exception:
response.StatusCode = HttpStatusCode.InternalServerError;
response.Content = new StringContent(dataResponse.Exception.Message);
break;
}

return response;
}

/* You might also want set the ocation header:
* response.Headers.Location = new Uri("Contacts/" + item.Id, UriKind.Relative);
*/
}
}

Service Unit Tests


Service-Unit-Tests


Hosting The Service in an ASP.NET Web Application

public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
var webApiConfig = new WebApiConfiguration {EnableTestClient = true};
RouteTable.Routes.SetDefaultHttpConfiguration(webApiConfig);
RouteTable.Routes.MapServiceRoute<ContactService>("Contacts");
}
}

Alternatively…. Console Hosting


The source code also contains an example of hosting within a client application:

private const string CONTACTS_URI = "http://localhost:8010/contacts";

private static void Main(string[] args)
{
using (var host = new HttpServiceHost(typeof (ContactService), CONTACTS_URI))
{
host.Open();

ExitOnReadKey();
}

private static void ExitOnReadKey()
{
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}

Client Access to the REST service interface from a Console App

private static void HttpGetAll()
{
var client = new HttpClient();
HttpResponseMessage response = client.Get(CONTACTS_URI);
Console.WriteLine("GET: {0} RESPONSE: {1} \n{2}\n", CONTACTS_URI, response.StatusCode, response.Content.ReadAsString());
}

private static void HttpPost()
{
var client = new HttpClient();
HttpContent jsonContactNew = new StringContent("{\"Id\":0,\"Name\":\"Edward\"}", Encoding.UTF8, MediaTypes.JSON);
HttpResponseMessage response = client.Post(CONTACTS_URI, jsonContactNew);
Console.WriteLine("POST (INSERT CONTACT): {0} RESPONSE: {1} \n{2}\n", CONTACTS_URI, response.StatusCode, response.Content.ReadAsString());
}

private static void HttpPut()
{
var client = new HttpClient();
HttpContent jsonContactUpdated = new StringContent("{\"Id\":2,\"Name\":\"Ziggy\"}", Encoding.UTF8, MediaTypes.JSON);
HttpResponseMessage response = client.Put(CONTACTS_URI, jsonContactUpdated);
Console.WriteLine("PUT (UPDATE CONTACT ID 2): {0} RESPONSE: {1}\n", CONTACTS_URI, response.StatusCode);
}

private static void HttpDelete()
{
var client = new HttpClient();
const string uri = CONTACTS_URI + "/3";
HttpResponseMessage response = client.Delete(uri);
Console.WriteLine("DELETE (CONTACT ID 3): {0} RESPONSE: {1} \n", uri, response.StatusCode);
}

private static void HttpGet()
{
var client = new HttpClient();
const string uri = CONTACTS_URI + "/1";
HttpResponseMessage response = client.Get(uri);
Console.WriteLine("GET: {0} RESPONSE: {1} \n{2}\n", uri, response.StatusCode, response.Content.ReadAsString());
}

// Some methods worth knowing:
// 1. When expecting JSON in response body you can set the following header in the request:
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypes.JSON));
//
// 2. When deserializing JSON single item within response body use the following:
// var contact = response.Content.ReadAs<Contact>();
//
// 3. When deserializing JSON collection within response body use the following:
// var contacts = response.Content.ReadAs<Contact[]>();

Output


ScreenShot163


Monitoring


HTTP


You can use Fiddler to monitor all HTTP requests and responses.  The screenshot below shows Fiddler on the left hand side picking up all requests and responses from the WCF Web API Test Client (explained within Debugging below) on the right hand side.


Fiddler-Monitoring


Debugging


Configure the Firewall (if required)


Make sure you punch a hole in your firewall before testing (or change the port from 8010 to 80 in the source code). 


On Windows 7 > Control Panel > Windows Firewall > Allow a Program or Feature through the Firewall


ScreenShot152


Fiddler


We are now ready to open Fiddler and use the Request Builder tab to enter your URI, set the HTTP verb (defaults to GET) and execute your request.  On the left hand side you’ll see the HTTP response and headers, in this case the result was 200 (OK):


Fiddler-GET-example1


Then switch to the Inspectors tab to view the response body containing our data.


Fiddler-GET-example2


For PUT and POST requests you must add the following to the request headers:


Content-Type: application/json; charset=utf-8


Fiddler-POST-exampleFiddler-PUT-example


WCF Web API Test Client


We switched this new Microsoft Tool on in the Global.asax page with “EnableTestClient = true” which automatically routes:



In our case this would be http://localhost:8010/Contacts/Test:


ScreenShot153ScreenShot156ScreenShot157ScreenShot158ScreenShot159


 


Full source code:  http://stevenhollidge.com/blog-source-code/RestDemo.zip

Friday, 23 September 2011

RabbitMQ

RabbitMQ

Here’s a simple example of how to use RabbitMQ to provide a simple pub/sub messaging architecture (aka observer/listener pattern).

Pub/Sub example

Once the RabbitMQ server is installed we are going to create a C# client to:

  • Create a queue
  • Send 10 messages to the queue
  • Read the messages back using two different techniques (5 messages each)

The code also shows how to inspect for the last message retrieved from a subscription.

The Output

ScreenShot134

Code

 

RabbitMQ Server Installation

For the server you’ll need to first install Erlang and then run the RabbitMQ installer:

http://www.erlang.org/download.html  (Windows Binary File)

http://www.rabbitmq.com/server.html  (Installer for Windows systems)

RabbitMQ Client Libraries

For the C# client libraries:

http://www.rabbitmq.com/dotnet.html

Why would you use a queue?

Queues allow messages containing work items or events to be stored in a durable manner. This enables disconnected middleware processes to be able to read from the queue, allowing for scalable processing via multiple processes reading from the queue. By contrast, sockets and other network protocols assume that direct connections always exist.

Different message types can be used on the same queue to provide a workflow scenario. Different processes can monitor the same queue for specific message types and post back to the same queue with a message type for the next stage in the workflow, which in turn gets picked up by another process and so it goes on.

For example, we might have a Web Server dealing with HTTP traffic and 2 worker processes all reading from and writing to the same queue.

  • A web server receives a request from a user to for an order and places an OrderReceived message on queue
  • Worker 1, which had subscribed to to this message type reads the message and “confirms the order” on the back end systems.  Once completed it writes OrderConfirmed message type back to the queue, which is picked up by worker 2
  • Worker 2, which had subscribed to the OrderConfirmed message type, completes its processing and writes an OrderDespatched message type back to the queue.

In reality, “Worker X” might be a bank of multiple computers and processes to allow for a high throughput of messages to be actioned.

A nice webcast can be found here by Clemens Vasters:

http://blogs.msdn.com/b/clemensv/archive/2011/03/18/why-would-anyone-ever-use-a-message-queue.aspx

Source Code

You can download the source code for this example:

http://stevenhollidge.com/blog-source-code/RabbitMqDemo.zip

Wednesday, 21 September 2011

Generating Silverlight WCF Service Proxy

As a general rule it’s always best to stay away from the the Visual Studio Add Reference service proxy/client generator.

The preferred route is to use the utility tools provided, here’s an example of using the SlSvcUtil.exe tool:

cd "C:\Program Files (x86)\Microsoft SDKs\Silverlight\v5.0\Tools"
SlSvcUtil http://localhost:1558/Services/StocksService.svc /edb /ct:System.Collections.ObjectModel.ObservableCollection`1 /r:"C:\Program Files (x86)\Microsoft Silverlight\5.0.61118.0\System.Windows.dll"

ScreenShot131


Some points of interest:



  • You’ll only need to import the two generated files into your Silverlight client project.  When importing into my project I renamed the StocksService.cs to StocksServiceClient.cs
  • The command prompt doesn’t have to be the Visual Studio Command Prompt but does require elevated admin rights
  • /edb flag enables data binding (implements INotifyPropertyChanged)
  • /ct flag sets the collection type

For more info you can hit the MSDN docs:


http://msdn.microsoft.com/en-us/library/cc197958(v=vs.95).aspx

Wednesday, 14 September 2011

Simple Serialization To Console

Here's a few examples of a basic data item being serialized using different techniques:

Xml, Mtom, Binary, BinarySession & Json.

ScreenShot

 

Source code: http://stevenhollidge.com/blog-source-code/SerializationExamples.zip

Tuesday, 16 August 2011

Silverlight Application Framework

This custom framework contains the following functionality:

Client

  • Ribbon menu bar (from DivElements)
  • Navigation frames for the main container and content pages
  • MVVM architecture
  • MVVM Light Locator to make ViewModels available to Views
  • MVVM Light Messenger for content navigation, status updates and logout
  • MVVM Light RelayEvent for Commanding in View Models
  • MVVM Light EventToCommand for key press binding in ViewModels
  • Custom client-side session data management
  • Mockable dialog box service
  • Custom Silverlight “loader”

Server

  • WCF Services (not WCF RIA Services)
  • Custom ASP.NET Authentication and login UI
  • ASP.NET Authorisation to allow for both secure and unsecure services
  • Text to Path web service to generate Xaml Path data from any string & font

 

Silverlight Application Framework demo from Steven Hollidge on Vimeo.

Navigation with the Ribbon and MVVM Light Messenger

The Ribbon button has a tag property that has the desired Uri set as its content.

When the button is clicked, a message is sent that contains the desired Uri.

The messenger listener receives the message and calls the ContentFrame.NavigateTo method.

Click here to download the source code:

http://stevenhollidge.com/blog-source-code/SilverlightExampleApp.zip

PS    Prefer a Treeview Menu to the Ribbon?

If you prefer the style of a Tree View menu, which is free compared to the Ribbon which is licensed, you can check out my blog posting here.

http://stevenhollidge.blogspot.com/2011/06/treeview-menu-for-silverlight.html

[ScreenShot008%255B5%255D.png]

Wednesday, 8 June 2011

Silverlight High Frequency Services

I’ve written a quick Silverlight application that listens to a WCF Service streaming a fire hose of the latest Fx prices at a rate of 1,200 per minute.

The application showcases the MVVM pattern, makes use of MVVMLight Toolkit and features the cosmopolitan/metro theme.

ScreenShot072_thumb[5]

The Service

The WCF service exposes a pub/sub model with a call back for each new FX Rate price:

using System;
using System.ServiceModel;
using System.Threading;
using System.Threading.Tasks;

namespace PricingServiceHost
{
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class PricingService : IPricingService
{
static PricingService()
{
Task.Factory.StartNew(() =>
{
var factory = new PriceFactory();
while (true)
{
Thread.Sleep(50);
if (PriceUpdate == null) continue;
FxRate latestPrice = factory.GetNextPrice();

PriceUpdate(
null,
new PriceUpdateEventArgs
{
LatestPrice = latestPrice
});
}
});
}

static event EventHandler<PriceUpdateEventArgs> PriceUpdate;

IPricingServiceCallback _callback;

public void Subscribe()
{
_callback = OperationContext.Current.GetCallbackChannel<IPricingServiceCallback>();
PriceUpdate += PricingService_PriceUpdate;
}

public void UnSubscribe()
{
PriceUpdate -= PricingService_PriceUpdate;
}

void PricingService_PriceUpdate(object sender, PriceUpdateEventArgs e)
{
if (((ICommunicationObject)_callback).State == CommunicationState.Opened)
{
try
{
_callback.PriceUpdate(e.LatestPrice);
}
catch
{
UnSubscribe();
}
}
else
{
UnSubscribe();
}
}
}
}


The Model (M)

using System;
using System.Collections.ObjectModel;
using GalaSoft.MvvmLight;

namespace MetroPricingSample.Models
{
public class DisplayFxRate : ObservableObject
{
public static ObservableCollection<DisplayFxRate> InitialRates = new ObservableCollection<DisplayFxRate>
{
new DisplayFxRate("AUD", (decimal) 0.93272),
new DisplayFxRate("BRL", (decimal) 1.58100),
new DisplayFxRate("CAD", (decimal) 0.97495),
new DisplayFxRate("CHF", (decimal) 0.83603),
new DisplayFxRate("CNY", (decimal) 0.15425),
new DisplayFxRate("EUR", (decimal) 0.68103),
new DisplayFxRate("GBP", (decimal) 0.60819),
new DisplayFxRate("INR", (decimal) 44.6300),
new DisplayFxRate("JPY", (decimal) 80.0032),
new DisplayFxRate("NZD", (decimal) 1.21847),
new DisplayFxRate("RUB", (decimal) 27.7411),
new DisplayFxRate("THB", (decimal) 0.03303),
new DisplayFxRate("ZAR", (decimal) 6.71610)
};

public DisplayFxRate() { }

public DisplayFxRate(string isoCode, decimal rate)
{
IsoCode = isoCode;
PreviousRate = rate;
CurrentRate = rate;
Updated = DateTime.Now;
}

public const string IsoCodePropertyName = "IsoCode";
private string _isoCode = string.Empty;
public string IsoCode
{
get { return _isoCode; }

set
{
if (_isoCode == value) return;
_isoCode = value;
RaisePropertyChanged(IsoCodePropertyName);
}
}

public const string PreviousRatePropertyName = "PreviousRate";
private decimal _previousRate = 0;
public decimal PreviousRate
{
get { return _previousRate; }

set
{
if (_previousRate == value) return;
_previousRate = value;
RaisePropertyChanged(PreviousRatePropertyName);
}
}

public const string CurrentRatePropertyName = "CurrentRate";
private decimal _currentRate = 0;
public decimal CurrentRate
{
get { return _currentRate; }

set
{
if (_currentRate == value) return;

_previousRate = _currentRate;
_currentRate = value;

RaisePropertyChanged(PreviousRatePropertyName);
RaisePropertyChanged(CurrentRatePropertyName);
RaisePropertyChanged(DeltaPropertyName);
RaisePropertyChanged(StatusPropertyName);
}
}


public const string DeltaPropertyName = "Delta";
public decimal Delta
{
get
{
decimal result;

if (PreviousRate == 0 || CurrentRate == 0)
result = 0;
else
result = Math.Round(((CurrentRate / PreviousRate) - 1), 2);

return result;
}
}

public const string StatusPropertyName = "Status";
public Status Status
{
get
{
Status status;
var delta = Delta;

if (delta > 0)
status = Status.Increase;
else if (delta < 0)
status = Status.Decrease;
else
status = Status.NoChange;

return status;
}
}

public const string UpdatedPropertyName = "Updated";
private DateTime _updated = DateTime.MinValue;
public DateTime Updated
{
get { return _updated; }

set
{
if (_updated == value) return;
_updated = value;
RaisePropertyChanged(UpdatedPropertyName);
}
}
}
}


The View (V)

<navigation:Page x:Class="MetroPricingSample.Views.Pricing"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:Converters="clr-namespace:MetroPricingSample.Converters"
d:DesignHeight="300"
d:DesignWidth="640"
DataContext="{Binding PricingViewModel, Source={StaticResource Locator}}"
Style="{StaticResource PageStyle}"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.Resources>
<Converters:BoolToSubscribedTextConverter x:Key="BoolToSubscribedTextConverter" />
<Converters:StatusToIconConverter x:Key="StatusToIconConverter" />
<Converters:IsoCodeToFlagConverter x:Key="IsoCodeToFlagConverter" />
<Converters:DateTimeToTimeConverter x:Key="DateTimeToTimeConverter" />
</Grid.Resources>

<StackPanel Grid.Row="0">
<Button x:Name="btnSubscribe"
Width="200"
Height="30"
HorizontalAlignment="Left"
Content="{Binding Subscribed, Converter={StaticResource BoolToSubscribedTextConverter}}"
Command="{Binding SubscriptionCommand, Mode=TwoWay}" />
<TextBlock x:Name="tbInfo" Height="30" Text="{Binding ErrorText}"/>

</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<TextBlock Text="Currency" Width="85" Margin="10,0,0,0"/>
<TextBlock Text="Previous" Width="100"/>
<TextBlock Text="Current" Width="100" />
<TextBlock Text="Delta" Width="160" />
<TextBlock Text="Updated" />
</StackPanel>
<ListBox x:Name="lbFxRates"
ItemsSource="{Binding Path=Rates}"
Grid.Row="2">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding IsoCode, Converter={StaticResource IsoCodeToFlagConverter}}" Width="20" Margin="10,0,0,0"/>
<TextBlock Text="{Binding IsoCode}" Width="50" Margin="10,0,0,0"/>
<TextBlock Text="{Binding PreviousRate}" Width="100" />
<TextBlock Text="{Binding CurrentRate}" Width="100" />
<TextBlock Text="{Binding Delta}" Width="100" HorizontalAlignment="Right" Margin="0,0,10,0" />
<Image Source="{Binding Status, Converter={StaticResource StatusToIconConverter}}" Width="20" />
<TextBlock Text="{Binding Updated, Converter={StaticResource DateTimeToTimeConverter}}" Width="200" Margin="30,0,0,0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</navigation:Page>


The View Model (VM)



The view model in the client currently uses a generated proxy for the WCF service, we I really don’t like, this will be removed in the next refactoring iteration.

using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using MetroPricingSample.Models;
using MetroPricingSample.ServiceReference1;

namespace MetroPricingSample.ViewModels
{
public class PricingViewModel : ViewModelBase
{
public ObservableCollection<DisplayFxRate> Rates { get; set; }

private const string SubscribedPropertyName = "Subscribed";
private bool _subscribed = false;

public bool Subscribed
{
get { return _subscribed; }

set
{
if (_subscribed == value)
{
return;
}

_subscribed = value;
RaisePropertyChanged(SubscribedPropertyName);
}
}

private const string ErrorTextPropertyName = "ErrorText";
private string _errorText = string.Empty;

public string ErrorText
{
get { return _errorText; }

set
{
if (_errorText == value)
{
return;
}

_errorText = value;
RaisePropertyChanged(ErrorTextPropertyName);
}
}


public ICommand SubscriptionCommand { get; set; }

private bool _subscriptionCommand_CanExecute = true;

private PricingServiceClient _client;

public PricingViewModel()
{
Rates = DisplayFxRate.InitialRates;

if (IsInDesignMode) return;

_client = new PricingServiceClient();
_client.SubscribeCompleted += _client_SubscribeCompleted;
_client.UnSubscribeCompleted += _client_UnSubscribeCompleted;
_client.PriceUpdateReceived += PriceUpdate;
SubscriptionCommand = new RelayCommand(SubscriptionCommand_Execute, () => _subscriptionCommand_CanExecute);
}

void SubscriptionCommand_Execute()
{
if (!Subscribed)
{
_client.SubscribeAsync();
}
else
{
_client.UnSubscribeAsync();
}

_subscriptionCommand_CanExecute = false;
}

void _client_UnSubscribeCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error == null)
{
Subscribed = false;
ErrorText = "";
}
else
{
ErrorText = "Unable to connect to service.";
}

_subscriptionCommand_CanExecute = true;
}

void _client_SubscribeCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error == null)
{
Subscribed = true;
ErrorText = "";
}
else
{
ErrorText = "Unable to connect to service.";
}

_subscriptionCommand_CanExecute = true;
}

public void PriceUpdate(object sender, PriceUpdateReceivedEventArgs e)
{
if (Subscribed)
{
PriceUpdate(e.fxRate);
}
}

public void PriceUpdate(FxRate fxRate)
{
try
{
foreach (var rate in Rates.Where(rate => rate.IsoCode == fxRate.IsoCode))
{
rate.CurrentRate = fxRate.Rate;
rate.Updated = fxRate.Updated;
}
}
catch (Exception e)
{
//log here
}
}
}
}


Conclusion



The application currently uses MVVM Light on the client side and WCF for the server side service.  The GUI is struggling to keep up with the service so I plan to introduce the Telerik Grid Control to see how it deals with the updates.  The functionality currently provided by value converters will be moved out of the view and into the view model and I’ll also refactor the application to use a REST interface with Service Stack (written by Demis Bellot).



You can download the source code here:



https://github.com/stevenh77/MetroPricingSample



When you run the application, be sure to set the Web host and Service to both start up at runtime (right click on solution > Properties > Multiple Startup Projects > START both MetroPricingSample.Web and PricingServiceHost).



Note: As I’m looking purely at performance I’ve deliberately omitted tests and some of the error handling and graceful dereferencing, I’ll add these as I refactor later on.

Monday, 21 March 2011

How to Enable Tracing for WCF Service Messages and Parameters

If you want to view the messages and parameters being sent to and from your Wcf Services you can follow these simple steps.

To demonstrate we will be creating a new “WCF Service Library” project:

add-project

Right click on your App.Config file and select “Edit Wcf Configuration”. 

Select the Diagnostics node (on the left hand side of the form), and then click “Enable Message Logging” (on the right hand side of the form).

Next click the setting for Log Level (set to “Malformed, Transport” by default and select “Service Messages” as shown below:

edit-config 

Finally, expand the Diagnostics node and select the Message Logging node and change the LogEntireMessage setting to True.

edit-config2

Close the Microsoft Service Configuration Editor, saving your changes.

By default the Editor sets the log file to an absolute file path.  You can change this in the App.Config to a relative path:

From:

To:


Press F5 to run your WCF Service Library, double click on the GetData() method within the IService1 contract.  Enter a request value for the operation on the right hand side, we’ll be using 1234 for this example, and click “Invoke”.


run-wcf


Now close your WCF Test Client window.


To view your Service Log file, open Windows Explorer and navigate to your Project output folder.  For this example that directory is C:\Projects\WcfTracingDemo\WcfTracingDemo\Bin\Debug. 


You will now be able to see your new log file:  app_messages.svclog


Double click on the file to open it within the Microsoft Service Trace Viewer application and select the activity line on the left hand side.


This will populate the Message Log Trace lines on the right hand side, to see your request scroll down to the second to last entry and select it.  To view the request scroll down on the “Formatted” tab to view the formatted request:


request


To view the response select the last entry in the Message Log Trace lines and again scroll down within the “Formatted” section to reveal your response.


response


I hope you find this brief tutorial on how to Trace messages in WCF comes in useful – happy coding!


Source code:  WcfTracingDemo.zip

Wednesday, 9 March 2011

How to create JSON WCF RESTful Service in 60 seconds

WCF makes it very easy to expose JSON data over a RESTful interface, as long as you are aware of a couple of “gotchas” in advance.

This article will explain those to you, so you can focus on your business logic rather than configuration of your WCF services.

We start this example by creating a WCF Service Library project:

new-project

Next we need to add a reference to the System.ServiceModel.Web framework.  Right click on your project file and select Add Reference…

add-reference

As this framework is not part of the .Net Framework 4 Client Profile, Visual Studio kindly informs us that it will update our target Framework to the full version of .Net Framework 4.  Click Yes to accept this change:

target-change

We are now ready to update our code.

Copy and paste the following code into the App.Config file:

<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service name="WcfJsonRestService.Service1">
<endpoint address="http://localhost:8732/service1"
binding="webHttpBinding"
contract="WcfJsonRestService.IService1"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior>
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>


Notice the binding is set to webHttpBinding as opposed to the normal project template default of wsHttpBinding. 


The other important change is the addition of an endpointBehavior for WebHttp.


These two changes are required to enable JSON over REST for WCF.


Copy and paste the following code into the IService1 file:

using System.ServiceModel;

namespace WcfJsonRestService
{
[ServiceContract]
public interface IService1
{
[OperationContract]
Person GetData(string id);
}
}


Notice we are accepting an “In” parameter for id of datatype string.  For this example we are returning a custom type of Person.




Copy and paste the following code into the Service1.cs file:

using System;
using System.ServiceModel.Web;

namespace WcfJsonRestService
{
public class Service1 : IService1
{
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "data/{id}")]
public Person GetData(string id)
{
// lookup person with the requested id
return new Person()
{
Id = Convert.ToInt32(id),
Name = "Leo Messi"
};
}
}

public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
}


The key elements here are the attributes applied to the method.  We are enabling the method to be called over HTTP GET, returning the data in Json format and setting the Uri template to ensure we are using a RESTful interface.


To test your brand new service we will pass in the id value of 10 simply by opening your favourite browser and pasting in the following URL: 


http://localhost:8732/Service1/data/10


json-browser


Source code:  WcfJsonRestService.zip


Example With Multiple Parameters

using System;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace WcfJsonRestService
{
[ServiceContract]
public interface IService1
{
[OperationContract]
Person GetData(string id);

[OperationContract]
Person GetDataWithTwoParams(string id, string name);
}

public class Service1 : IService1
{
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "data/{id}")]
public Person GetData(string id)
{
// lookup person with the requested id
return new Person()
{
Id = Convert.ToInt32(id),
Name = "Leo Messi"
};
}

[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "data/{id}/{name}")]
public Person GetDataWithTwoParams(string id, string name)
{
// create person with the requested id and name
return new Person()
{
Id = Convert.ToInt32(id),
Name = name
};
}
}

public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
}

WCF Services with Zero Config (on both the Host and Client)

When dealing with your WCF services have you ever gotten so confused with all the settings in your App.Config that you just wanted to throw the file away? Well now you can, and I’ll show you how.

With .Net 4.0 WCF allows developers to take the convention over configuration approach to their services. This means that by omitting configuration settings, the .Net framework will create defaults in their place and make your services fully available and operational.

So lets check out how it’s done!

First, create a new WCF Service Library project and call it WcfZeroConfig:

new-project

Now, and this is good bit, delete the App.Config file. Just right click on the file, select Delete – feels good right?!

The WCF Service Library project template makes two methods available for us by default and for this artictle we’ll be using the GetData(int value) method for our example.

As our WCF library is now complete, we are going to add another project to our solution to host the service. In production you would usually use IIS or a Windows Service to host your WCF Service Library but for this simple example we are going to use a Console application.

From the Visual Studio main menu select File > Add > New Project > Windows > Console Application and name the project WcfHost:

Console-project

As this project will be acting as the host we’ll need to add references to both the System.ServiceModel assembly and our WCF Service Library project. Right click on the WcfHost Project file, select Add Reference and select System.ServiceModel framework:

servicemodel-ref

Now select Projects (in the top left hand corner) and double click the WcfZeroConfig and press the close button.

Add-reference

To complete the host, copy and paste the following code into the Program.cs file:


As you can see it takes one line of code to create the ServiceHost object, passing in an Endpoint address and a single method call to open the host channel to make the service available.


On startup of the host, under the covers WCF (in .Net 4.0) will detect that no Binding or Contract has been supplied, either in our App.Config file or from our code, and will provide default values. From the Endpoint address the framework can see we are using Http and will provide the default binding of basicHttpBinding. The contract type of IService1 will also be inferred from the Service1 concrete class.


To run the host, select the WcfHost project and press F5 to build and run the application.


wcfhost


We are now ready to create the client application to consume our WCF Service. From the Visual Studio main menu select File > Add > New Project > Windows > Console Application and name the project WcfClient:


wcfclient


As before you’ll need to add the System.ServiceModel framework (right click on the project file > Add Reference…) along with the WcfZeroConfig project, this is so that the WcfClient project is aware of the WCF Service contract type “IService1”.


To complete our client, just paste in the following lines of code into the Program.cs file:


To complete our example:


1) You’ll first need to build the solution by right clicking on the solution file and selecting Build Solution


2) Run the C:\Projects\WcfZeroConfig\WcfHost\bin\Debug\WcfHost.exe file


3) And finally, run the C:\Projects\WcfZeroConfig\WcfClient\bin\Debug\WcfClient.exe file and enter a number


That number then gets passed to our WCF Service and our client receives the result.


WcfClient-result


The full source code is available here: WcfZeroConfig.zip