Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Morten Bock 1867 posts 2138 karma points MVP 2x admin c-trib
    Jun 13, 2013 @ 15:41
    Morten Bock
    0

    Web API UmbracoApiController does not use DependencyResolver

    I've just started trying out the new Web API options in version 6.1.x, and I stumbled on this.

    When I have a regular RenderMvcController, and I have set up my IoC for the DependencyHandler, then I am able to resolve dependencies in my controllers constructor.

    If I create a constructor in a UmbracoApiController, then it throws an exception: 

    Type 'FooSite.Web.Api.Controllers.BasketApiController' does not have a default constructor

    Looking at the source, it seems that the DependencyResolver is not used when resolving these ApiControllers, as it is for RenderMvcControllers and SurfaceControllers.

    Is this a bug, or is there a different way to set up IoC for the WebAPI controllers?

  • Shannon Deminick 1476 posts 4950 karma points hq
    Jun 17, 2013 @ 02:45
    Shannon Deminick
    0

    Hi, will have a look today/tomorrow and let you know!

  • Morten Bock 1867 posts 2138 karma points MVP 2x admin c-trib
    Jun 19, 2013 @ 11:25
    Morten Bock
    0

    It seems that SurfaceControllers have the same issue. If it does not have a parameterless constructur, then I get this:

    [MissingMethodException: No parameterless constructor defined for this object.]
       System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
       System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98
       System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241
       System.Activator.CreateInstance(Type type, Boolean nonPublic) +69
       System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +67
    
    [InvalidOperationException: An error occurred when trying to create a controller of type 'Site.Web.SurfaceControllers.QuickAddSurfaceController'. Make sure that the controller has a parameterless public constructor.]
       System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +181
       System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) +77
       Umbraco.Web.Mvc.UmbracoControllerFactory.CreateController(RequestContext requestContext, String controllerName) +108
       Umbraco.Web.Mvc.MasterControllerFactory.CreateController(RequestContext requestContext, String controllerName) +129
       System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +197
       System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +50
       System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48
       System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
       System.Web.Mvc.<>c__DisplayClass7.<BeginProcessRequest>b__6() +29
       System.Web.Mvc.ServerExecuteHttpHandlerWrapper.Wrap(Func`1 func) +38
       System.Web.Mvc.ServerExecuteHttpHandlerAsyncWrapper.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +98
       System.Web.HttpServerUtility.ExecuteInternal(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage, VirtualPath path, VirtualPath filePath, String physPath, Exception error, String queryStringOverride) +1529
       System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage) +78
       System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +25
       System.Web.HttpServerUtilityWrapper.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +23
       System.Web.Mvc.Html.ChildActionExtensions.ActionHelper(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues, TextWriter textWriter) +459
       System.Web.Mvc.Html.ChildActionExtensions.Action(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues) +88
       System.Web.Mvc.Html.ChildActionExtensions.Action(HtmlHelper htmlHelper, String actionName, String controllerName) +11

     

  • Shannon Deminick 1476 posts 4950 karma points hq
    Jun 20, 2013 @ 00:55
    Shannon Deminick
    0

    I'll have a look today

  • Shannon Deminick 1476 posts 4950 karma points hq
    Jun 20, 2013 @ 09:09
    Shannon Deminick
    0

    I didn't get time today. If this is the case can you please log an issue and assign to me otherwise it will get lost in the abyss ?

  • Morten Bock 1867 posts 2138 karma points MVP 2x admin c-trib
    Jun 20, 2013 @ 09:32
    Morten Bock
    0

    Yup, I will log an issue. Just wanted to make sure that it was not "by design" :)

    Edit: Issue created: http://issues.umbraco.org/issue/U4-2405

  • Shannon Deminick 1476 posts 4950 karma points hq
    Jun 20, 2013 @ 09:34
    Shannon Deminick
    0

    nah, they should be IoC compatible.

  • Floris Robbemont 57 posts 89 karma points c-trib
    Jun 20, 2013 @ 13:00
    Floris Robbemont
    1

    This could be a possible solution: https://gist.github.com/florisrobbemont/5821863

  • Shannon Deminick 1476 posts 4950 karma points hq
    Jun 21, 2013 @ 01:42
    Shannon Deminick
    102

    Ok, for SurfaceControllers it should work however you must not have your IoC container setup correctly. You can see in your stack trace above that it stems from this line of code:

    System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext,Type controllerType)+181

    The MVC default controller factory *does* use the DependencyResolver so you are getting this exception because your IoC container is not setup correctly and based on which IoC container you are using there are various ways to set it up. For example, AutoFac is super easy to setup and you more or less just change the dependency resolver to use the autofac dependency resolver and a way you go. I know Floris is using the windsor container which i think requires more effort with setting up controller factories, etc...

    I've also tested that WebApi controllers do work with IoC so this must be how you've setup your IoC container. Here's an example of how you'd setup your container for AutoFac to work for both SurfaceControllers and UmbracoApiControllers:

    On app startup:

        //this example will put the ApplicationContext, 
        //all MVC controllers and API controllers in the
        //current assembly in the container.
        var builder = new ContainerBuilder();
        builder.RegisterInstance(ApplicationContext.Current).AsSelf();
        builder.RegisterControllers(Assembly.GetExecutingAssembly());
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
        var container = builder.Build();
        //setup the webapi dependency resolver to use autofac
        var resolver = new AutofacWebApiDependencyResolver(container);
        GlobalConfiguration.Configuration.DependencyResolver = resolver;
        //setup the mvc dependency resolver to user autofac
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

    Then we could have a SurfaceController that looks like:

        [PluginController("MyArea")]
        public class MyTestController : SurfaceController
        {
            private ApplicationContext _appContext;
            public MyTestController(ApplicationContext appContext)
            {
                _appContext = appContext;
            }
    
            [ChildActionOnly]
            public ActionResult DoThis()
            {
                return Content("Hello World");
            }
        }

    And we could have an api controller that looks like:

        public class MyTestApiController : UmbracoApiController
        {
            private ApplicationContext _appContext;
            public MyTestApiController(ApplicationContext appContext)
            {
                _appContext = appContext;
            }
    
            public IEnumerable<string> GetStuff()
            {
                return new[] { "hello", "world" };
            }
        }

    I've verified and tested that this all works so it has something to do with how you've setup your IoC container. The only place in our codebase that we are actually constructing any controllers is in the SurfaceControllerFactory and that is purely to manually inject the UmbracoContext as a ctor parameter if the DependencyResolver fails, otherwise all controller construction is done via the underlying DefaultControllerFactory which also uses the DependencyResolver. We haven't setup anything out of the ordinary for WebApi whatsoever so whatever IoC stuff you need to do for that will just work.


  • Morten Bock 1867 posts 2138 karma points MVP 2x admin c-trib
    Jun 21, 2013 @ 09:04
    Morten Bock
    0

    Looks like there are some lines in there that I don't have currently.

    I used this as a starting point: http://our.umbraco.org/Documentation/Reference/Mvc/using-ioc

    Maybe that should be updated with this part:

    GlobalConfiguration.Configuration.DependencyResolver= resolver;


  • Floris Robbemont 57 posts 89 karma points c-trib
    Jun 21, 2013 @ 09:19
    Floris Robbemont
    1

    @Shannon:

    Awesome post! Good details.

    As for the documentation on using an IoC with Umbraco:

    I've noticed that this is a difficult concept for some developers to grasp. For instance: which container to use? Why should I use an IoC in the first place? etc

    Maybe we can extend the documentation with samples for all the major IoC components out there (Windsor, Ninject, Autofac, MEF (not an IoC, but it is used as such at times), etc). The Windsor container requires some additional work because you need to manually release every dependency. The Umbraco documentation should not include a 'How to choose an IoC component' page, but it is important to notify the user of potential pitfalls (You will get a lot of memory leaks with Windsor if you don't release every dependency properly).

    I can see an 'Umbraco.Integration.IoC' package coming up ^^

    Umbaco.Integration.IoC.Windsor
    Umbaco.Integration.IoC.Ninject 
    Umbaco.Integration.IoC.Autofac

    Which have all the neccesary boiler-plate code to wire your IoC of choice into Umbraco

  • Morten Bock 1867 posts 2138 karma points MVP 2x admin c-trib
    Jun 21, 2013 @ 10:02
    Morten Bock
    0

    Ok, so first off, it turned out to be a facepalm moment when I realized that i had actually done a controller registry, that only included RenderMvcControllers. I addet the others, and then it worked like a charm. And I didn't need to add the extra lines to my bootstrapper, as it worked fine with just the DependenceResolver.SetResolver().

    But it wasn't for nothing as I was made aware of the release tracking stuff in Windsor that I needed to handle to avoid leaks. So thanks for that :)

    @Floris I think it would be a great idea to make som IoC samples for different containers. And maybe even som reusable bits.

    Thanks for all the info.

  • Morten Bock 1867 posts 2138 karma points MVP 2x admin c-trib
    Jun 21, 2013 @ 11:50
    Morten Bock
    0

    I was too quick, I do actually need this line when using the ApiControllers:

    GlobalConfiguration.Configuration.DependencyResolver = resolver;
  • Dave Woestenborghs 3198 posts 10621 karma points MVP 4x admin c-trib
    Mar 06, 2014 @ 13:09
    Dave Woestenborghs
    0

    Hi all,

    I'm using v6.1.6 and don't get DI working well with ApiControllers. All API calls return in a 500 error and nothing comes up in the logs

    This my IOC setup code using autofac

        // setup autofac 
                var builder = new ContainerBuilder();
    
                builder.RegisterInstance(ApplicationContext.Current).AsSelf();
    
                builder.RegisterControllers(Assembly.Load("MyProject.Controllers"));
    
                builder.RegisterApiControllers(Assembly.Load("MyProject.Controllers"));
    builder.RegisterType<Services.SettingsService>().As<Interfaces.ISettingsService>(); builder.RegisterType<Services.SnippetService>().As<Interfaces.ISnippetService>(); builder.RegisterType<Services.MemberService>().As<Interfaces.IMemberService>(); builder.RegisterType<Services.MailService>().As<Interfaces.IMailService>(); var container = builder.Build(); // WEB API var resolver = new AutofacWebApiDependencyResolver(container); GlobalConfiguration.Configuration.DependencyResolver = resolver; // MVC DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

    This is my test api controller code

     public class TestApiController : UmbracoApiController
        {
            public TestApiController() 
            {
    
            }
            /// <summary>
            /// The get test.
            /// </summary>
            /// <returns>
            /// The <see cref="string"/>.
            /// </returns>
            public string GetTest()
            {
                return "test";
            }
        }

    Am I doing something wrong ?

  • Dave Woestenborghs 3198 posts 10621 karma points MVP 4x admin c-trib
    Mar 06, 2014 @ 13:15
    Dave Woestenborghs
    0

    Also if I put breakpoints in my API controller..They never get hit.

    Dave

  • Dave Woestenborghs 3198 posts 10621 karma points MVP 4x admin c-trib
    Mar 10, 2014 @ 09:01
    Dave Woestenborghs
    0

    Anyone ?

  • Shannon Deminick 1476 posts 4950 karma points hq
    Mar 10, 2014 @ 09:07
    Shannon Deminick
    0

    haven't had time to follow up on this, sometime this week

  • Dave Woestenborghs 3198 posts 10621 karma points MVP 4x admin c-trib
    Mar 10, 2014 @ 09:47
    Dave Woestenborghs
    0

    At the moment I have the problem that none of my API controllers are working anymore. Also the Umbraco core api's. Like the ones used in the examine dashboard and the one called when setting domains. All return a 500 error. 

    When I attach a break point in the umbraco core source also none of the api controllers get hit while other breakpoints (for example in PublishedContentRequest) get hit.

    Dave

  • Shannon Deminick 1476 posts 4950 karma points hq
    Mar 10, 2014 @ 11:31
    Shannon Deminick
    0

    Until I have time to look at what issue you are having, do these help at all?

    http://issues.umbraco.org/issue/U4-2405 http://issues.umbraco.org/issue/U4-4181

    Sounds slightly similar

  • Dave Woestenborghs 3198 posts 10621 karma points MVP 4x admin c-trib
    Mar 10, 2014 @ 15:37
    Dave Woestenborghs
    0

    My code matches the second issue. But no API controllers work. RenderMvcControllers and Surface controllers do.

     

     

  • Shannon Deminick 1476 posts 4950 karma points hq
    Mar 18, 2014 @ 04:05
    Shannon Deminick
    1

    I've just tested this with autofac and it works just fine.

    first install auto-fac packages:

    Install-Package Autofac.Mvc4
    Install-Package Autofac.WebApi
    

    In my example, i just did everything in App_Code, here's the file:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using Autofac;
    using Autofac.Integration.Mvc;
    using Autofac.Integration.WebApi;
    using Umbraco.Core;
    using Umbraco.Web.Models;
    using Umbraco.Web.Mvc;
    using Umbraco.Web.WebApi;
    using System.Web.Http;
    
    namespace Umbraco.Web.UI.App_Code
    {
        public class MyStartupHandler : ApplicationEventHandler
        {
            protected override void ApplicationInitialized(
                UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
            {            
                var builder = new ContainerBuilder();
    
                //Register the UmbracoContext as a factory since the below controllers
                //require this in their ctor
                builder.Register(c => UmbracoContext.Current).AsSelf();
    
                //Register the current assembly (since these are in App_Code which are dynamic assemblies)
                builder.RegisterControllers(Assembly.GetExecutingAssembly());
                builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
    
                var container = builder.Build();
    
                //setup the webapi dependency resolver to use autofac
                var resolver = new AutofacWebApiDependencyResolver(container);
                GlobalConfiguration.Configuration.DependencyResolver = resolver;
                //setup the mvc dependency resolver to user autofac
                DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
            }
        }
    
        public class MyCoolApiController : UmbracoApiController
        {
            //ctor requires injected umbraco context
            public MyCoolApiController(UmbracoContext umbracoContext)
                : base(umbracoContext)
            {
    
            }
    
            public object GetStuff()
            {
                return "hello world";
            }
        }
    
        public class umbHomepageController : RenderMvcController
        {
            //ctor requires injected umbraco context
            public umbHomepageController(UmbracoContext umbracoContext)
                : base(umbracoContext)
            {
    
            }
    
            public ActionResult Index(RenderModel model)
            {
                return Content("hello world");
            }
    
        }
    }
    

    IoC is now working for MVC controller hijacking for any doc type with an alias of umbHomepage and is working for WebApi controllers, for this example, if you browse to /umbraco/api/mycoolapi/getstuff
    you'll see an xml rendition of Hello world.

  • Shannon Deminick 1476 posts 4950 karma points hq
    Mar 18, 2014 @ 04:06
    Shannon Deminick
    0

    And just to show that it does use the dependency resolver for webapi, if i remove the UmbracoContext registration and go to that webapi url again i get the following result:

    <Error>
        <Message>An error has occurred.</Message>
        <ExceptionMessage>
        None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Umbraco.Web.UI.App_Code.MyCoolApiController' can be invoked with the available services and parameters: Cannot resolve parameter 'Umbraco.Web.UmbracoContext umbracoContext' of constructor 'Void .ctor(Umbraco.Web.UmbracoContext)'.
        </ExceptionMessage>
        <ExceptionType>Autofac.Core.DependencyResolutionException</ExceptionType>
        <StackTrace>
        at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters) at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters) at Autofac.Core.Resolving.InstanceLookup.Execute() at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters) at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters) at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters) at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters) at Autofac.Integration.WebApi.AutofacWebApiDependencyScope.GetService(Type serviceType) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
        </StackTrace>
    </Error>
    
  • Dave Woestenborghs 3198 posts 10621 karma points MVP 4x admin c-trib
    Mar 18, 2014 @ 10:57
    Dave Woestenborghs
    0

    Still having 500 errors. NOthing in log and no error message in the browser. Also the default backend API controllers (Domain and Examine) return 500 errors, even with DI turned off for API controllers.

    This code works on a clean install, so I think the problem lies somewhere else. Still need some investigating to do. 

  • Daryl Duckmanton 1 post 21 karma points
    Dec 11, 2014 @ 05:09
    Daryl Duckmanton
    0

    I'm using Unity but this will definitely work. The resolver needs to implement the System.Web.Http.Dependencies.IDependencyResolver interface.

    GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);

  • Sandra van Dijk 1 post 21 karma points
    Jun 10, 2016 @ 16:52
    Sandra van Dijk
    1

    I had an issue in Umbraco, it kept giving the following error:

    An error occured

    Object reference not set to an instance of an object.

    EXCEPTION DETAILS

    System.NullReferenceException: Object reference not set to an instance of an object.

    Umbraco.Web.Security.WebSecurity.IsAuthenticated() Umbraco.Web.Security.WebSecurity.ValidateCurrentUser(Boolean+throwExceptions)

    After a lot of investigation I found the issue was due to a wrong setup of dependency injection.

    In the end I found that below setup works for Umbraco version 7.4.3. I hope somebody else might benefit from this.

    public class Global : UmbracoApplication
    {
        protected override void OnApplicationStarted(object sender, EventArgs e)
        {
            base.OnApplicationStarted(sender, e);
    
            var builder = new ContainerBuilder();
    
            builder.RegisterInstance(ApplicationContext.Current).AsSelf();
            builder.Register(c => UmbracoContext.Current).AsSelf();
            builder.RegisterInstance(new UmbracoHelper(UmbracoContext.Current)).AsSelf();
    
            builder.RegisterControllers(Assembly.GetExecutingAssembly());
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces().InstancePerLifetimeScope();
            //register umbraco webapi controllers used by the admin site
            builder.RegisterApiControllers(typeof(UmbracoApplication).Assembly);
    
    
            var container = builder.Build();
            //setup the webapi dependency resolver to use autofac
            var resolver = new AutofacWebApiDependencyResolver(container);
            GlobalConfiguration.Configuration.DependencyResolver = resolver;
            //setup the mvc dependency resolver to user autofac
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        }
    }
    

    Where the Global.asax is set as followed:

    <%@ Application Inherits="VarovaUmbraco.Global" CodeBehind="Global.asax.cs" Language="C#" %>

Please Sign in or register to post replies

Write your reply to:

Draft