webapi - dbcontext injection into fluentvalidation validator - asp.net-web-api

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.

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.

NRules Issue: Rule Matching on super class dont work

i have a class C, which inherits from A.
Now i want to write a Rules, that matches on type A. So I tried the following code:
public class tmp : Rule
{
public override void Define()
{
A t = null;
When().Match<A>(() => t);
Then().Do(ctx => Console.WriteLine("test"));
}
}
But this don't work. Can anyone explain, how I can fix this issue?
This doesnt work.
Best regards
Matching on the parent class definitely does work in NRules. Here is a full working example.
public class A
{ }
public class C : A
{ }
public class TestRule : Rule
{
public override void Define()
{
A fact = null;
When()
.Match(() => fact);
Then()
.Do(x => Console.WriteLine("Test"));
}
}
public class Program
{
static void Main(string[] args)
{
var repo = new RuleRepository();
repo.Load(x => x.From(xx => xx.AssemblyOf<Program>()));
var factory = repo.Compile();
var session = factory.CreateSession();
session.Insert(new C());
session.Fire();
}
}
If you post the rest of your code, I can update this answer and show where the issue is.
Maybe you didn't insert the fact into the session, or maybe you didn't call the Fire method, or maybe you didn't include the rule when compiling the session factory.

.Net Core DI to filter class from startup

How do I inject the database to my TestAttribute class as .net core does magically to my controller.
services.AddScoped<DbContextOptions>(p => p.GetRequiredService<DbContextOptions<Context>>());
services.TryAdd(new ServiceDescriptor(typeof(Context), typeof(Context), ServiceLifetime.Transient));
services.AddMvc(options =>
{
options.Filters.Add(new TestAttribute(/*need parameter*/));
}
public HouseController([FromServices] Context database)
{
this.Database = database;
}
public class TestAttribute : ActionFilterAttribute
{
public TestAttribute([FromServices] Context database)
{
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
}
}
To inject dependencies into Filters you need to use the ServiceFilter attribute (see docs)
Manually injecting it to an controller action
[ServiceFilter(typeof(TestAttribute))]
public Task<IActionResult> Index()
{
...
}
Also your registration is wrong in the startup for all actions! Filters.Add() accepts a Type, when you want to use attributes which require dependencies (because you can't instantiate them in code), see "Filter Scopes" in the docs.
services.AddMvc(options =>
{
options.Filters.Add(typeof(TestAttribute));
}

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.

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

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.

Resources