Skip to main content
added 393 characters in body
Source Link

Please mind: this applies to accessing ANY WCF (.svc) service from the Tridion Event System (including the core service). We have had requirements to access custom services from the Event system and that is why we have a solutions for this. One could, no should ask whether it is necessary to access the CoreService from the EventSystem, as other API's are available on the CMS server.

Please mind: this applies to accessing ANY WCF (.svc) service from the Tridion Event System (including the core service). We have had requirements to access custom services from the Event system and that is why we have a solutions for this. One could, no should ask whether it is necessary to access the CoreService from the EventSystem, as other API's are available on the CMS server.

Source Link

Your eventsystem dll can not find its own app.config ("someAssemblyName.dll.config"). .Net assemblies will look for config files in the original calling assembly location. This is the Tridion CMS core, NOT your event system dll.

How to configure it then?

  1. Put your Webservice info in the machine.config or Tridion.ContentManager.config. --> now the calling assembly has access to the config info, and therefore also the eventsystem assembly.
  2. In your eventSystem dll, explicitly point to a custom config file (with the web service settings), and then use a CustomChanelFactory.

We have successfully implemented option 2 recently. We started by reading this article: http://weblogs.asp.net/pabloretyk/archive/2010/01/17/override-wcf-client-settings-for-custom-config-file-using-duplexchannel.aspx

And then implemented our own CustomChanelFactory class:

using System;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;

namespace ADAMWSConnector
{
    /// <summary>
    /// Custom channel factory to use a non-standard config file
    /// </summary>
    /// <typeparam name="TChannel"></typeparam>
    public class CustomChannelFactory<TChannel> : ChannelFactory<TChannel>
    {
        /// <summary>
        /// Path to the used configuration file
        /// </summary>
        public string ConfigurationPath { get; private set; }

        public CustomChannelFactory(string configurationPath) : base(typeof(TChannel))
        {
            ConfigurationPath = configurationPath;
        }

        internal ServiceEndpoint CreateEndpoint()
        {
            return CreateDescription();
        }

        protected override ServiceEndpoint CreateDescription()
        {
            ServiceEndpoint serviceEndpoint = base.CreateDescription();
            if (ConfigurationPath == null || !File.Exists(ConfigurationPath))
                return base.CreateDescription();

            ExeConfigurationFileMap executionFileMap = new ExeConfigurationFileMap();
            executionFileMap.ExeConfigFilename = ConfigurationPath;
            System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(executionFileMap, ConfigurationUserLevel.None);
            ServiceModelSectionGroup serviceModeGroup = ServiceModelSectionGroup.GetSectionGroup(config);
            ChannelEndpointElement selectedEndpoint = null;

            foreach (ChannelEndpointElement endpoint in serviceModeGroup.Client.Endpoints)
            {
                if (endpoint.Contract == serviceEndpoint.Contract.ConfigurationName)
                {
                    selectedEndpoint = endpoint;
                    break;
                }
            }
            if (selectedEndpoint != null)
            {
                if (serviceEndpoint.Binding == null)
                {
                    serviceEndpoint.Binding = CreateBinding(selectedEndpoint.Binding, serviceModeGroup);
                }

                if (serviceEndpoint.Address == null)
                {
                    serviceEndpoint.Address = new EndpointAddress(selectedEndpoint.Address, GetIdentity(selectedEndpoint.Identity), selectedEndpoint.Headers.Headers);
                }

                if (serviceEndpoint.Behaviors.Count == 0 && !String.IsNullOrEmpty(selectedEndpoint.BehaviorConfiguration))
                {
                    AddBehaviors(selectedEndpoint.BehaviorConfiguration, serviceEndpoint, serviceModeGroup);
                }

                serviceEndpoint.Name = selectedEndpoint.Contract;
            }

            return serviceEndpoint;
        }

        #region private

        /// <summary>        
        /// Configures the binding for the selected endpoint.
        /// </summary>        
        /// <param name="bindingName"></param>
        /// <param name="group"></param>
        /// <returns></returns>
        private Binding CreateBinding(string bindingName, ServiceModelSectionGroup group)
        {
            BindingCollectionElement bindingElementCollection = group.Bindings[bindingName];
            if (bindingElementCollection.ConfiguredBindings.Count > 0)
            {
                IBindingConfigurationElement be = bindingElementCollection.ConfiguredBindings[0];
                Binding binding = GetBinding(be);
                if (be != null)
                {
                    be.ApplyConfiguration(binding);
                }
                return binding;
            }
            return null;
        }

        /// <summary>        
        /// Adds the configured behavior to the selected endpoint        
        /// </summary>        
        /// <param name="behaviorConfiguration"></param>        
        /// <param name="serviceEndpoint"></param>        
        /// <param name="group"></param>        
        private void AddBehaviors(string behaviorConfiguration, ServiceEndpoint serviceEndpoint, ServiceModelSectionGroup group)
        {
            EndpointBehaviorElement behaviorElement = group.Behaviors.EndpointBehaviors[behaviorConfiguration];
            for (int i = 0; i < behaviorElement.Count; i++)
            {
                BehaviorExtensionElement behaviorExtension = behaviorElement[i];
                object extension = behaviorExtension.GetType().InvokeMember("CreateBehavior", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, behaviorExtension, null);

                if (extension != null)
                {
                    serviceEndpoint.Behaviors.Add((IEndpointBehavior)extension);
                }
            }
        }

        /// <summary>        
        /// Gets the endpoint identity from the configuration file        
        /// </summary>        
        /// <param name="element"></param>        
        /// <returns></returns>        
        private EndpointIdentity GetIdentity(IdentityElement element)
        {
            EndpointIdentity identity = null;
            PropertyInformationCollection properties = element.ElementInformation.Properties;
            if (properties["userPrincipalName"].ValueOrigin != PropertyValueOrigin.Default)
            {
                return EndpointIdentity.CreateUpnIdentity(element.UserPrincipalName.Value);
            }

            if (properties["servicePrincipalName"].ValueOrigin != PropertyValueOrigin.Default)
            {
                return EndpointIdentity.CreateSpnIdentity(element.ServicePrincipalName.Value);
            }
            if (properties["dns"].ValueOrigin != PropertyValueOrigin.Default)
            {
                return EndpointIdentity.CreateDnsIdentity(element.Dns.Value);
            }
            if (properties["rsa"].ValueOrigin != PropertyValueOrigin.Default)
            {
                return EndpointIdentity.CreateRsaIdentity(element.Rsa.Value);
            }
            if (properties["certificate"].ValueOrigin != PropertyValueOrigin.Default)
            {
                X509Certificate2Collection supportingCertificates = new X509Certificate2Collection();
                supportingCertificates.Import(Convert.FromBase64String(element.Certificate.EncodedValue));
                if (supportingCertificates.Count == 0)
                {
                    throw new InvalidOperationException("UnableToLoadCertificateIdentity");
                }
                X509Certificate2 primaryCertificate = supportingCertificates[0];
                supportingCertificates.RemoveAt(0);

                return EndpointIdentity.CreateX509CertificateIdentity(primaryCertificate, supportingCertificates);
            }

            return identity;
        }

        /// <summary>        
        /// Helper method to create the right binding depending on the configuration element        
        /// </summary>        
        /// <param name="configurationElement"></param>        
        /// <returns></returns>        
        private Binding GetBinding(IBindingConfigurationElement configurationElement)
        {
            if (configurationElement is CustomBindingElement)
                return new CustomBinding();
            else if (configurationElement is BasicHttpBindingElement)
                return new BasicHttpBinding();
            else if (configurationElement is NetMsmqBindingElement)
                return new NetMsmqBinding();
            else if (configurationElement is NetNamedPipeBindingElement)
                return new NetNamedPipeBinding();
            else if (configurationElement is NetPeerTcpBindingElement)
                return new NetPeerTcpBinding();
            else if (configurationElement is NetTcpBindingElement)
                return new NetTcpBinding();
            else if (configurationElement is WSDualHttpBindingElement)
                return new WSDualHttpBinding();
            else if (configurationElement is WSHttpBindingElement)
                return new WSHttpBinding();
            else if (configurationElement is WSFederationHttpBindingElement)
                return new WSFederationHttpBinding();
            return null;
        }

        #endregion
    }
}

The last step is to connect to the webservice using your custom config file:

    /// <summary>
    /// Uses the configuration to setup the connection.
    /// </summary>
    /// <returns></returns>
    private ConnectorServiceClient GetClient()
    {
        Trace.TraceInformation("GetClient()");

        ConnectorServiceClient client;
        if (String.IsNullOrEmpty(configPath))
        {
            client = new ConnectorServiceClient(bindingName);
        }
        else
        {
            CustomChannelFactory<IConnectorServiceChannel> x = new CustomChannelFactory<IConnectorServiceChannel>(configPath);
            ServiceEndpoint endpoint = x.CreateEndpoint();
            client = new ConnectorServiceClient(endpoint.Binding, endpoint.Address);
        }


        return client;
    }

(we used this to connect to a custom webservice from our EventSystem, this should work for the core service as well. The "configPath" var is the full path to the custom config file)