Autofac and WebAPI - Default constructor error - asp.net

I have a webforms project, but am using WebAPI for web services. I am trying to implement Autofac. I am getting:
'MyController' does not have a default constructor
According to the Autofac documentation I have the configuration correct but obviously there is a problem. I am using Visual Studio 2010/.Net 4. Here is my Application_Start
private void Application_Start(object sender, EventArgs e)
{
//This enables api controllers in a separate class library
GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver), new AssembliesResolver());
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
//Elmah for webapi
GlobalConfiguration.Configuration.Filters.Add(new ElmahHandleErrorApiAttribute());
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
var builder = new ContainerBuilder();
//Register DbContext
builder.RegisterType<MyDbContext>()
.As<IMyDbContext>()
.InstancePerRequest();
//Register service layer
var businessLayer = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
var container = builder.Build();
_containerProvider = new ContainerProvider(container);
var webApiResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = webApiResolver;
}
A typical api controller looks like this:
[Authorize]
public class MyController : ApiController
{
public IMyService context { get; set; }
public MyController(IMyService context)
{
this.context = context;
}
// GET api/dostuff
/// <summary>
/// Get a list of all dtos
/// </summary>
/// <returns></returns>
public IEnumerable<MyDto> Get()
{
try
{
return context.MyDtos.ToList();
}
catch (Exception ex)
{
var message = string.Format("{0} {1} HTTP/1.1 {2} Exception: {3}", Request.Method, Request.RequestUri, HttpStatusCode.MethodNotAllowed, ex.Message);
var errorMessage = new System.Web.Http.HttpError(message) { { "ErrorCode", 405 } };
throw new HttpResponseException(ControllerContext.Request.CreateErrorResponse(HttpStatusCode.MethodNotAllowed, errorMessage));
}
}
}

If you have your api controllers in a different assembly than your web project, then you need to need to tell Autofac where to find your controllers with changning:
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
To
builder.RegisterApiControllers(typeof(MyController).Assembly);
This will instruct Autofac to scan the assembly of MyController and register all ApiController derived types what it founds there.

Related

How to configure dependency injection for self hosted MVC 4 API controllers

After checking all the similar questions on SO, my issue persists so I'm opening a new question for it.
I have a unit test that references anther project that contains a MVC 4 ApiController which has a constructor for dependency injection.
public class DataController : ApiController
{
public DataController(IRepository repository){}
}
In my test, I'm using Microsoft.Extensions.DependencyInjection and have the following setup:
// Note: this redundant type access is necessary to load controllers from a different assembly,
// see https://stackoverflow.com/a/11758025/1468097
var type = typeof(DataController);
var services = new ServiceCollection().AddSingleton<IRepository>(new ImMemoryRepository());
var httpConfiguration = new HttpConfiguration
{
DependencyResolver = new DependencyResolver(services.BuildServiceProvider()),
IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always
};
httpConfiguration.Routes.MapHttpRoute("Default", "{controller}/{action}");
httpConfiguration.DependencyResolver = new DependencyResolver(services.BuildServiceProvider());
var httpServer = new HttpServer(httpConfiguration);
var client = new HttpClient(httpServer);
var response = await client.GetAsync("http://whatever/data/getdata?id=000");
and I have a fairly barebone implementation of the dependency resolver as nested private class inside the test class:
private class DependencyResolver : IDependencyResolver
{
private readonly ServiceProvider _serviceProvider;
public DependencyResolver(ServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
public void Dispose() => _serviceProvider.Dispose();
public object GetService(Type serviceType) => _serviceProvider.GetService(serviceType);
public IEnumerable<object> GetServices(Type serviceType) => _serviceProvider.GetServices(serviceType);
public IDependencyScope BeginScope() => new Scope(_serviceProvider.CreateScope());
private class Scope : IDependencyScope
{
private readonly IServiceScope _scope;
public Scope(IServiceScope scope) => _scope = scope;
public void Dispose() => _scope.Dispose();
public object GetService(Type serviceType) => _scope.ServiceProvider.GetService(serviceType);
public IEnumerable<object> GetServices(Type serviceType) => _scope.ServiceProvider.GetServices(serviceType);
}
}
The response I get from the test is a 500 server error saying
Type 'Mvc.DataController' does not have a default constructor
It seems I'm doing what all the others are doing for dependency injection in MVC 4, even for this question that has a very similar symptom.
Am I missing something obvious here?
Update
I've tried NinjectResolver comes down to the same problem:
var kernel = new StandardKernel();
kernel.Bind<IRepository>().ToConstant(new InMemoryRepository());
var httpConfiguration = new HttpConfiguration
{
DependencyResolver = new NinjectResolver(kernel)
};
I figured this out.
The key is that you also need to add the controller you are hitting in your dependency configuration.
In my case, I just need to add:
var services = new ServiceCollection();
// This is what was missing.
services.AddTransient<DataController>();
Taken from here (although I'm not using OWIN at all): How to add Microsoft.Extensions.DependencyInjection to OWIN Self-Hosted WebApi
Another example using Autofac:
var builder = new ContainerBuilder();
// You need to register the assembly that contains your controller.
builder.RegisterApiControllers(typeof(DataController).Assembly);
Taken from here: Dependency injection not working with Owin self-hosted Web Api 2 and Autofac

Injecting dependency from a Class Library project to Xamarin Forms

I have got a brand new Xamarin Form project that requires access to an existing class in a class library project (net standards 2.1).
I would like Xamarin forms to use ClientQueries class from the other project.
ClientQueries has got a HttpClientFactory property using dependency injection and several methods to call an API and it looks something like this:
public class ClientQueries
{
private readonly ClientFactory _ClientFactory;
public TSClientQueries(ClientFactory ClientFactory)
{
_ClientFactory = ClientFactory ?? throw new ArgumentNullException(nameof(ClientFactory));
}
public async Task<Result<Token>> GetToken(CancellationToken cancellationToken, string username, string password)
{
var client = _ClientFactory.Create();
var response = await client.GetToken(cancellationToken, username, password).ConfigureAwait(true);
return response;
}
}
I follow this sample which explains we can make use of Microsoft.Extensions for HttpClientFactory and adding singleton services. This is the link: ASP.NET Core's Dependency Injection into Xamarin Apps with HostBuilder
I tried this StartUp class in Xamarin Forms project which is similar to the asp.net core project which uses the same Class Library with ClientQueries:
public class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static void Init()
{
var a = Assembly.GetExecutingAssembly();
using var stream = a.GetManifestResourceStream("App1.appsettings.json");
var host = new HostBuilder()
.ConfigureHostConfiguration(c =>
{
c.AddCommandLine(new string[] { $"ContentRoot={FileSystem.AppDataDirectory}" });
c.AddJsonStream(stream);
})
.ConfigureServices((c, x) => ConfigureServices(c, x))
.ConfigureLogging(l => l.AddConsole(o =>
{
//o.DisableColors = true;
}))
.Build();
ServiceProvider = host.Services;
}
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
#region "api service"
services.AddSingleton<ClientQueries>();
services.AddHttpClient<Client>("HttpClient",
x => { x.BaseAddress = new Uri(ctx.Configuration["APIConfiguration:BaseAddress"]); }
).AddPolicyHandler(GetRetryPolicy());
services.AddSingleton<ClientFactory>();
#endregion
}
Is it possible to inject ClientQueries in ViewModels like the way I used to do it in asp.net core controllers?
public class AccountController : Controller
{
private readonly ClientQueries _ClientQueries;
public AccountController(ClientQueries ClientQueries)
{
_tsClientQueries = tsClientQueries;
}
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var t = await _ClientQueries.GetToken(CancellationToken.None, model.UserName, model.Password);
[coded abbreviated for simplicity]
Or Has Prism got anything functionality which will allow me to use depedency injection for HttpClientFactory in ClientQueries and Use ClienQueries as a singleton in ViewModels in Xamarin?
I tried DependencyServices and I did not get it right.
Thanks.

Autofac Web API error Make sure that the controller has a parameterless public constructor

I am getting this error while configuring Autofac with ASP.NET WebAPI.
An error occurred when trying to create a controller of type 'UserController'. Make sure that the controller has a parameterless public constructor.
Startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var file = HostingEnvironment.MapPath("~/log4net.config");
if (file != null)
{
var configFile = new FileInfo(file);
if (configFile.Exists)
XmlConfigurator.ConfigureAndWatch(configFile);
else
BasicConfigurator.Configure();
}
else
{
BasicConfigurator.Configure();
}
var builder = new ContainerBuilder();
var config = new HttpConfiguration();
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(config);
builder.RegisterModule(new WebModule(app));
// Register your MVC controllers.
builder.RegisterControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register model binders that require DI.
builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
builder.RegisterModelBinderProvider();
// OPTIONAL: Register web abstractions like HttpContextBase.
builder.RegisterModule<AutofacWebTypesModule>();
// OPTIONAL: Enable property injection in view pages.
builder.RegisterSource(new ViewRegistrationSource());
// OPTIONAL: Enable property injection into action filters.
builder.RegisterFilterProvider();
// register config
builder.Register(ct => config).AsSelf().SingleInstance();
HelpPageConfig.Register(config);
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseAutofacMvc();
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
config.EnsureInitialized();
}
}
Global.asax.cs
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
}
WebModule.cs
public class WebModule : Module
{
private readonly IAppBuilder _app;
public WebModule(IAppBuilder app)
{
_app = app;
}
protected override void Load(ContainerBuilder builder)
{
// ---- Utilities -----------------------------------
builder.RegisterType<Log4NetLogger>()
.As<ILogger>().SingleInstance();
builder.RegisterType<DataAccessConfigurationSettings>()
.As<IDataAccessSettings>().SingleInstance();
builder.RegisterType<ApplicationServicesConfigurationSettings>()
.As<IApplicationServicesSettings>().SingleInstance();
builder.RegisterType<ValidationExceptionHandler>()
.As<IExceptionHandler<ValidationException>>().SingleInstance();
builder.RegisterType<SqlExceptionHandler>()
.As<IExceptionHandler<SqlException>>().SingleInstance();
builder.RegisterType<GeneralExceptionHandler>()
.As<IExceptionHandler<Exception>>().SingleInstance();
// ---- Business ------------------------------------
builder.RegisterType<UserBusiness>()
.As<IUserBusiness>().InstancePerRequest();
// ---- Validator -----------------------------------
builder.RegisterType<UserSignupModelValidator>()
.AsSelf().SingleInstance();
// ---- Controllers -----------------------------------
builder.RegisterType<DeflateCompressionActionFilter>()
.AsWebApiActionFilterFor<UserController>().InstancePerRequest();
base.Load(builder);
}
}
UserController.cs
[RoutePrefix("api/User")]
public class UserController : ApiController
{
private readonly IUserBusiness _userBusiness;
public UserController(IUserBusiness userBusiness)
{
_userBusiness = userBusiness;
}
...
}
I believe I'm missing some minor thing which I have no idea, any help would be appreciated.
Have a look on this answer: MVC Web API not working with Autofac Integration
Maybe you just need to do set you container like it was described on the link above:
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver =
new AutofacWebApiDependencyResolver(container);
Theoretically you get a different exception but it could also be the case.

Autofac in MVC 5

I'm trying to learn how to use autofac in MVC and not having a great deal of luck.
I installed Autofac.mvc5 from Nuget version 4.01
and Autofac v4.6.1
I have the following controller:
[Authorize]
public class NotificationsController : ApiController
{
private ApplicationDbContext _context;
private readonly IMapper _mapper;
public NotificationsController(IMapper notificationMapper)
{
_context = new ApplicationDbContext();
_mapper = notificationMapper;
}
public IEnumerable<NotificationDto>GetNewNotifications()
{
var userId = User.Identity.GetUserId();
var notifications = _context.UserNotifications
.Where(un => un.UserId==userId)
.Select(un=>un.Notification)
.Include(n=>n.Gig.Artist).ToList();
return notifications.Select(notification => _mapper.Map<NotificationDto>(notification)).ToList();
}
}
My Global.Asax is:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
ConfigureAutofac();
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void ConfigureAutofac()
{
var autoMapperConfig = new MapperConfiguration(c =>
{
c.AddProfile(new NotificationProfile());
});
var mapper = autoMapperConfig.CreateMapper();
var builder = new ContainerBuilder();
builder.RegisterInstance(mapper);
builder.Register(x => new NotificationsController(x.Resolve<IMapper>()));
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
What I'm finding is that when I call this using postman I get an error saying I need a parameterless consturctor, but then if I put in the constructor _mapper is null.
Could someone point me in the right direction please?
Your controller derives from ApiController, so you are not using MVC, you are using WebApi (I assume WebApi2). So, you should use either Controller and MVC, or install Autofac.WebApi2 NuGet package to handle WebApi2.
Also, instead of registering all your controllers manually, you could use RegisterControllers() for MVC or RegisterApiControllers() for WebApi to have all your controllers registered at once. Especially, when your NotificationController does not use any fancy injection to require more custom registration.
The documentation describes very well, how you should use Autofac with MVC or WebApi, you should start there.

MVC5+ WebAPI+ Owin+ Ninject

I have a project where I use MVC5 with WebAPI. Authentication uses Owin. I want to setup Ninject dependency resolver. I tried soultions for MVC5, for MVC5 with Owin, for WebApi for WebAPI with Owin. But I can't combine them. Does anybody have the steps for MVC5+WebApi+Owin+Ninject bundle?
One of the last solution to make it workable for WebApi I followed here:
https://github.com/ninject/Ninject.Web.Common/wiki/Setting-up-a-OWIN-WebApi-application
I added latest NuGet packages.
My Startup class:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var webApiConfiguration = new HttpConfiguration();
WebApiConfig.Register(webApiConfiguration);
app.UseNinjectMiddleware(CreateKernel).UseNinjectWebApi(webApiConfiguration);
ConfigureAuth(app);
}
private static StandardKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
return kernel;
}
}
WebApiConfig. I didn't add any routing configurations. As it is added in Global.asax:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var webApiConfiguration = new HttpConfiguration();
app.UseNinjectMiddleware(CreateKernel).UseNinjectWebApi(webApiConfiguration);
ConfigureAuth(app);
}
private static StandardKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
return kernel;
}
}
WebApi with routings(and removed routing registration in Global.asax) it doesn't work too. On each request to ANY WebApi I have "{"Message":"Authorization has been denied for this request."}":
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var webApiConfiguration = new HttpConfiguration();
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
webApiConfiguration.SuppressDefaultHostAuthentication();
webApiConfiguration.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
webApiConfiguration.MapHttpAttributeRoutes();
webApiConfiguration.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
app.UseNinjectMiddleware(CreateKernel).UseNinjectWebApi(webApiConfiguration);
ConfigureAuth(app);
}
private static StandardKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
return kernel;
}
}
ConfigureOAuth. I'm using two way authentication one based on cookie another based on Tokne. One suitable for WebApi another for MVC:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<UserManager, User>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager,DefaultAuthenticationTypes.ApplicationCookie))
}
});
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
And my API controller.
[Authorize(Roles = "Admin")]
public class AdminPanelController : ApiController
{
private readonly IAdminPanelService _service;
public AdminPanelController(IAdminPanelService service)
{
_service = service;
}
}
I always have exception that AdminPanelController should be parameterless. If I add any routing to WebApiConfig I will have the same exception for AdminPanelController and not authorized for other(though Bearer Token generates and pass to WebApi controllers)
To get Dependency Injection to work with Web API you should implemented an IDependencyResolver for Ninject and apply it to HttpConfiguration.DependencyResolver.
See more here. You can even find a NuGet package here.

Resources