I have added MediatR to OnApplicationStarted in global.asax
But it's not resolving for my controller.
It returns an error:
{
"Message": "An error has occurred.",
"ExceptionMessage": "An error occurred when trying to create a controller of type 'NotificationApiController'. Make sure that the controller has a parameterless public constructor.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Type 'MyDomain.MyProject.Controllers.NotificationApiController' does not have a default constructor",
"ExceptionType": "System.ArgumentException",
"StackTrace": " at System.Linq.Expressions.Expression.New(Type type)\r\n at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
}
}
The global.asax:
var builder = new ContainerBuilder();
/* MVC Controllers */
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterAssemblyModules(Assembly.GetExecutingAssembly());
builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
builder.RegisterModelBinderProvider();
/* WebApi Controllers */
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
/* Umbraco Controllers */
builder.RegisterControllers(typeof(UmbracoApplication).Assembly);
builder.RegisterApiControllers(typeof(UmbracoApplication).Assembly);
/* Custom Api Controllers */
builder.RegisterApiControllers(typeof(Controllers.SearchResultsApiController).Assembly);
builder.RegisterModule<WebApiConfig>();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver =
new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.Always;
WebApiConfig:
public class WebApiConfig : Module
{
protected override void Load(ContainerBuilder builder)
{
// Register custom types with Autofac
/* Third-party types */
// This didn't work so I added the below line with the explicit handler
builder.AddMediatR(this.GetType().Assembly);
// But it didn't make any difference
builder.AddMediatR(typeof(Index).Assembly);
/* Umbraco context types */
ApplicationContext applicationContext = ApplicationContext.Current;
builder.RegisterInstance(applicationContext.Services.ContentService)
.As<IContentService>();
builder.RegisterInstance(applicationContext.Services.MemberService)
.As<IMemberService>();
//builder.Register(c => UmbracoContext.Current).AsSelf();
builder.Register(c => UmbracoContext.Current).InstancePerRequest();
builder.Register(x => new UmbracoHelper(UmbracoContext.Current))
.InstancePerRequest();
}
}
The controller:
public class SearchResultsApiController : UmbracoApiController
{
private readonly IMediator _mediator;
public SearchResultsApiController(IMediator mediator)
{
_mediator = mediator;
}
}
I'm using .NET 4.7.2 (and Umbraco 7.15.3 if that matters).
The issue was that I had api controllers in two separate projects so probably the resolver couldn't find the right one.
If api controllers in one project were working, the controllers in the second project were failing showing the above error.
I have consolidated all api controllers in one project and now everything works fine.
Related
I have an ASP.NET Web API, .Net 4.6.1 project where I need to capture some info in the middleware and then retrieve it in a code that will be called from controller. In .Net core it is very easy with registering my custom context class as Scoped and resolving it in different stages of message processing. In .Net Framework, what looked similar to it was Autofac's InstancePerRequest so I tried but it does not work as I expected. Apparently every time I do BeginScope() it returns a new instance even if I am within same reuest? I am implementing the IAutofacContinuationActionFilter interface where I resolve my service, registered with InstancePerRequest, then later in controller I try to resolve it again and get new instance. What am I missing here?
Oh and in the controller below, both instances of IHomeService: injected via constructor and resolved manually are creating new instances.
UPDATE:
The code above is oversimplification of the real situation. The call where I need the information passed from filter is in a separate class and call happens through a series of autogenerated code. The constructor injection is not an option for me so I was hoping to have a solution similar to .Net Core DI.
my WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var container = MyContainerBuilder.Build(config);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
my container builder class:
public class MyContainerBuilder
{
public static IContainer Build(HttpConfiguration config)
{
var builder = new ContainerBuilder();
builder.RegisterWebApiFilterProvider(config);
builder
.Register(c => new MyCustomFilter())
.AsWebApiActionFilterForAllControllers()
.InstancePerRequest();
// var assembly = typeof(IHomeService).Assembly;
// builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
builder
.RegisterType<HomeService>()
.As<IHomeService>()
.InstancePerRequest();
return builder.Build();
}
}
filter:
public class MyCustomFilter : IAutofacContinuationActionFilter
{
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> next)
{
using (var scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope().GetRequestLifetimeScope())
{
var hs = scope.Resolve<IHomeService>();
++hs.Counter;
var hs1 = scope.Resolve<IHomeService>();
++hs1.Counter;
var r = next().Result;
return await Task.FromResult(r);
}
}
}
controller:
[Route("home")]
public class HomeController : ApiController
{
public IHomeService HomeService { get; set; }
public HomeController(IHomeService homeService)
{
HomeService = homeService;
}
[HttpGet]
[Route("")]
public string Index()
{
var dr = GlobalConfiguration.Configuration.DependencyResolver as AutofacWebApiDependencyResolver;
using (var scope = dr.GetRequestLifetimeScope())
// This does not work either, returns new instance:
// using (var scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope().GetRequestLifetimeScope())
{
var hs = scope.Resolve<IHomeService>();
++hs.Counter;
}
return "Home";
}
}
service class I try to resolve:
public interface IHomeService
{
int Counter { get; set; }
}
public class HomeService : IHomeService
{
public HomeService()
{
Console.WriteLine("Yet another instance of HomeService!!!");
}
public int Counter { get; set; }
}
Thanks in advance
You can't create your own request scope, you need to get it from the request message. In the filter, that's like:
var scope = actionContext.Request.GetDependencyScope();
An example filter showing this is in the Autofac docs
However, since you're using Autofac interfaces, they're injected by Autofac for each request - if your filter needs a per-request service, it's better to make it a constructor parameter on the filter. You only need to do service location if you're not using Autofac filter interfaces.
If you seriously need service location in the filter, you can still use the constructor to make things easier - add an ILifetimeScope parameter to the filter constructor and you'll get the request scope as a parameter.
For the controller, same thing: inject what you need in the constructor rather than using service location. If you need the request scope because you can't escape service location, either inject an ILifetimeScope into the controller constructor or get the request lifetime off the HttpRequestMessage.
How to get the object of ITelemety object in an ASP.NET Core custom ActionFilterAttribute using dependency injection?
Platform - .Net Core
What is required : ITelemetry object
Sample Code for reference
public class CustomActionFilterAttribute : ActionFilterAttribute
{
private readonly ITelemetry telemetry;
public CustomActionFilterAttribute(ITelemetry telemetry)
{
this.telemetry= telemetry;
}
}
I need to resolve ITelemetry object to add few Global Properties in the request context so that those can be logged in Request log. With above sample code, an exception is thrown when ITelemetry object is not being resolved by DI.
The CustomActionFilterAttribute is injected as shown below in startup.
services.AddMvc(options =>
{
options.Filters.Add<CustomActionFilterAttribute>();
});
A custom exception that requires the use of an Autowired variable
// #Component // should be a component to use autowire variable
#ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public class MyException extends Exception {
// This should be commented in
// #Autowired
// public RequestModel reqModel;
// public MyException(ExceptionType type) {
public MyException(ExceptionType type, RequestModel reqModel) {
super();
switch type {
// ...
}
}
}
And then in the rest controllers and interceptors we throw the error
public class MyInterceptor extends HandlerInterceptorAdapter {
// These should be commented out
#Autowired
public RequestModel reqModel;
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// This should be thrown without adding the reqModel as a param
// Unsure how to throw this without using "new"
throw new MyException.MyException(PARAM_MISSING, reqModel);
}
}
I understand that instantiating a class using new will null the Autowired variables. When I tried dropping the new and converting the MyException class to a #Component, I see issues with calling the MyException class constructor.
What is the best way to implement a custom exception in springboot using an autowired variable?
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.
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.