Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autofac with WCF - Nested lifetimes problems

Tags:

c#

wcf

autofac

I have a number of WCF services setup which are consumed by angularjs. Currently I've run into a problem that I'm struggling with. I keep receiving the following error message when calling the WCF services. Any pointers on how to fix the issue would be appreciated. A more detailed error message can be found at the bottom

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

Edit: It seems to work when I build the container in global.asax Application_BeginReques() also, hence not only Application_Start. No clue why but at least this is better than before.... However I've previously had this error occur every now and then so I don’t trust this ugly work-around

Here is my global.asax

        ..
        ..

    protected void Application_Start(object sender, EventArgs e)
    {
        var containerBuilder = new IoC();

        // Register your service implementations.
        containerBuilder.Builder.RegisterType<Konstrukt.SL.Services.GetBudgetData>().Named<object>("GetBudgetDataService");
        containerBuilder.Builder.RegisterType<Konstrukt.SL.Services.GetReferenceData>().Named<object>("GetReferenceDataService");
        containerBuilder.Builder.RegisterType<Konstrukt.SL.Services.UpdateBudgetData>().Named<object>("UpdateBudgetDataService");
        ..
        ..
        containerBuilder.Builder.RegisterType<Konstrukt.SL.Services.DimValue>().Named<object>("DimValueService");
        containerBuilder.Builder.RegisterType<Konstrukt.SL.Services.DisplayNameMapping>().Named<object>("DisplayNameMappingService");
        ..
        ..

        // use only one context object per scope/request
        containerBuilder.Builder.RegisterType<Konstrukt.DAL.KonstruktEntities>().InstancePerLifetimeScope();

        AutofacHostFactory.Container = containerBuilder.Builder.Build();
    }

Here is my IoC class which registeres all the interfaces

public class IoC
{
    private ContainerBuilder _builder;
    public ContainerBuilder Builder 
    {
        get 
        {
            if (_builder == null)
            {
                _builder = new ContainerBuilder();

                _builder.RegisterType<BL.App.App>().As<BL.App.IApp>();
                _builder.RegisterType<BL.AppConfig.AppConfig>().As<BL.AppConfig.IAppConfig>();
                _builder.RegisterType<BL.AppConfig.ComponentSetting>().As<BL.AppConfig.IComponentSetting>();
                _builder.RegisterType<BL.AppConfig.Component>().As<BL.AppConfig.IComponent>();
                _builder.RegisterType<BL.AppConfig.AppSetting>().As<BL.AppConfig.IAppSetting>();
                _builder.RegisterType<BL.AppConfig.AppDataSet>().As<BL.AppConfig.IAppDataSet>();
                _builder.RegisterType<BL.AppConfig.AttributeData>().As<BL.AppConfig.IAttributeData>();
                _builder.RegisterType<BL.AppConfig.Constants>().As<BL.AppConfig.IConstants>();
                ..
                ..
                _builder.RegisterType<BL.DimensionData.DisplayNameMapping>().As<BL.DimensionData.IDisplayNameMapping>();
                ..
                ..
                //DAL registrations, etc
                ..
                ..

            }
            return _builder;
        }
        set { _builder = value; }
    }

}

Sample WCF service implementation

public class DimValue : IDimValue
{
    private BL.DimensionData.IDimValue _dimValue;
    public DimValue(ILifetimeScope container, BL.DimensionData.IDimValue dimValue)
    {
        AutofacHostFactory.Container = container;
        _dimValue = dimValue;
    }

    public IList<Shared.Poco.DimValue> GetDimValueList(int budgetId, string dimensionFilterJSON, string userId)
    {
        Dictionary<Shared.Poco.Dimension, List<string>> dimensionFilters = null;
        Shared.Poco.User user = new Shared.Poco.User { UserId = userId };
        IList<Shared.Poco.DimValue> list = new List<Shared.Poco.DimValue>();

        using (var scope = AutofacHostFactory.Container.BeginLifetimeScope())
        {
            dimensionFilters = JsonConvert.DeserializeObject<Dictionary<Shared.Poco.Dimension, List<string>>>(dimensionFilterJSON);

            foreach (KeyValuePair<Shared.Poco.Dimension, List<string>> kvp in dimensionFilters)
            {
                foreach (Shared.Poco.DimValue dv in _dimValue.GetDimValueList(budgetId, kvp.Key, kvp.Value, user))
                {
                    list.Add(dv);
                }
            }
        }
        return list;
    }
}

[ServiceContract]
    public interface IDimValue
    {
        [OperationContract]
        [WebInvoke(Method = "GET",
                    RequestFormat = WebMessageFormat.Json,
                    ResponseFormat = WebMessageFormat.Json,
                    BodyStyle = WebMessageBodyStyle.Bare,
                    UriTemplate = "GetDimValueList?budgetId={budgetId}&dimensionFilterJSON={dimensionFilterJSON}&userId={userId}")]
        IList<Shared.Poco.DimValue> GetDimValueList(int budgetId, string dimensionFilterJSON, string userId);
    }

service markup

<%@ ServiceHost Language="C#" 
Debug="true" 
Service="DimValueService"
Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" 
CodeBehind="DimValue.svc.cs" %>

Detailed error when calling DimValue.svc/GetDimValueList

The server encountered an error processing the request. The exception message is 'Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.'. See server logs for more details. The exception stack trace is: at Autofac.Core.Lifetime.LifetimeScope.CheckNotDisposed() at Autofac.Core.Lifetime.LifetimeScope.BeginLifetimeScope(Object tag) at Autofac.Core.Lifetime.LifetimeScope.BeginLifetimeScope() at Autofac.Integration.Wcf.AutofacInstanceContext..ctor(ILifetimeScope container) at Autofac.Integration.Wcf.AutofacInstanceProvider.GetInstance(InstanceContext instanceContext, Message message) at System.ServiceModel.Dispatcher.InstanceBehavior.GetInstance(InstanceContext instanceContext, Message request) 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.ProcessMessage4(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

like image 595
JohanLarsson Avatar asked Jan 20 '15 20:01

JohanLarsson


People also ask

How does Autofac work with WCF?

The way Autofac hooks into WCF, it uses an instance provider to resolve your service and dependencies. The instance provider makes use of the service instance context to track the lifetime scope in which your service and its dependencies live. What that boils down to: A lifetime scope is created based on the instance context mode of your service.

What is instanceperrequest in Autofac?

When you register a component as InstancePerRequest (), you’re telling Autofac to look for a lifetime scope that is tagged as the request scope and to resolve the component from there. That way if you have unit-of-work lifetime scopes that take place during a single request, the per-request dependency will be shared during the request:

Does WCF support per request lifetime dependencies?

Due to WCF internals, there is no explicit support in WCF for per-request lifetime dependencies. There are a couple of benefits to using Autofac in conjunction with your service client application: Deterministic disposal: Automatically free resources consumed by proxies created by ChannelFactory.CreateChannel<T> ().

What is a lifetime scope in Autofac?

The concept of a lifetime scope in Autofac combines these two notions. Effectively, a lifetime scope equates with a unit of work in your application. A unit of work might begin a lifetime scope at the start, then services required for that unit of work get resolved from a lifetime scope.


1 Answers

The problem is that your service implementation is taking in a child lifetime scope and then changing the global lifetime scope.

The ILifetimeScope coming into your service implementation is a child of the container specific to that insurance of the service implementation and it gets disposed when the service implementation gets disposed.

But the service implementation is switching the global container - AutofacHostFactory.Container - to be that child lifetime scope. So when the service instance goes away... So does, now, the global container.

Stop setting the container from the service and you should be fine.

In general, after app startup, you really shouldn't be messing with the global root container.

like image 122
Travis Illig Avatar answered Sep 28 '22 07:09

Travis Illig