2015-07-09 9 views
7

Mam projekt Web API, który używa StructureMap dla jego DI. Od jakiegoś czasu działa dobrze, ale mam pewne problemy ze stronami pomocy interfejsu API (Microsoft.AspNet.WebApi.HelpPage), w których InvalidOperationExceptions są generowane w wyniku pustego stosu.StructureMap powoduje wyjątek Stack Pusty w Web Help Pages ModelDescriptionLink.cshtml

stworzyłem nowy projekt Web API ze stron pomocy, i to działa dobrze, dopóki dodać pakiet StructureMap.WebApi2, podczas gdy wcześniej wspomniano jest wyjątek jest tutaj, wewnątrz ModelDescriptionLink.cshtml

else if (modelDescription is CollectionModelDescription) 
{ 
    var collectionDescription = modelDescription as CollectionModelDescription; 
    var elementDescription = collectionDescription.ElementDescription; 
    @:Collection of @Html.DisplayFor(m => elementDescription.ModelType, "ModelDescriptionLink", new { modelDescription = elementDescription }) 
} 
else 
{ 
    @Html.DisplayFor(m => modelDescription) 
} 

Jest wyrzucany pod @:Collection of @Html.DisplayFor(m => elementDescription.ModelType, "ModelDescriptionLink", new { modelDescription = elementDescription }), gdy próbuje wyświetlić link do opisu zasobu dla swojego modelu.

Jest to odchudzona trasa, która nadal powoduje wyjątek:

[Route("Test")] 
public IHttpActionResult Post([FromBody] IEnumerable<MySimpleModel> models) 
{ 
    return null; 
} 

Próbując znaleźć dokumentację dla tej trasy w http://localhost:21966/Help/Api/POST-Test powoduje wyjątek:

Exception image

byłem tylko w stanie Aby znaleźć jeden example of someone having the same problem, a ich rozwiązania miały przejść od StructureMap do Ninject lub aby uniknąć wyjątku z zerowymi kontrolami.

Oto szczyt ślad stosu:

[InvalidOperationException: Stack empty.] 
System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) +52 
System.Collections.Generic.Stack`1.Peek() +6693321 
System.Web.WebPages.WebPageBase.get_Output() +51 
System.Web.WebPages.WebPageBase.GetOutputWriter() +35 
System.Web.WebPages.WebPageExecutingBase.BeginContext(String virtualPath, Int32 startPosition, Int32 length, Boolean isLiteral) +50 
ASP._Page_Areas_HelpPage_Views_Help_DisplayTemplates_ModelDescriptionLink_cshtml.Execute() in c:...ModelDescriptionLink.cshtml:28 
System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +271 
System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +122 
System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +145 
System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +695 
System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +382 
System.Web.Mvc.Html.ActionCacheViewItem.Execute(HtmlHelper html, ViewDataDictionary viewData) +278 

łapiąc wyjątek w tym miejscu, to pojawia się później w HelpPageApiModel.cshtml na niemal identyczny linii: @Html.DisplayFor(m => m.ResourceDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ResourceDescription }). Jest to dodatek ślad stosu:

[InvalidOperationException: Stack empty.] 
System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) +52 
System.Collections.Generic.Stack`1.Pop() +6667365 
System.Web.WebPages.WebPageBase.PopContext() +66 
System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +154 

Odpowiedz

1
nie

Wciąż pewny dlaczego StructureMap.WebApi2 jest ten problem, ale moje rozwiązanie było w zasadzie przepisać uchwałę o zależnościach bez tej biblioteki. This blog post był dość pomocny, aby sposób wdrożenia go bez użycia IDependencyScope

Edytuj: Poproszono mnie o pokazanie przykładu. Jest to w zasadzie rozwiązanie zamieszczone na tym blogu, ale mam nadzieję, że i tak będzie pomocne.

Dodaj aktywator zwyczaj kontrolera dla StructureMap:

public class StructureMapWebApiControllerActivator : IHttpControllerActivator 
{ 
    private readonly IContainer _container; 

    public StructureMapWebApiControllerActivator(IContainer container) 
    { 
     _container = container; 
    } 

    public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) 
    { 
     var nested = _container.GetNestedContainer(); 
     var instance = nested.GetInstance(controllerType) as IHttpController; 
     request.RegisterForDispose(nested); 
     return instance; 
    } 
} 

Oto co moja inicjalizacji pojemnik wygląda następująco:

public class IoC 
{ 
    public static IContainer InitializeContainer() 
    { 
     IContainer container = new Container 
     (
      c => c.Scan 
      (
       scan => 
       { 
        scan.Assembly("assembly1"); 
        scan.Assembly("assembly2"); 
        scan.LookForRegistries(); 
        scan.WithDefaultConventions(); 
       } 
      ) 
     ); 
     return container; 
    } 
} 

a potem gdziekolwiek jesteś konfigurowania HttpConfiguration (dla takich jak wywoływanie MapHttpAttributeRoutes()), zastąp aktywator kontrolera nową mapą struktury jeden:

HttpConfig.MapHttpAttributeRoutes(); 
var container = IoC.InitializeContainer(); 
HttpConfig.Services.Replace(typeof(IHttpControllerActivator), new StructureMapWebApiControllerActivator(container)); 

W moim przypadku używam OWIN, więc nie używam GlobalConfiguration.ConfigurationHttpConfiguration, ale pomysł jest taki sam. Uważam, że zazwyczaj istnieje klasa WebApiConfig, która ma metodę, która została przekazana HttpConfiguration, więc można tam dokonać wymiany.

Edit ponownie do zmienionych części HelpController.cs:

Pierwotnie StructureMap próbował użyć drugiego konstruktora (który miał argument HttpConfiguration), co powoduje, że jest w stanie utworzyć instancję HelpController. Naprawiłem to, zaznaczając drugi konstruktor jako chroniony.

Zmieniłem również odniesienie z HttpConfiguration GlobalConfiguration na moje własne (z mojej klasy Startup) w wywołaniu pierwszego konstruktora, ponieważ używam Owina.

public HelpController(): this(Startup.HttpConfig) 
{ 
    logger.Trace("Help controller created"); 
} 

protected HelpController(HttpConfiguration config) 
{ 
    Configuration = config; 
} 
+0

Czy możesz (lub ktoś) opublikować tutaj działające rozwiązanie? Próbowałem odwołać się do blogu bez powodzenia. – ssmith

+0

@ssmith Pewnie, właśnie dodałem kod pokazujący, jak to działa – Zout

+0

dzięki! Mam dokładnie to, co jest w moim rozwiązaniu, ale wciąż widzę pusty Stack. wyjątek w ModelDescriptionLink.cshtml. :( – ssmith

Powiązane problemy