2014-10-15 2 views
1

Мы, наконец, преобразовали наше VS 2008 решение .NET MVC в VS 2012 (yay!), Но обнаружили тревожную проблему. В веб-приложении заканчивается память. После многих часов расследования мы обнаружили следующее:Роли не освобождаются, приложение заканчивается из памяти

Пользовательский атрибут для наших контроллеров используется для проверки того, что у пользователя есть соответствующая роль для доступа к этой функции. Это выглядит следующим образом:

[ScreenAccessAuthorize(Roles = "RoleName.Access"), Prefs] 
public class SampleController : SampleBaseController 

Мы используем метод переопределения конкатенировать роль других возможных имен ролей для нашей системы. Это выглядит следующим образом:

public class ScreenAccessAuthorize : AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     ... 
    } 

    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     string r = "Admin," + Roles + ".Admin," + Roles + ".Access," + Roles; 
     Roles = r; 
     return base.AuthorizeCore(httpContext); 
    } 
} 

Там не было никаких проблем с этим кодом в проекте VS 2008 (таргетингом 3.5 Framework), но по какой-то причине, проект VS 2012 (нацеливание Framework 4.5) выполняется из памяти в функция переопределения.

При первом запуске атрибута (в 2012 году) значение для ролей - «RoleName.Access», а значение для r - «Admin, RoleName.Admin, RoleName.Access, RoleName.Access», но затем вызовы метода переопределения атрибута, похоже, объединяют значение Роли до тех пор, пока в приложении не будет памяти.

Дальнейшее расследование (в 2012 году) показало, что если мы изменим контроллеры, посетив другую страницу, значение Роли, похоже, будет сброшено, но если мы будем непрерывно выбирать функции, которые вызывают методы внутри одного контроллера, значение для ролей будет расти пока у нас не закончится память. Это какой-то странный вид кеширования в MVC4? Почему не значение для сброса ролей в атрибуте?

[ScreenAccessAuthorize(Roles = "RoleName.Access"),Prefs] 

Мы видим, что Роли является собственностью System.Web.Mvc, которая имеет другой номер версии от MVC3 к MVC4, которая с той лишь разницей, что мы знаем.

Может кто-нибудь подумать, почему у нас возникает эта проблема?

Пожалуйста, дайте мне знать, если вам нужно больше объяснений.

ответ

1

Ответ скрыт глубоко внутри кода MVC. Проблема связана с тем фактом, что поставщик фильтра по умолчанию, FilterAttributeFilterProvider, который строит экземпляры фильтров для контроллеров и действий, по умолчанию разрешен кеш экземпляров фильтра.

Это означает, что, хотя фабрика контроллера по умолчанию будет всегда создавать новый экземпляр контроллера, поставщик фильтра будет не обязательно создать новый экземпляр фильтра, а разрешить повторное использование кэшированного экземпляра.

Один из обходных путей, чтобы иметь строку переназначения в конструкторе

public class ScreenAccessAuthorize : AuthorizeAttribute 
{ 
    public ScreenAccessAuthorize(string Roles) 
    { 
     int v = 0; 
     string r = "Admin," + Roles + ".Admin," + Roles + ".Access," + Roles; 
     this.Roles = r; 
    } 

    protected override void HandleUnauthorizedRequest( 
     AuthorizationContext filterContext) 
    { 
    } 

    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     return base.AuthorizeCore(httpContext); 
    } 
} 

, но как вы можете видеть это требует предоставления параметра Я в явном виде

[ScreenAccessAuthorize("RoleName.Access") 

вместо

[ScreenAccessAuthorize(Roles = "RoleName.Access"), Prefs] 

где присвоение выполнено после вызывается конструктор (и, следовательно, значение параметра будет null в конструкторе.

Другое решение - отключить кеширование экземпляра фильтра, заменив поставщика фильтра новым, который не кэширует экземпляры. Это можно сделать в global.asax

protected void Application_Start() 
    { 
     // remove the one that caches instances by default 
     FilterProviders.Providers.Remove( 
      FilterProviders.Providers.Where( 
       p => p is FilterAttributeFilterProvider).FirstOrDefault()); 

     // replace with one that doesn't 
     FilterProviders.Providers.Add(new FilterAttributeFilterProvider(false)); 
    } 
+0

Спасибо. Я реализовал конструктор в фильтре и явно предоставил этот параметр, но код конструктора выполнялся несколько раз для одного запроса и казался чрезмерным, хотя и предотвращал рост строки Roles и нехватка памяти. Затем я отключил кэширование экземпляров, как вы сказали. Это решение, похоже, работает намного лучше. Есть несколько фильтров, и строка ролей растет в каждом из них, поэтому отключение кэширования работает для всех. – codenewbie

1

Кроме того, вы, вероятно, должны делать это в конструкторе, а не в AuthorizeCore, да .. есть кеширование роли, хотя я не уверен, когда он был добавлен.

Проблема здесь, хотя кажется более похожей на сам контроллер не разрушается и не воссоздается. Вы, случайно, делаете инъекцию зависимостей? Вы используете фабрику пользовательских контроллеров? Я бы посмотрел.

Вы можете попробовать отключить кеширование роли в файле web.config.

<roleManager ... cacheRolesInCookie="false"...> 
+0

В основном вы задаете вопросы, а не предоставляете ответ. –

+0

Это устаревший проект, поэтому мы застряли с тем, что у нас есть, пока не сделаем улучшения. Мы не используем инъекции зависимостей, у нас нет фабрики настраиваемых контроллеров. В этой статье говорится, что контроллер собирает мусор после его создания: http://stackoverflow.com/questions/2355139/what-is-the-lifetime-of-a-asp-net-mvc-controller. Но я не понимаю, почему у нас не было этой проблемы, прежде чем мы обновили ее. Можете ли вы придумать что-нибудь еще? – codenewbie

+0

@codenewbie - see edit –

Смежные вопросы