WCF Durable services are WCF services in which the operations can remember the values of private variables (=the state of the service) inbetween restarts of the serivcehost and/or client. This is new in .NET 3.5 and the concept goes beyond the idea of .NET 3.0 WCF sessions. In WCF sessions the service keeps the content in a session environment which is not persisted by default, this means that the service can only remember the data as long as it is running. When the service is down, the data is lost. Also when the client is restarted the session is not accessible anymore because the client creates a new session by instantiating the proxy.
With durable services all state in the service is persisted to a database store. This database has a predefined structure. The instance of the service is serialized, a new GUID is generated, the serialized XML is inserted into a table with this GUID as key and the client gets the GUID. The client is supposed to send this GUID back to the service when it wants to be talking to the same instance. This guid is the instancId. The instanceID allows the service to recognize the client so it can reload the XML from the database and deserialize this XML to a running instance so the operation can work with the private state again.
With this concept both client and service can be restarted, as long as the client knows the instanceID and can send it back to the service, the service can recreate its state. So the service is realy durable.
In a durable scenario we typically have :
- an operation to start the durable session (PowerOn). This operation creates the record in the database.
- an operation that sends some data that the service has to remember (SendDataToTheService).
- an operation that receives this data back from the service (GetDataFromService).
- an operation that stops the session (PowerOff). This operation deletes the records from the database.
For this a normal WCF ServiceContract is used :
[ServiceContract]
public interface IServiceInterface
{
[OperationContract]
void PowerOn();
[OperationContract]
void SendDataToTheService(int data);
[OperationContract]
int GetDataFromService();
[OperationContract]
void PowerOff();
}
The implementation of the service needs some attributes.
1. The class has to be marked as [Serializable] and as [DurableService]
[Serializable]
[DurableService]
public class ServiceImplementation : ServiceInterfaces.IServiceInterface
2. The implementation of the operations have to be marked as [DurableOperation]. This attribute has two boolean flags.
CanCreateInstance = true : calling this operation results in creating the serialization and inserting it into the database.
CompletesInstance = true : calling this operation results in deleting the persited instance from the database.
int someDataToRemember = default(int);
[DurableOperation(CanCreateInstance = true)]
public void PowerOn()
{
Console.WriteLine("PowerOn");
Console.WriteLine("-" + System.ServiceModel.Dispatcher.DurableOperationContext.InstanceId);
}
[DurableOperation()]
public void SendDataToTheService(int data)
{
Console.WriteLine("Got " + data);
someDataToRemember = data;
}
[DurableOperation()]
public int GetDataFromService()
{
Console.WriteLine("Returning " + someDataToRemember);
return someDataToRemember;
}
[DurableOperation(CompletesInstance = true)]
public void PowerOff()
{
Console.WriteLine("PowerOff");
}
wsHttpContextBinding : To allow durable services and the exchange of the instanceId the wsHttpContextBinding has to used on both service and client.
<services>
<service behaviorConfiguration="ServiceBehavior"
name="ServiceImplementations.ServiceImplementation">
<endpoint address="ContextOverHttp"
binding="wsHttpContextBinding"
contract="ServiceInterfaces.IServiceInterface" />
</service>
</services>
The service needs a behavior which has a persistenceProvidor element.
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior" >
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<windowsAuthentication allowAnonymousLogons="false" includeWindowsGroups="true" />
</serviceCredentials>
<persistenceProvider
type="System.ServiceModel.Persistence.SqlPersistenceProviderFactory, System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
connectionStringName="DurableServiceStore"
persistenceOperationTimeout="00:00:10"
lockTimeout="00:01:00"
serializeAsText="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
This behavior element refers to a connectionstring to the database with the tables.
<connectionStrings>
<add name="DurableServiceStore" connectionString="Data Source=KUCLXPX\SQLEXPRESS;Initial Catalog=DurableServicesPersistenceDB;Integrated Security=True"/>
</connectionStrings>
This database has to have the schema and stored procedure as defined SqlPersistenceProviderSchema.sql and SqlPersistenceProviderLogic.sql found in C:\WINDOWS\Microsoft.NET\Framework\v3.5\SQL\EN. Simply create a SQL Database and run the two scripts on it.
When the operation with the CanCreateInstance flag is true is called a record will be inserted into the table InstanceData;

... and the generated instanceId is send as part of the SOAP header to the client.
If you also want to make the client durable you have to manually add the instanceId to the context. This is done by getting a contextmanager from the InnerChannel of the proxy and asking this contextmanager for a reference to a dictionary of name/value pairs. One of the items in this dictionary has the name instanceID (watch it : case sensitive!!!). Give this item the value of the GUID. You can also obtain this GUID in this way.
System.ServiceModel.Channels.IContextManager contextManager;
contextManager = ((System.ServiceModel.IContextChannel)client.InnerChannel).GetProperty<System.ServiceModel.Channels.IContextManager>();
IDictionary<string, string> context = contextManager.GetContext();
if (!context.ContainsKey("instanceId"))
{
context.Add("instanceId", "<the instanceID goes here>");
contextManager.SetContext(context);
}
Now the calls the other operations receive this instanceId. A signal for WCF to reload the state before executing the call.
Nice to now : You can look into the tables to see the serialized state.

