Как правильно определить область композиции для каждого запроса с помощью ASP.NET MVC, WebAPI и MEF

Недавно я добавил MEF в приложение MVC/WebAPI, используя различные ресурсы, включая этот ответ SO Как интегрировать MEF с ASP.NET MVC 4 и веб-API ASP.NET. Хотя это работало какое-то время, я начал получать периодические ошибки, связанные с подключением к базе данных, наиболее частая из которых: «System.InvalidOperationException: Timeout expired. Период ожидания истек до получения подключения из пула. Это может произошло из-за того, что все соединения в пуле использовались и был достигнут максимальный размер пула».

Я понял, что пропускаю соединения, но не понял, почему. Все мои репозитории реализовали IDisposable и удалили свои соединения, когда это было сделано. Установка точек останова в моих методах удаления быстро показало, что они никогда не срабатывали. Когда я основывал свой код на примере, указанном выше, я заметил отсутствие какой-либо очистки, но будучи новичком в MEF и MVC, я ошибочно предположил, что очистка выполняется где-то в конвейере зависимостей MVC/MEF.

Мне интересно, как другие люди справились с использованием MEF, чтобы правильно определить композицию для каждого запроса как в MVC, так и в WebAPI?

Я нашел расплывчатые указания здесь и там, и все они ориентированы либо на MVC, либо на WebAPI. Mef.codeplex имеет почти полное решение, ориентированное на MVC, здесь: https://mef.codeplex.com/releases/view/79090, но он основан на предварительной версии MVC. Я нашел решение WebAPI здесь: https://github.com/WebApiContrib/WebApiContrib.IoC.Mef . В данный момент я обкатываю свое собственное решение, но, поскольку я ненавижу изобретать колесо, я подумал, что попрошу узнать, знает ли кто-нибудь об одном из них, который уже вращается.


person Robb Vandaveer    schedule 27.12.2013    source источник
comment
У меня тоже проблемы с памятью, когда WebAPI использует MEF для разрешения зависимостей, я использовал те же ресурсы, что и вы. Я немного смягчил это, настроив перезапуск пула каждые 5 минут. Однако это привело к другой проблеме, заключающейся в том, что преобразователь время от времени перестает работать, и я получаю исключение с нулевой ссылкой, которое сейчас исследую. как только пул приложений перерабатывается во второй раз, все снова становится хорошо! Было бы хорошо, если бы команда WebAPI или команда MEF показали нам, как это должно быть сделано.   -  person John    schedule 12.02.2014
comment
@John Это, конечно, не идеально. Я в основном решил эту проблему на данный момент. У меня есть три проекта, один из которых выполняет основную часть DI, один помогает определить область зависимостей для MVC, а третий помогает для WebAPI. Я опубликую на Github, как только у меня будет время.   -  person Robb Vandaveer    schedule 12.02.2014
comment
Спасибо, мне было бы очень интересно посмотреть на ваше решение.   -  person John    schedule 13.02.2014


Ответы (2)


В конце концов, я сам занялся этой проблемой во время праздников, так как не нашел ничего, что бы меня удовлетворило. У вклада MEF в CodePlex было хорошее начало, но он не был закончен. Я включил его с некоторыми изменениями и объединил это с некоторыми исследованиями, методом проб и ошибок.

Я создал проект на Github (ссылка ниже, я знаю, что внешние ссылки не одобряются, но это слишком много кода, чтобы включать встроенный). В нем четыре проекта. Первая обеспечивает композицию и демонтаж ядра, две библиотеки помещают ядро ​​в контекст MVC и WebAPI соответственно, а последняя представляет собой просто быстрый пример приложения MVC с двумя контроллерами, каждый из которых зависит от другого внедряемого класса. Одно предостережение: я считаю проект WebAPI незавершенным, поскольку он еще не включает средства для поставщиков фильтров WebAPI (и, возможно, другие вещи, о которых я еще не думал или которые мне не нужны).

Надеюсь, это поможет.

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

person Robb Vandaveer    schedule 13.02.2014

Вау, спасибо. Я также попытался решить эту проблему. Хотя гораздо проще подход, я подтвердил резкое сокращение использования памяти. Я создал MefDependencyResolver, который в методе BeginScope вместо возврата 'this', как мы видели в других примерах, я создаю дочерний контейнер на основе отфильтрованного каталога, как показано на сайте mef codplex http://mef.codeplex.com/wikipage?title=Filtering%20Catalogs&referringTitle=Parts%20Lifetime.

Мой тестовый проект WebAPI сначала использует код структуры сущностей для хранения сущностей в БД.

Я создал тестовый клиент для POST 15000 сущностей в базу данных, в каждом тесте я запускал 3 параллельных клиента, повторяя тест 3 раза. С областью начала, возвращающей «это» и NOOP в методе Dispose, я максимально увеличил память, выделенную для ApplicationPool. При возвращении нового контейнера на основе отфильтрованного каталога и удалении контейнера в методе Dispose и повторении теста объем памяти увеличился до 600 МБ и остался на этом уровне, IIS остался доволен, и повторного использования пула не произошло.

 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
}
person John    schedule 14.02.2014
comment
Если вас интересует только композиция с ограниченной областью действия в WebAPI (я делаю это предположение, потому что ваш код не будет работать для MVC), почему бы просто не использовать проект, который я перечислил выше в своем вопросе, или один из других проектов MEF WebAPI. доступно на Nuget? - person Robb Vandaveer; 25.02.2014