Autofac, ASP.NET MVC 3 httpRequest scope and AutoMapper: No scope with a Tag matching 'httpRequest' is visible - asp.net-mvc-3

When I use a web type registered with autofac from an automapper mapping, I get this error:
No scope with a Tag matching 'httpRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being reqested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.
When another type is resolved in the mapping it works.
When a web type is resolved from the controller it works.
Why doesnt web (or any other httprequest scoped?) types get successfully resolved in my mapping?
protected void Application_Start()
{
var builder = new ContainerBuilder();
builder.RegisterModule<AutofacWebTypesModule>();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AssignableTo<Profile>()
.As<Profile>()
;
builder.Register(c => Mapper.Engine)
.As<IMappingEngine>();
builder.RegisterType<AnotherType>()
.As<IAnotherType>();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
var profiles = container.Resolve<IEnumerable<Profile>>();
Mapper.Initialize(c => profiles.ToList().ForEach(c.AddProfile));
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
public class HomeController : Controller
{
private readonly IMappingEngine _mapper;
private readonly Func<HttpContextBase> _httpContext;
public HomeController(IMappingEngine mapper, Func<HttpContextBase> httpContext)
{
_mapper = mapper;
_httpContext = httpContext;
}
public ActionResult Index()
{
var test = _httpContext.Invoke();
return View(_mapper.Map<Model, ViewModel>(new Model()));
}
}
public class MyProfile : Profile
{
private readonly Func<HttpContextBase> _httpContext;
private readonly Func<IAnotherType> _anotherType;
public MyProfile(Func<HttpContextBase> httpContext, Func<IAnotherType> anotherType)
{
_httpContext = httpContext;
_anotherType = anotherType;
}
protected override void Configure()
{
CreateMap<Model, ViewModel>()
.ForMember(d => d.Url, o => o.ResolveUsing(s =>
{
var test = _anotherType.Invoke().GetAValue();
return _httpContext.Invoke().Request.Url;
}))
;
}
}
public interface IAnotherType
{
string GetAValue();
}
public class AnotherType : IAnotherType
{
public string GetAValue() { return "a value"; }
}
public class ViewModel
{
public string Url { get; set; }
}
public class Model
{
}
EDIT: Its easy to create an empty MVC project, paste the code and try it out and see for yourself.
EDIT: Removed the ConstructServicesUsing call because its not required by the example. No services are resolved through AutoMapper in the example.

#rene_r above is on the right track; adapting his answer:
c.ConstructServicesUsing(t => DependencyResolver.Current.GetService(t))
Still might not compile but should get you close.
The requirement is that the call to DependencyResolver.Current is deferred until the service is requested (not kept as the value returned by Current when the mapper was initialised.)

I think you should use DependencyResolver.Current.Resolve instead of container.Resolve in
Mapper.Initialize(c =>
{
c.ConstructServicesUsing(DependencyResolver.Current);
profiles.ToList().ForEach(c.AddProfile);
});

I recently had a similar problem and it turned out to be a bad setup in my bootstrapper function. The following autofac setup did it for me.
builder.Register(c => new ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers))
.AsImplementedInterfaces()
.SingleInstance();
builder.Register(c => Mapper.Engine)
.As<IMappingEngine>()
.SingleInstance();
builder.RegisterType<TypeMapFactory>()
.As<ITypeMapFactory>()
.SingleInstance();
I did not have to specify resolver in the Mapper.Initialize() function. Just called
Mapper.Initialize(x =>
{
x.AddProfile<DomainToDTOMappingProfile>();
});
after the bootstrapped and it works fine for me.

Related

Register and Resolving Dependencies on Request Based on Param (Autofac WEB API)

I have an Application with autofac dependency injection and I wanted to use a specific dll extension based on the parameter I have on the request.
Here's my global.asax where I initialize autofac.
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterApiControllers(Assembly.GetExecutingAssembly());
containerBuilder.RegisterModule<ExModule>();
var container = containerBuilder.Build();
container.Resolve<IArtigoErp>();
Here's the autofac module where I load register my DLL's
public class ExModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
//Load DLL1 from folder and register it
RegistaDepedencias<IArtigoErp>(builder, "DLL1");
//Load DLL2 from folder and register it
RegistaDepedencias<IArtigoErp>(builder, "DLL2");
}
private void RegistaDepedencias<T>(ContainerBuilder builder, string NomeDll)
{
RegisterDep<T>(GetEnumerableTypes<T>(NomeDll), builder);
}
private void RegisterDep<T>(IEnumerable<Type> types, ContainerBuilder builder)
{
foreach (var t in types)
{
builder.RegisterType(t).As<T>();
}
}
private IEnumerable<Type> GetEnumerableTypes<T>(string NomeDll)
{
return Directory.EnumerateFiles(Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "Engine"))
.Where(x => x.Contains(NomeDll) && x.EndsWith(NomeDll +".dll"))
.Select(x => Assembly.LoadFrom(x))
.SelectMany(x => x.GetTypes()
.Where(t => typeof(T).IsAssignableFrom(t) && t.IsClass));
}
}
Both my DLL's have a class that extend from IArtigoErp.
So the ideia is, based on the parameter I get on my request, I want to run the method in either DLL1 or DLL2.
Example:
if(param == 1)
_artigoErp.GetLista(); // In DLL1
if(param == 2)
_artigoErp.GetLista(); // In DLL2
EDIT 1:
The parameter comes from the post method as it follows (Guid IdLoja)
public class ArtigoController : ApiController
{
private readonly IArtigoErp _artigoErp;
private readonly IArtigoLoja _artigoLoja;
public ArtigoController(IArtigoErp artigoErp, IArtigoLoja artigoLoja)
{
_artigoErp = artigoErp;
_artigoLoja = artigoLoja;
}
[Route("PostArtigos")]
public CallResponse PostArtigos([FromBody] Guid IdLoja)
{
}
}
I guess we can also process this in the begin_request method in global.asax
Thanks in advance.

webapi - dbcontext injection into fluentvalidation validator

Hello I am having a hard time passing an HTTP scoped object into a fluent validation validator. Everything works until I try to inject a service and then it throws the following error
No scope with a Tag matching 'AutofacWebRequest' is visible from the
scope in which the instance was requested. This generally indicates
that a component registered as per-HTTP request is being requested by
a SingleInstance() component
It comes down to the below snippet I think and I don't know how to mimic it in webapi
public Func<T> HttpRequestScopedFactoryFor<T>(){
return () => system.Web.Mvc.DependencyResolver.Current.GetService<T>();
}
IN MVC my autofac code looked like so
public class FluentValidationModule : Module
{
public Func<T> HttpRequestScopedFactoryFor<T>()
{
return () => System.Web.Mvc.DependencyResolver.Current.GetService<T>();
}
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterType<ApplicationDbContext>().AsSelf().InstancePerRequest();
builder.RegisterInstance(HttpRequestScopedFactoryFor<ApplicationDbContext>());
}
}
}
My FluentValidation validator code looked like so
public class RoleDTOValidator : AbstractValidator<RoleDTO>
{
readonly Func<ApplicationDbContext> _dbFactory;
public RoleDTOValidator(Func<ApplicationDbContext> dbFactory)
{
_dbFactory = dbFactory;
RuleFor(m => m.Id).NotEmpty();
RuleFor(m => m.Name).NotEmpty().Must(DoesNotExist).WithMessage("Another permission exists with that name."); ;
RuleFor(m => m.Description).NotEmpty();
RuleFor(m => m.Permissions).NotEmpty().SetCollectionValidator(new ApplicationRoleClaimValidator());
}
bool DoesNotExist(RoleDTO model, string name)
{
ApplicationDbContext db = _dbFactory();
int count = db.Roles.Count(item => item.Name == name);
return count == 0;
}
}
Any help is much appreciated.

Custom route constraint causes intermittent 404 errors

I have an Asp.Net Core 1 RC1 application that uses a custom route constraint to control access to the application. The application (hosted on a server running IIS 7.5) is getting intermittent 404 errors which I suspect is caused by this routing constraint. Here you can see a screenshot that shows the intermittent 404 errors:
I suspect that this issue is related to the code that defines the route constraint not being thread-safe. The custom route constraint needs a DbContext because it needs to check in the database if the application is enabled for the brand specified in the route, and I suspect that this DbContext instance could be causing the issue. Here is how the routing is defined in the application:
// Add MVC to the request pipeline.
var appDbContext = app.ApplicationServices.GetRequiredService<AppDbContext>();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "branding",
template: "branding/{brand}/{controller}/{action}/{id?}",
defaults: new { controller="Home", action="Index" },
constraints: new { brand = new BrandingRouteConstraint(appDbContext) });
});
And here is the custom route constraint:
// Custom route constraint
public class BrandingRouteConstraint : IRouteConstraint
{
AppDbContext _appDbContext;
public BrandingRouteConstraint(AppDbContext appDbContext) : base() {
_appDbContext = appDbContext;
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey, IDictionary<string, object> values, RouteDirection routeDirection)
{
if (values.Keys.Contains(routeKey))
{
var whiteLabel = _appDbContext.WhiteLabels.Where(w => w.Url == values[routeKey].ToString()).FirstOrDefault();
if (whiteLabel != null && whiteLabel.EnableApplication != null && (bool)whiteLabel.EnableApplication)
{
return true;
}
}
return false;
}
}
Can anyone confirm that this issue is caused by the code not being thread-safe and recommend a way to change the implementation so that it is thread-safe?
I can't comment on RouteContraint's, haven't used them much, but have you tried Resource Based Authorization instead? Looks like it might be more suited to what you're trying to achieve?
From here and here:
Request authentication service inside your controller
public class DocumentController : Controller
{
IAuthorizationService authorizationService;
public DocumentController(IAuthorizationService authorizationService)
{
this.authorizationService = authorizationService;
}
}
Apply authorization checks in your Action:
public async Task<IActionResult> Edit(Guid documentId)
{
Document document = documentRepository.Find(documentId);
if (document == null)
{
return new HttpNotFoundResult();
}
if (await authorizationService.AuthorizeAsync(User, document, Operations.Edit))
{
return View(document);
}
else
{
return new HttpUnauthorizedResult();
}
}
I've used the OperationAuthorizationRequirement class in the sample, so define this class in your project:
public static class Operations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement { Name = "Create" };
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement { Name = "Read" };
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement { Name = "Update" };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = "Delete" };
}
Implement the authorization handler (using built in OperationAuthorizationRequirement requirement):
public class DocumentAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
protected override void Handle(AuthorizationContext context,
OperationAuthorizationRequirement requirement,
Document resource)
{
// Validate the requirement against the resource and identity.
// Sample just checks "Name"field, put your real logic here :)
if (resource.Name == "Doc1")
context.Succeed(requirement);
else
context.Fail();
}
}
And not forgetting ConfigureServices:
services.AddInstance<IAuthorizationHandler>(
new DocumentAuthorizationHandler());
It's a bit more work, but adds quite a lot of flexibility.

Structure Map parameterless constructor error

I am trying to set up structure map ver 3.0.5.0 with Web API 2.
I have followed this implementation: Configuring dependency injection with ASP.NET Web API 2.1
However, I am getting this error when doing a get against my ComplexesController:
An error occurred when trying to create a controller of type 'ComplexesController'. Make sure that the controller has a parameterless public constructor.
Can anyone see what is wrong with my structuremap config? The Create method never gets called.
This is my implementation:
public class StructureMapControllerActivator : IHttpControllerActivator
{
private readonly IContainer _container;
public StructureMapControllerActivator(IContainer container)
{
if (container == null) throw new ArgumentNullException("container");
_container = container;
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
try
{
var scopedContainer = _container.GetNestedContainer();
scopedContainer.Inject(typeof(HttpRequestMessage), request);
request.RegisterForDispose(scopedContainer);
return (IHttpController)scopedContainer.GetInstance(controllerType);
}
catch (Exception e)
{
// TODO : Logging
throw e;
}
}
}
This method is in my startup...
public void InitializeContainer()
{
// STRUCTURE MAP
Container container = new Container();
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new StructureMapControllerActivator(container));
container.Configure(x => x.For<IForumRepository>().Use<ForumRepository>());
container.Configure(x => x.For<IComplexRepository>().Use<ComplexRepository>());
}
.. and this is the controller:
public class ComplexesController : ApiController
{
private IComplexRepository _repo;
public ComplexesController(IComplexRepository repo)
{
_repo = repo;
}
// GET: api/Complexes
public IList<Complex> GetComplexes()
{
var complexes = _repo.GetList();
return complexes;
}
...
My full Startup class
[assembly: OwinStartup(typeof(AngularJSAuthentication.API.Startup))]
namespace AngularJSAuthentication.API
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
}
}
The problem here is that you are registering your service activator with a GlobalConfiguration object and not your HttpConfiguration object. In this scenario The GlobalConfiguration object is never used as it is replaced by the HttpConfiguration object. In order to solve your issue you should replace your InitializeContainer() method with the following.
public void InitializeContainer(HttpConfiguration config)
    {
        // STRUCTURE MAP
        Container container = new Container();
        config.Services.Replace(typeof(IHttpControllerActivator), new StructureMapControllerActivator(container));
        container.Configure(x => x.For<IForumRepository>().Use<ForumRepository>());
        container.Configure(x => x.For<IComplexRepository>().Use<ComplexRepository>());        
    }
you should then pass the HttpConfiguration object from your Startup class to the new InitializeContainer() method.
Hope this helps.
-B
I am trying to gain a solid understanding of the complete lifecycle. I think my setup may be slightly different to the above. Here is what worked for me.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
var container = IocConfig.Setup();
// Allow a controller to be declared without a parameterless constructor
config.DependencyResolver = new DependencyResolver(container);
config.Services.Add( typeof(IExceptionLogger), new GlobalExceptionLogger( container.GetInstance<ILoggingService>()));
// Web API routes
config.MapHttpAttributeRoutes();
// Setup Authentication
ConfigureOAuth(app, container);
var corsOptions = CorsOptions.AllowAll;
app.UseCors(corsOptions);
// Add ASP.Net Web API to OWIN pipeline
app.UseWebApi(config);
}
}
It worked after I added this line:
// Allow a controller to be declared without a parameterless constructor
config.DependencyResolver = new DependencyResolver(container);
You have to get that my var container loads from a static class called IocConfig with a static Setup method. This is where the interfaces are mapped to their concrete implementations.
Also, you can probably ignore the GlobalExceptionLogger line if you want to use my complete example.

StructureMap Exception Code: 202

everyone, I have problems when using MVC3 code is as follows
public SystemController(IRepository repository)
:this
(
repository,
new AspNetMembershipProviderWrapper(System.Web.Security.Membership.Provider),
new AspNetMembershipProviderWrapper(System.Web.Security.Membership.Provider),
new AspNetRoleProviderWrapper(Roles.Provider),
new SmtpClientProxy(new SmtpClient(Utils.Setting.EmailServer,
int.Parse(Utils.Setting.EmailPort))
{
EnableSsl = true,
UseDefaultCredentials = true,
Credentials = new NetworkCredential(Utils.Setting.EmailAccount,
Utils.Setting.EmailPassword),
DeliveryMethod = SmtpDeliveryMethod.Network
})
){}
public SystemController(IRepository repository,
IUserService userService,
IPasswordService passwordService,
IRolesService rolesService,
ISmtpClient smtpClient)
: base(repository)
{
_userService = userService;
_passwordService = passwordService;
_rolesService = rolesService;
_smtpClient = smtpClient;
}
public class SmtpClientProxy : ISmtpClient
{
private readonly SmtpClient _smtpClient;
public SmtpClientProxy(SmtpClient smtpClient)
{
_smtpClient = smtpClient;
}
#region ISmtpClient Members
public void Send(MailMessage mailMessage)
{
_smtpClient.Send(mailMessage);
}
#endregion
}
ObjectFactory.Initialize(x =>
{
x.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.WithDefaultConventions();
});
x.For<ISessionFactory>()
.Singleton()
.Use(GetSessionFactory());
x.For<ISession>()
.HybridHttpOrThreadLocalScoped()
.Use(y => y.GetInstance<ISessionFactory>().OpenSession());
x.For<IUserService>()
.Use<AspNetMembershipProviderWrapper>();
x.For<IPasswordService>()
.Use<AspNetMembershipProviderWrapper>();
x.For<IPasswordService>()
.Use<AspNetMembershipProviderWrapper>();
x.For<IRolesService>()
.Use<AspNetRoleProviderWrapper>();
x.For<ISmtpClient>()
.Use<SmtpClientProxy>().Ctor<SmtpClient>();
x.For<MembershipProvider>()
.Use(System.Web.Security.Membership.Provider);
x.For<RoleProvider>()
.Use(Roles.Provider);
});
Error info:
StructureMap Exception Code: 202
No Default Instance defined for PluginFamily System.Net.Mail.SmtpClient, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
I think the problem lies in this:
x.For <ISmtpClient> ()
. Use <SmtpClientProxy> (). Ctor <SmtpClient> ();
I ask you how to write it?
Your SmtpClientProxy class requires an SmtpClient class in its constructor. You don't have anything registered for SmtpClient.
Try adding this to your registration:
x.For<SmtpClient>().Use<SmtpClient>();
This assumes that SmtpClient does not take dependencies in its constructor. If it does you will likely get an error that one of its dependencies are not registered with a default implementation.
Alternatively you could change the constructor code to this (no constructor dependency):
private readonly SmtpClient _smtpClient = new SmtpClient();
public SmtpClientProxy()
{
}
Without knowing what you are trying to do, it's hard to answer definitively.

Resources