Click or drag to resize

How To: Call an Informatie Vlaanderen SOAP Service from an ASP.NET MVC application

A request in an MVC application, generally comes down to invoking an Action on a Controller. The action method will perform some work to populate a model which will be passed to a View. During this process services can be called from the action method or from other methods the action method invokes.

Each method which needs to call a service, can by itself create a FederatedChannelFactoryT, create a channel, call the operation and close the channel.

Although FederatedChannelFactoryT and the Be.Vlaanderen.Framework.Identity framework will provide a caching mechanism for the tokens issued by the STS, there is still an overhead associated to the cost of creating a ChannelFactory and opening and closing the proxy.

Ideally we would:

  1. Create the FederatedChannelFactory the first time an action method is invoked.

  2. Create the channel the first time it is needed in the execution of an action method.

  3. Close the channel when the action is done or an exception occured.

The proposed solution in this "How To" document, consists of creating a base Controller class, which properly controls the creation and lifetime of the factories and channels as discussed here above. All Controller classes which need to use the service can then inherit from that base class.

Using FederatedChannelFactory<T> in an MVC Controller.

  1. Configure your application so it can call the secured Service.

    For more information see: How To: Consume an Informatie Vlaanderen SOAP Service

  2. Create a base Controller class.

    1. Right-click the Controller folder in your project.

    2. Choose Add.

    3. Click the Controller menu item.

  3. Add a static readonly FederatedChannelFactory for each client endpoint you need to access

    C#
    ...
    public class BaseController : Controller
    {
        private static readonly FederatedChannelFactory<IClaimsServiceChannel> claimsServiceFactory =
           new FederatedChannelFactory<IClaimsServiceChannel>("WS2007FederationHttpBinding_IClaimsService");
    
        ...
  4. Add a static readonly field for each client service contract you need to access

    C#
    ...
    public class BaseController : Controller
    {
        private static readonly FederatedChannelFactory<IClaimsServiceChannel> claimsServiceFactory =
          new FederatedChannelFactory<IClaimsServiceChannel>("WS2007FederationHttpBinding_IClaimsService");
    
        [ThreadStatic]
        private static IClaimsServiceChannel claimsChannel;
    
    ...

    We decorate the channel field with the ThreadStatic attribute to make sure we will have just one instance of this channel per thread. The channel needs to have affinity with the thread that created it, because the identity is used in the ActAs and OnBehalfOf scenario, to provide the SAML token for the channel.

  5. Add a protected static property for each necessary endpoint.

    C#
    ...
    public class BaseController : Controller
    {
        private static readonly FederatedChannelFactory<IClaimsServiceChannel> claimsServiceFactory =
          new FederatedChannelFactory<IClaimsServiceChannel>("WS2007FederationHttpBinding_IClaimsService");
    
        [ThreadStatic]
        private static IClaimsServiceChannel claimsChannel;
    
        protected static IClaimsServiceChannel ClaimsChannel
        {
            get
            {
                if (claimsChannel == null)
                {
                    claimsChannel = claimsServiceFactory.CreateChannelOnBehalfOfCurrentUser();
                }
                else if (claimsChannel.State != CommunicationState.Opened)
                {
                    claimsChannel.SafeClose();
                    claimsChannel = claimsServiceFactory.CreateChannelOnBehalfOfCurrentUser();
                }
    
                return claimsChannel;
            }
        }
    
    ...
  6. Override the OnException and OnActionExecuted method, to safely close the channels in the case of an exception or when the action has completed.

    C#
    ...
    public class BaseController : Controller
    {
        private static readonly FederatedChannelFactory<IClaimsServiceChannel> claimsServiceFactory =
          new FederatedChannelFactory<IClaimsServiceChannel>("WS2007FederationHttpBinding_IClaimsService");
    
        [ThreadStatic]
        private static IClaimsServiceChannel claimsChannel;
    
        protected static IClaimsServiceChannel ClaimsChannel
        {
            get
            {
                if (claimsChannel == null)
                {
                    claimsChannel = claimsServiceFactory.CreateChannelOnBehalfOfCurrentUser();
                }
                else if (claimsChannel.State != CommunicationState.Opened)
                {
                    claimsChannel.SafeClose();
                    claimsChannel = claimsServiceFactory.CreateChannelOnBehalfOfCurrentUser();
                }
    
                return claimsChannel;
            }
        }
    
        protected override void OnException(ExceptionContext filterContext)
        {
            base.OnException(filterContext);
    
            CloseChannels();
        }
    
        protected override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
    
            CloseChannels();
        }
    
        private static void CloseChannels()
        {
            claimsChannel.SafeClose();
            ...
        }
    
    }

    In the above example, we call a CloseChannels method from the OnException and OnActionExecuted methods. If you only have one channel, you can ofcourse omit the CloseChannels method, and close the channel directly in the overriden methods.

  7. Finally inherit your Controller types from BaseController and use the static property to call your service.

    ...
    public class AdminController : BaseController
    {
        ...
    
        [HttpGet]
        public ActionResult Index()
        {
            ...
    
            var data = ClaimsChannel.GetData(42);
    
            ...
        }
    
      ...
    }
See Also