How to properly scope composition per request using ASP.NET MVC, WebAPI, and MEF
Asked Answered
M

2

6

I recently added MEF to an MVC/WebAPI application using a variety of resources including this SO answer How to integrate MEF with ASP.NET MVC 4 and ASP.NET Web API. While this worked for a time, I started to receive intermittent errors related to making connections to the database, the most frequent one being: "System.InvalidOperationException: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached."

I realized I was leaking connections but didn't understand why. All of my repositories implemented IDisposable and disposed of their connections when done. Putting breakpoints in my dispose methods quickly revealed that they were never being hit. When I based my code off of the example linked to above, I noticed the lack of any cleanup, but being new to MEF and MVC I wrongly assumed that cleanup was being done somewhere in MVC's/MEF's dependency pipeline.

I'm wondering how other people have tackled using MEF to properly scope composition on a per request basis in both MVC and WebAPI?

I have found vague guidance here and there and it's all geared toward either MVC or WebAPI. Mef.codeplex has an almost complete MVC centric solution here: https://mef.codeplex.com/releases/view/79090 but it's based off of a preview version of MVC. I found a WebAPI solution here: https://github.com/WebApiContrib/WebApiContrib.IoC.Mef. I'm rolling my own solution at the moment but as I hate to reinvent the wheel, I thought I'd ask to see if anyone knew of one rolling around already.

Memorable answered 27/12, 2013 at 18:57 Comment(3)
I too am having memory issues with WebAPI using MEF for dependency resolution, I followed the same resources as you have. I have mitigated it slightly by setting the pool recycle every 5 mins. However this has led to another problem, in that the resolver stops working occasionally and I get a null reference exception which I am investigating now. once the app pool recycles a second time everything comes good again! It would be good to see either the WebAPI team or MEF team show us how this should be done.Venezuela
@Venezuela That certainly isn't ideal. I've mostly solved this issue for the moment. I have three projects, one that does the bulk of the DI, one that helps dependency scope for MVC, and one that helps for WebAPI. I'll post on Github just as soon as I have time.Memorable
Thanks I would be very interested in looking at your solution.Venezuela
M
5

I ended up tackling this problem myself over the holidays after not finding anything to my satisfaction. MEF contrib on CodePlex had a good start but it was unfinished. I incorporated it with a few modification and combined that with some research and trial and error.

I've created a project on Github (link below, I know external links are frowned upon but it's just too much code to include inline). In it are four projects. The first provides core composition and teardown, the two libraries put the core into the context of MVC and WebAPI respectively, and the last is just a quick sample MVC app with two controllers that each depend on another class which is injected. One caveat, I consider the WebAPI project unfinished as it doesn't yet include facilities for WebAPI filter providers (and maybe other things I haven't thought of or needed yet).

I hope this helps.

https://github.com/rlvandaveer/Heliar-Web-Composition

Memorable answered 13/2, 2014 at 17:26 Comment(0)
V
2

Wow thanks. I also had a go at resolving this, Though much simpler approach I have confirmed a dramatic reduction in memory use. I created a MefDependencyResolver Which in the BeginScope method instead of returning 'this' as we have seen in other examples, I create a child container based on a filtered catalog as shown in the mef codplex site http://mef.codeplex.com/wikipage?title=Filtering%20Catalogs&referringTitle=Parts%20Lifetime.

My WebAPI test project uses entity framework code first to store entities in a DB.

I created a test client to POST 15000 entities into the database, in each test I ran 3 concurrent clients, repeating the test 3 times. With begin scope returning 'this' and a NOOP in the Dispose method I maxed out the memory allocated for the ApplicationPool. By returning a new container based on a filtered catalogue and disposing the container in the Dispose method and repeating the test Memory increased to 600MB and stayed there IIS remain happy and no pool recycling occurred.

 public class MefDependencyResolver : System.Web.Http.Dependencies.IDependencyResolver, System.Web.Mvc.IDependencyResolver
{
    protected CompositionContainer _container;

    public MefDependencyResolver(CompositionContainer container)
    {
        _container = container;
    }

    public IDependencyScope BeginScope()
    {
        var filteredCat = new FilteredCatalog(_container.Catalog,
            def => def.Metadata.ContainsKey(CompositionConstants.PartCreationPolicyMetadataName) &&
            ((CreationPolicy)def.Metadata[CompositionConstants.PartCreationPolicyMetadataName]) == CreationPolicy.NonShared);
        var child = new CompositionContainer(filteredCat, _container);


        return new MefDependencyResolver(child);
    }

    /// <summary>
    /// Called to request a service implementation.
    /// 
    /// Here we call upon MEF to instantiate implementations of dependencies.
    /// </summary>
    /// <param name="serviceType">Type of service requested.</param>
    /// <returns>Service implementation or null.</returns>
    public object GetService(Type serviceType)
    {
        if (serviceType == null)
            throw new ArgumentNullException("serviceType");

        var name = AttributedModelServices.GetContractName(serviceType);
        var export = _container.GetExportedValueOrDefault<object>(name);
        if (export != null)
        {
            Trace.WriteLine("PAUSE");
        }
        return export;
    }

    /// <summary>
    /// Called to request service implementations.
    /// 
    /// Here we call upon MEF to instantiate implementations of dependencies.
    /// </summary>
    /// <param name="serviceType">Type of service requested.</param>
    /// <returns>Service implementations.</returns>
    public IEnumerable<object> GetServices(Type serviceType)
    {
        if (serviceType == null)
            throw new ArgumentNullException("serviceType");

        var exports = _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        return exports;
    }

    #region IDisposable
    private bool _disposed = false;
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing) // Managed:
            {
                //NOOP since MEF does not have the idea of a Scoped Container (except it does have a concept of a filtered container!)
                //
                Trace.WriteLine("DISPOSING MEF CONTAINER.");
                this._container.Dispose();
                this._container = null;

            }
            // Unmanaged:



            _disposed = true;
        }
    }

    ~MefDependencyResolver()
    {
        Dispose(false);
    }
    #endregion
}
Venezuela answered 14/2, 2014 at 17:35 Comment(1)
If you're only concerned about scoped composition in WebAPI, (I'm making that assumption because your code won't work for MVC), why not just use the project I listed above in my question or one of the other MEF WebAPI projects available on Nuget?Memorable

© 2022 - 2024 — McMap. All rights reserved.