webadvanced / Structuremap.MVC5

Apache License 2.0
21 stars 9 forks source link

System.InvalidOperationException Stack empty when using SM.MVC5 #14

Open martsinkevich opened 8 years ago

martsinkevich commented 8 years ago

Hello, After installing SM.MVC5 package got next exception System.InvalidOperationException Stack empty. in case when you try to display circular object dependency with partial view.

Ex. @model WebApplication.Models.Test

@foreach (var val in Model.Tests) { @Html.Partial(val.GetType().Name, val) }

or just @Html.DisplayFor(model=>model.Tests)

Object example: public class Test { public ICollection Tests { get; set; } }

Any thoughts how it is possible to resolve? Thanks.

fean commented 8 years ago

Yes, I'm also having the same problem! Did you ever solve it?

sminceo commented 8 years ago

I'm getting the same problem when I'm running a recursive partial view in MVC 5. Did either of you figure out a solution?

medvjed commented 8 years ago

The issue is with the view creation which is internally done with the DependencyResolver thus with structuremap. Structuremap always gives you the same instance which is a problem for recursive views of views in an foreach.

There is a solution, but I would rather see structuremap solving this issue. on an better level. right before changing your DependencyResolver get the old one. var defaultResolver = DependencyResolver.Current; DependencyResolver.SetResolver(StructureMapDependencyScope);

Reregister your viewengines: SimpleViewPageActivator activator = new SimpleViewPageActivator(defaultResolver); RazorViewEngine viewEngine = new RazorViewEngine(activator) ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(viewEngine); ViewEngines.Engines.Add(new WebFormViewEngine(activator)); You now get a fresh new view for every instance

SimpleViewPageActivator is a custom class: ` public class SimpleViewPageActivator : IViewPageActivator { private IDependencyResolver resolver;

    public SimpleViewPageActivator(IDependencyResolver resolver)
    {
        if (resolver == null)
        {
            throw new ArgumentException("resolver can't be null");
        }
        this.resolver = resolver;
    }

    public object Create(ControllerContext controllerContext, Type type)
    {
        return this.resolver.GetService(type) ?? Activator.CreateInstance(type);
    }
}`
nickalbrecht commented 8 years ago

In case you're still looking for a solution, I ended up using a convention for Views, and gave some details here. https://github.com/webadvanced/Structuremap.MVC5/issues/3#issuecomment-222526385

martsinkevich commented 8 years ago

as a 'dirty' fix update StructureMapDependencyScope class to disable StructureMap for cshtml views.

protected override object DoGetInstance(Type serviceType, string key) {
            IContainer container = (CurrentNestedContainer ?? Container);

            //disable StructureMap for views, without this, causes "Stack Empty" exception
            if (serviceType.Name.Contains("cshtml"))
            {
                return Activator.CreateInstance(serviceType);
            }

            if (string.IsNullOrEmpty(key)) {
                return serviceType.IsAbstract || serviceType.IsInterface
                    ? container.TryGetInstance(serviceType)
                    : container.GetInstance(serviceType);
            }

            return container.GetInstance(serviceType, key);
        }