Exception on attempt to call WCF methods after enabling/disabling of the any module.

Topics: Core, Writing modules
Jan 20, 2015 at 11:07 AM
After enabling/disabling of any module in the Orchard CMS on attempt to call WCF method exception appears like below:

Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.

at Autofac.Core.Lifetime.LifetimeScope.CheckNotDisposed() in c:\Projects\OSS\autofac\Core\Source\Autofac\Core\Lifetime\LifetimeScope.cs:line 328
at Autofac.Core.Lifetime.LifetimeScope.BeginLifetimeScope(Object tag) in c:\Projects\OSS\autofac\Core\Source\Autofac\Core\Lifetime\LifetimeScope.cs:line 121
at Orchard.Environment.WorkContextAccessor.CreateWorkContextScope(HttpContextBase httpContext)
at Orchard.Environment.WorkContextAccessor.CreateWorkContextScope()
at Orchard.Wcf.OrchardInstanceContext..ctor(IWorkContextAccessor workContextAccessor)
at Orchard.Wcf.OrchardInstanceProvider.GetInstance(InstanceContext instanceContext, Message message)
at System.ServiceModel.InstanceContext.GetServiceInstance(Message message)
at System.ServiceModel.Dispatcher.InstanceBehavior.EnsureServiceInstance(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)



I've got it on Orchard 1.7.3 and 1.8.1

Is it some kind of known issue? Is there some fix for it?

TIA.
Feb 20, 2015 at 1:36 PM
Could please somebody help with it?
Developer
Feb 20, 2015 at 1:44 PM
Hard to say. Might you be injecting some per-request scoped instances into some singleton instances?
How does your class look like that invokes the WCF method (including the constructor of the class)?
Feb 20, 2015 at 3:28 PM
So, below code of mine

Simple contract and stub realization
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Text;

namespace TestWcfService
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
    [System.ServiceModel.ServiceContractAttribute(Namespace = "http://TestWcfService.com/", ConfigurationName = "IService1")]
    public interface IService1 : IDependency 
    {
        [OperationContract]
        void DoWork();
    }

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class TestWcfService : IService1
    {
        public void DoWork()
        {
            
        }
    }

}
Routes look like:
using Orchard.Mvc.Routes;
using Orchard.Wcf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Activation;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace TestWcfService
{
    public class Routes : IRouteProvider
    {

        private static Route TestService =
            new ServiceRoute("TestWcfService", new OrchardServiceHostFactory(), typeof(IService1));

        public void GetRoutes(ICollection<RouteDescriptor> routes)
        {
            foreach (var routeDescriptor in GetRoutes())
                routes.Add(routeDescriptor);
        }

        public IEnumerable<RouteDescriptor> GetRoutes()
        {
            return new[] {
                     new RouteDescriptor {   Priority = 25,
                                             Route = TestService

                     }
                 };
        }
    }
}
and in the main web.config it has part like
<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>    
  </system.serviceModel>
So, I call DoWork method by mean of SoapUi first time and everything is Ok. After that I enable/disable some feature (Code Generation for example) and after that on attempt to call DoWork from SoapUi I get exception described above. At the same time TestWcfService?wsdl returns WSDL correctly.

Thanks.
Developer
Feb 21, 2015 at 9:50 AM
Interesting, I didn't even know about the OrchardServiceHostFactory. If know one else replies to this thread I'd open an issue for it with repro steps.
Feb 23, 2015 at 10:00 AM
I have this troubles with WCF too. Seem we are not along with this problem, I think problem somewhere in the static field which stores TestWcfService Route, but without static field we have another problem: http://orchard.codeplex.com/discussions/256924
Please create an issue, because it is kind of problem everytime restart app pool after enabling/disabling module on producation environment.
Apr 20, 2015 at 8:14 AM
Our company is developing an enterprice application based on Orchard and it's a stopper for using WCF. Please give an advice where to start searching for a solution?
Developer
Apr 20, 2015 at 11:00 PM
So redbubble is this your full code? Couldn't you upload a full source somewhere so we can test it out?
Apr 21, 2015 at 11:42 AM
Piedone wrote:
So redbubble is this your full code? Couldn't you upload a full source somewhere so we can test it out?
Here is the module with the fix described below... To reproduce issue uncomment code in the Route.cs


So, in the process investigation of the problem, i found that exception appears on attempt to create new work context in the OrchardInstanceContext -> ctor() after enable/disable of the module.

I've found a solution but i'm not sure it is not a workaround..

To resolve this issue i've created new host factory + appropriate infrastructure classes (see code below)
public class OrchardStaticServiceHostFactory: ServiceHostFactory, IShim {
        public OrchardStaticServiceHostFactory()
        {
            OrchardHostContainerRegistry.RegisterShim(this);
        }

        public IOrchardHostContainer HostContainer { get; set; }

        public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses) {
            IComponentRegistration registration;
            if (constructorString == null) {
                throw new ArgumentNullException("constructorString");
            }

            if (constructorString == string.Empty) {
                throw new ArgumentOutOfRangeException("constructorString");
            }

            if (HostContainer == null) {
                throw new InvalidOperationException();
            }

            // Create work context
            IRunningShellTable runningShellTable = HostContainer.Resolve<IRunningShellTable>();
            ShellSettings shellSettings = runningShellTable.Match(baseAddresses.First().Host, baseAddresses.First().LocalPath);

            IOrchardHost orchardHost = HostContainer.Resolve<IOrchardHost>();
            ShellContext shellContext = orchardHost.GetShellContext(shellSettings);
            IWorkContextAccessor workContextAccessor = shellContext.LifetimeScope.Resolve<IWorkContextAccessor>();
            WorkContext workContext = workContextAccessor.GetContext();
            if (workContext == null) {
                using (IWorkContextScope workContextScope = workContextAccessor.CreateWorkContextScope()) {
                    ILifetimeScope lifetimeScope = workContextScope.Resolve<ILifetimeScope>();
                    registration =  GetRegistration(lifetimeScope, constructorString);
                }
            }
            else {
                ILifetimeScope lifetimeScope = workContext.Resolve<ILifetimeScope>();
                registration = GetRegistration(lifetimeScope, constructorString);
            }

            if (registration == null) {
                throw new InvalidOperationException();
            }

            if (!registration.Activator.LimitType.IsClass) {
                throw new InvalidOperationException();
            }

            return CreateServiceHost(shellSettings, shellContext, workContextAccessor, registration, registration.Activator.LimitType, baseAddresses);
        }

        private ServiceHost CreateServiceHost(ShellSettings shellSettings, ShellContext shellContext, IWorkContextAccessor workContextAccessor, IComponentRegistration registration, Type implementationType, Uri[] baseAddresses) {
            ServiceHost host = CreateServiceHost(implementationType, baseAddresses);
            host.Opening += delegate {
                host.Description.Behaviors.Add(new OrchardDependencyInjectionStaticServiceBehavior(baseAddresses.First(), implementationType, registration));
            };

            return host;
        }

        private IComponentRegistration GetRegistration(ILifetimeScope lifetimeScope, string constructorString) {
            IComponentRegistration registration;
            if (!lifetimeScope.ComponentRegistry.TryGetRegistration(new KeyedService(constructorString, typeof(object)), out registration)) {
                Type serviceType = Type.GetType(constructorString, false);
                if (serviceType != null) {
                    lifetimeScope.ComponentRegistry.TryGetRegistration(new TypedService(serviceType), out registration);
                }
            }

            return registration;
        }
    }
public class OrchardDependencyInjectionStaticServiceBehavior: IServiceBehavior {
        private readonly Type _implementationType;
        private readonly IComponentRegistration _componentRegistration;
        private readonly Uri _baseAddress;

        public OrchardDependencyInjectionStaticServiceBehavior(Uri baseAddress, Type implementationType, IComponentRegistration componentRegistration)
        {
            if (implementationType == null) {
                throw new ArgumentNullException("implementationType");
            }

            if (componentRegistration == null) {
                throw new ArgumentNullException("componentRegistration");
            }
            _baseAddress = baseAddress;
            _implementationType = implementationType;
            _componentRegistration = componentRegistration;
        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {
            if (serviceDescription == null) {
                throw new ArgumentNullException("serviceDescription");
            }

            if (serviceHostBase == null) {
                throw new ArgumentNullException("serviceHostBase");
            }

            IEnumerable<string> source = serviceDescription.Endpoints.Where<ServiceEndpoint>(delegate(ServiceEndpoint ep) {
                return ep.Contract.ContractType.IsAssignableFrom(this._implementationType);
            }).Select<ServiceEndpoint, string>(delegate(ServiceEndpoint ep) {
                return ep.Contract.Name;
            });

            OrchardStaticInstanceProvider provider = new OrchardStaticInstanceProvider(_baseAddress, this._componentRegistration);
            foreach (ChannelDispatcherBase base2 in serviceHostBase.ChannelDispatchers) {
                ChannelDispatcher dispatcher = base2 as ChannelDispatcher;
                if (dispatcher != null) {
                    foreach (EndpointDispatcher dispatcher2 in dispatcher.Endpoints) {
                        if (source.Contains<string>(dispatcher2.ContractName)) {
                            dispatcher2.DispatchRuntime.InstanceProvider = provider;
                        }
                    }
                    continue;
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {
        }
    }
public class OrchardStaticInstanceProvider : IInstanceProvider, IShim
    {
        private readonly IComponentRegistration _componentRegistration;
        private readonly Uri _baseAddress;

        public OrchardStaticInstanceProvider(Uri baseAddress, IComponentRegistration componentRegistration)
        {
            _componentRegistration = componentRegistration;
            _baseAddress = baseAddress;
            OrchardHostContainerRegistry.RegisterShim(this);
        }

        public object GetInstance(InstanceContext instanceContext, Message message) {

            IRunningShellTable runningShellTable = HostContainer.Resolve<IRunningShellTable>();
            ShellSettings shellSettings = runningShellTable.Match(_baseAddress.Host, _baseAddress.LocalPath);

            IOrchardHost orchardHost = HostContainer.Resolve<IOrchardHost>();
            ShellContext shellContext = orchardHost.GetShellContext(shellSettings);
            IWorkContextAccessor workContextAccessor = shellContext.LifetimeScope.Resolve<IWorkContextAccessor>();

            OrchardInstanceContext item = new OrchardInstanceContext(workContextAccessor);
            instanceContext.Extensions.Add(item);
            return item.Resolve(_componentRegistration);

        }

        public object GetInstance(InstanceContext instanceContext) {
            return GetInstance(instanceContext, null);
        }

        public void ReleaseInstance(InstanceContext instanceContext, object instance) {
            OrchardInstanceContext context = instanceContext.Extensions.Find<OrchardInstanceContext>();
            if (context != null) {
                context.Dispose();
            }
        }

        public IOrchardHostContainer HostContainer { get; set; }
    }
Thanks.