The sessionful service instance management technique as described so far connects a client (or clients) to a service instance. Yet, the real picture is more complex. Recall from Chapter 1 that each service instance is hosted in a context, as shown in Figure 4-2.
What sessions actually do is correlate the client messages not to the instance but to the context that hosts it. When the session starts, the host creates a new context. When the session ends, the context is terminated. By default, the lifetime of the context is the same as that of the instance it hosts. However, for optimization purposes, WCF provides the service designer with the option of separating the two lifetimes and deactivating the instance separately from its context. In fact, WCF also allows the situation of a context that has no instance at all, as shown in Figure 4-2. I call this instance management technique context deactivation.
The common way of controlling context deactivation is via the ReleaseInstanceMode
property of the OperationBehavior
attribute:
public enum ReleaseInstanceMode { None, BeforeCall, AfterCall, BeforeAndAfterCall, } [AttributeUsage(AttributeTargets.Method)] public sealed class OperationBehaviorAttribute : Attribute,... { public ReleaseInstanceMode ReleaseInstanceMode {get;set;} //More members }
ReleaseInstanceMode
is of the enum type ReleaseInstanceMode
. The various values of ReleaseInstanceMode
control when to release the instance in relation to the method call: before, after, before and after, or not at all. When releasing the instance, if the service supports IDisposable
, then the Dispose( )
method is called and Dispose( )
has an operation context.
You typically apply instance deactivation only on some service methods, but not all of them, or with different values on different methods:
[ServiceContract(SessionMode = SessionMode.Required)]
interface IMyContract
{
[OperationContract]
void MyMethod( );
[OperationContract]
void MyOtherMethod( );
}
class MyService : IMyContract,IDisposable
{
[OperationBehavior(ReleaseInstanceMode = ReleaseInstanceMode.AfterCall
)]
public void MyMethod( )
{...}
public void MyOtherMethod( )
{...}
public void Dispose( )
{...}
}
The reason you typically apply it sporadically is that if you were to apply it uniformly you would have ended up with a per-call-like service, so you might as well have configured the service as per-call. If relying on instance deactivation assumes a certain call order, you can try and enforce that order using demarcating operations.
The default value for the ReleaseInstanceMode
property is ReleaseInstanceMode.None
, so these two definitions are equivalent:
[OperationBehavior(ReleaseInstanceMode = ReleaseInstanceMode.None)] public void MyMethod( ) {...} public void MyMethod( ) {...}
ReleaseInstanceMode.None
means that the instance lifetime is not affected by the call, as shown in Figure 4-3.
When a method is configured with ReleaseInstanceMode.BeforeCall
, if there is already an instance in the session, before forwarding the call, WCF will deactivate it, create a new instance in its place, and let that new instance service the call, as shown in Figure 4-4.
WCF deactivates the instance and calls Dispose( )
before the call is done on the incoming call thread, while the client blocks. This makes sure that the deactivation is indeed done before the call and not concurrently to it. ReleaseInstanceMode.BeforeCall
is designed to optimize methods such as Open( )
that acquire some valuable resources and yet wish to release the previously allocated resources. Instead of acquiring the resource when the session starts, you wait until the call to the Open( )
method, and then both release the previously allocated resources and allocate new ones. After Open( )
is called, you are ready to start calling other methods on the instance that are typically configured with ReleaseInstanceMode.None
.
When a method is configured with ReleaseInstanceMode.AfterCall
, WCF deactivates the instance after the call, as shown in Figure 4-5.
This is designed to optimize methods such as Close( )
that clean up valuable resources the instance holds, without waiting for the session to terminate. ReleaseInstanceMode.AfterCall
is typically applied on methods called after methods configured with ReleaseInstanceMode.None
.
When a method is configured with ReleaseInstanceMode.BeforeAndAfterCall
, as its name implies, it has the combined effect of ReleaseInstanceMode.BeforeCall
and ReleaseInstanceMode.AfterCall
. If the context has an instance before the call is made, then just before the call, WCF deactivates that instance, creates a new instance to service the call, and deactivates the new instance after the call, as shown in Figure 4-6.
ReleaseInstanceMode.BeforeAndAfterCall
may look superfluous at first glance, but it actually complements the other values. It is designed to be applied on methods called after methods marked with ReleaseInstanceMode.BeforeCall
or None
, or before methods marked with ReleaseInstanceMode.AfterCall
or None
. Consider a situation where the sessionful service wants to benefit from state-aware behavior (like a per-call service), while holding onto resources only when needed to optimize resource allocation and security lookup. If ReleaseInstanceMode.BeforeCall
was the only available option, then there would be a period of time after the call where the resources would still be allocated to the object but not in use. A similar situation occurs if ReleaseInstanceMode.AfterCall
were the only available option, because there would be a period of time before the call where the resource would be wasted.
Instead of making a design-time decision on which methods to use to deactivate the instance, you can make a runtime decision to deactivate the instance after the method returns. You do that by calling the ReleaseServiceInstance( )
method on the instance context. You obtain the instance context via the InstanceContext
property of the operation context:
public sealed class InstanceContext : CommunicationObject,... { public void ReleaseServiceInstance( ); //More members } public sealed class OperationContext : ... { public InstanceContext InstanceContext {get;} //More members }
Example 4-8 demonstrates this technique.
Example 4-8. Using ReleaseServiceInstance( )
[ServiceContract(SessionMode = SessionMode.Required)]
interface IMyContract
{
[OperationContract]
void MyMethod( );
}
class MyService : IMyContract,IDisposable
{
public void MyMethod( )
{
//Do some work then
OperationContext.Current.InstanceContext.ReleaseServiceInstance( )
;
}
public void Dispose( )
{...}
}
Calling ReleaseServiceInstance( )
has a similar effect to using ReleaseInstanceMode.AfterCall
. When used in a method decorated with ReleaseInstanceMode.BeforeCall
it has a similar effect to using ReleaseInstanceMode.BeforeAndAfterCall
.
Instance deactivation is an optimization technique, and like all such optimization techniques, you should avoid it in the general case. Consider using instance deactivation only after failing to meet both your performance and scalability goals and when careful examination and profiling has proven beyond a doubt that using instance deactivation will improve the situation. If scalability and throughput are your concern, you should take advantage of the simplicity of the per-call instancing mode, and avoid instance deactivation.
Get Programming WCF Services now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.