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 and extensibility 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 a context to
exist without an associated 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
, the
Dispose( )
method is called and Dispose( )
has an operation context.
You typically apply instance deactivation on some but not all service methods, 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 end up with a per-call-like service, in which case you might as well have configured the service as per-call.
If relying on instance deactivation assumes a certain call order, you can try to 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 ensures
that the deactivation is indeed done before the call, not concurrently with it. ReleaseInstanceMode.BeforeCall
is designed to optimize methods
such as Create( )
that acquire some valuable resources,
yet wish to release the previously allocated resources. Instead of acquiring the resources
when the session starts, you wait until the call to the Create(
)
method and then both release the previously allocated resources and allocate
new ones. After Create( )
is called, you are ready to
start calling other methods on the instance, which 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 a method such as Cleanup(
)
that cleans up valuable resources held by the instance, without waiting for
the session to terminate. ReleaseInstanceMode.AfterCall
is typically applied on methods called after methods configured with ReleaseInstanceMode.None
.
As its name implies, configuring a method with ReleaseInstanceMode.BeforeAndAfterCall
has the combined effect of using
ReleaseInstanceMode.BeforeCall
and ReleaseInstanceMode.AfterCall
. If the context has an instance
before the call is made, just before the call WCF deactivates that instance and creates a
new instance to service the call. It then 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
were the only available option, there would be
a period of time after the call when the resources would still be allocated to the object,
but would not be in use. A similar situation would occur if ReleaseInstanceMode.AfterCall
were the only available option, because there
would be a period of time before the call when the resources would be wasted.
Instead of making a design-time decision on which methods to use to deactivate an
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 : ... { 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
.
Tip
Instance deactivation affects a singleton as well, although combining the two makes little sense—by its very definition, it is permissible and even desirable to never deactivate the singleton.
Instance deactivation is an optimization technique, and like all 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 concerns, you should take advantage of the simplicity of the per-call instancing mode and avoid instance deactivation. The main reason I share this technique with you is that WCF itself makes extensive use of instance deactivation; thus, knowledge of it is instrumental in demystifying other aspects of WCF, such as durable services and transactions.
Get Programming WCF Services, 2nd Edition 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.