Daniel M
Daniel M

Reputation: 268

The type String cannot be constructed

I'm using Web.api and Unity and I am getting the following error when trying to open the default "help" area:

[InvalidOperationException: The type String cannot be constructed. You must configure the      container to supply this value.]
Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.GuardTypeIsNonPrimitive(IBuilderContext context, SelectedConstructor selectedConstructor) +280
Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.PreBuildUp(IBuilderContext context) +356
   Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) +260
   Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlanCreatorPolicy.CreatePlan(IBuilderContext context, NamedTypeBuildKey buildKey) +205
   Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) +231
   Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) +260
   Microsoft.Practices.ObjectBuilder2.BuilderContext.NewBuildUp(NamedTypeBuildKey newBuildKey) +250
   Microsoft.Practices.Unity.ObjectBuilder.NamedTypeDependencyResolverPolicy.Resolve(IBuilderContext context) +101
   BuildUp_System.Web.Http.HttpRouteCollection(IBuilderContext ) +202
   Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) +42
   Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) +319
   Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) +260
   Microsoft.Practices.ObjectBuilder2.BuilderContext.NewBuildUp(NamedTypeBuildKey newBuildKey) +250
   Microsoft.Practices.Unity.ObjectBuilder.NamedTypeDependencyResolverPolicy.Resolve(IBuilderContext context) +101
   BuildUp_System.Web.Http.HttpConfiguration(IBuilderContext ) +202
   Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) +42
   Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) +319
   Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) +260
   Microsoft.Practices.ObjectBuilder2.BuilderContext.NewBuildUp(NamedTypeBuildKey newBuildKey) +250
   Microsoft.Practices.Unity.ObjectBuilder.NamedTypeDependencyResolverPolicy.Resolve(IBuilderContext context) +101
   BuildUp_API.Areas.HelpPage.Controllers.HelpController(IBuilderContext ) +204
   Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) +42
   Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) +319
   Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) +260
   Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides) +373


[ResolutionFailedException: Resolution of the dependency failed, type = "API.Areas.HelpPage.Controllers.HelpController", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type String cannot be constructed. You must configure the container to supply this value.
-----------------------------------------------
At the time of the exception, the container was:

   Resolving API.Areas.HelpPage.Controllers.HelpController,(none)
  Resolving parameter "config" of constructor API.Areas.HelpPage.Controllers.HelpController(System.Web.Http.HttpConfiguration config)
Resolving System.Web.Http.HttpConfiguration,(none)
Resolving parameter "routes" of constructor System.Web.Http.HttpConfiguration(System.Web.Http.HttpRouteCollection routes)
  Resolving System.Web.Http.HttpRouteCollection,(none)
  Resolving parameter "virtualPathRoot" of constructor System.Web.Http.HttpRouteCollection(System.String virtualPathRoot)
    Resolving System.String,(none)
]
   Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides) +436
   Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, String name, IEnumerable`1  resolverOverrides) +50
   Microsoft.Practices.Unity.UnityContainer.Resolve(Type t, String name, ResolverOverride[] resolverOverrides) +48
   Microsoft.Practices.Unity.UnityContainerExtensions.Resolve(IUnityContainer container, Type t, ResolverOverride[] overrides) +61
   Unity.Mvc4.UnityDependencyResolver.GetService(Type serviceType) +140
   System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +87

[InvalidOperationException: An error occurred when trying to create a controller of type     'API.Areas.HelpPage.Controllers.HelpController'. Make sure that the controller has a parameterless    public constructor.]
   System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +247
   System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) +438
   System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName) +226
   System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +326
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +177
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +88
    System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +50
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +301
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

I am new to unity and am sure I am missing step(s). In webapiconfig.cs:

public static void Register(HttpConfiguration config)
{
    config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
    );

    //Custom formatter
    config.Formatters.Clear();
    config.Formatters.Add(new JSONPFormater());

    config.EnableSystemDiagnosticsTracing();

    //Setup DI
    Bootstrapper.Initialise();
}

Bootstraper.cs(default values)

public static class Bootstrapper
  {
    public static IUnityContainer Initialise()
    {
      var container = BuildUnityContainer();

      DependencyResolver.SetResolver(new UnityDependencyResolver(container));

      return container;
    }

    private static IUnityContainer BuildUnityContainer()
    {
      var container = new UnityContainer();

      // register all your components with the container here
      // it is NOT necessary to register your controllers

      // e.g. container.RegisterType<ITestService, TestService>();    
      RegisterTypes(container);

      return container;
    }

    public static void RegisterTypes(IUnityContainer container)
    {

    }
  }

My attempt at a web.config web.config

 <configSections>
    <!-- For more information on Entity Framework configuration, visit        http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework"     type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    <section name="unity"  type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <connectionStrings>
    <add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-API-20130708152001;Integrated     Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-API-20130708152001.mdf" />
    <add name="<REMOVED>DBEntities" connectionString="metadata=res://*/Models.DAL.<REMOVED>.csdl|res://*/Models.DAL.<REMOVED>.ssdl|res://*/Models.DAL.<REMOVED>.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=<REMOVED>;initial catalog=<REMOVED>;persist security info=True;user id=<REMOVED>;password=<REMOVED>;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />

<!--unity setting-->
<unity>
    <containers>
        <types>
            <register type="API.Areas.HelpPage.Controllers.HelpController, API">
                <constructor>
                    <param valu=""></param>
            </constructor>
            </register>
        </types>
    </containers>
</unity>

Am I headed in the right direction??

Thanks

Update: helpcontroller.cs:

public class HelpController : Controller
    {
        public HelpController()
            : this(GlobalConfiguration.Configuration)
        {
        }

        public HelpController(HttpConfiguration config)
        {
            Configuration = config;
        }

        public HttpConfiguration Configuration { get; private set; }

        public ActionResult Index()
        {
            return View(Configuration.Services.GetApiExplorer().ApiDescriptions);
        }

        public ActionResult Api(string apiId)
        {
            if (!String.IsNullOrEmpty(apiId))
            {
                HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId);
                if (apiModel != null)
                {
                    return View(apiModel);
                }
            }

            return View("Error");
        }
    }

url I am trying to access: http:// hostname:port/Help

Upvotes: 21

Views: 15643

Answers (5)

Always Learning
Always Learning

Reputation: 2713

The reason this happens is because Unity will by default choose to use the constructor with the most parameters thus bypassing the default constructor.

Comment out the two constructors that exist in the HelpController template and add a default one that sets the configuration.

    //public HelpController()
    //    : this(GlobalConfiguration.Configuration)
    //{
    //}

    //public HelpController(HttpConfiguration config)
    //{
    //    Configuration = config;
    //}

    public HelpController()
    {
        Configuration = GlobalConfiguration.Configuration;
    }

Upvotes: 6

Boney
Boney

Reputation: 2202

Registering the HttpConfiguration object as an instance in UnityContainer will also help resolve the issue.
Just need to add to add the below line while registering in UnityContainer.

public static void RegisterTypes(IUnityContainer container) {
    container.RegisterInstance<HttpConfiguration>(GlobalConfiguration.Configuration);
}

This will help Unity resolve the config parameter, when it invokes the constructor with the parameter.

public HelpController(HttpConfiguration config) {
    Configuration = config;
}

Upvotes: 3

Carl
Carl

Reputation: 1832

I've recently had this error happen on a previously working codebase. The following answer shows what I did to correct it:

Adding Web API and API documentation to an existing MVC project

Basically make the constructor with parameters protected rather than public

Upvotes: 1

user3221409
user3221409

Reputation: 501

I think a better way is to add InjectionConstructor attribute to the default constructor. This attribute forces unity to use the decorated constructor.

Example:

public class HelpController : Controller
{
    private const string ErrorViewName = "Error";

    [InjectionConstructor]
    public HelpController()
        : this(GlobalConfiguration.Configuration)
    {
    }

Upvotes: 40

Felipe Oriani
Felipe Oriani

Reputation: 38608

As your code sample, I'm assuming you are on a Controller and not a API Controller (from web api).

Your api controller has a dependency on the constructor from HttpConfiguration. The container problably does not have this definition for this type and consequently does not know how to solve it and the string on the error message should come from this type as a dependency. I recommend you use the GlobalConfiguration static class and access the Configuration property to get a HttpConfiguration instance. You could abstract it in a property, for sample:

// include this namespace
using System.Web.Http;

public class HelpController : Controller
{
    // remove the constructors...

    // property
    protected static HttpConfiguration Configuration
    {
        get { return GlobalConfiguration.Configuration; }
    }

    public ActionResult Index()
    {
        return View(this.Configuration.Services.GetApiExplorer().ApiDescriptions);
    }

    public ActionResult Api(string apiId)
    {
        if (!String.IsNullOrEmpty(apiId))
        {
            HelpPageApiModel apiModel = this.Configuration.GetHelpPageApiModel(apiId);
            if (apiModel != null)
            {
                return View(apiModel);
            }
        }

        return View("Error");
    }
}

Now, if you are on a Api Controller, you just can access directly the this.Configuration property that is already on the ApiController (base class for Api controllers) and get a instance of HttpConfiguration.

Upvotes: 23

Related Questions