I'm using Web Api 2, Autofac, and MediatR (CQRS). I have a mediator pipeline in place that has pre/post request handlers. That all works fine. I'm trying to hook up Validation now and decorate the pipeline with it.
Here is my Autofac DI code:
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
FluentValidationModelValidatorProvider.Configure(config);
ConfigureDependencyInjection(app, config);
WebApiConfig.Register(config);
app.UseWebApi(config);
}
private static void ConfigureDependencyInjection(IAppBuilder app, HttpConfiguration config)
{
var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces();
builder.Register<SingleInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => c.Resolve(t);
});
builder.Register<MultiInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
});
//register all pre handlers
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.As(type => type.GetInterfaces()
.Where(interfacetype => interfacetype.IsClosedTypeOf(typeof(IAsyncPreRequestHandler<>))))
.InstancePerLifetimeScope();
//register all post handlers
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.As(type => type.GetInterfaces()
.Where(interfacetype => interfacetype.IsClosedTypeOf(typeof(IAsyncPostRequestHandler<,>))))
.InstancePerLifetimeScope();
//register all async handlers
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.As(type => type.GetInterfaces()
.Where(interfaceType => interfaceType.IsClosedTypeOf(typeof(IAsyncRequestHandler<,>)))
.Select(interfaceType => new KeyedService("asyncRequestHandler", interfaceType)))
.InstancePerLifetimeScope();
//register pipeline decorator
builder.RegisterGenericDecorator(
typeof(AsyncMediatorPipeline<,>),
typeof(IAsyncRequestHandler<,>),
"asyncRequestHandler")
.Keyed("asyncMediatorPipeline", typeof(IAsyncRequestHandler<,>))
.InstancePerLifetimeScope();
//register validator decorator
builder.RegisterGenericDecorator(
typeof(ValidatorHandler<,>),
typeof(IAsyncRequestHandler<,>),
"asyncMediatorPipeline")
.InstancePerLifetimeScope();
// Register Web API controller in executing assembly.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
//register RedStripeDbContext
builder.RegisterType<RedStripeDbContext>().As<IRedStripeDbContext>().InstancePerRequest();
builder.RegisterType<AutofacServiceLocator>().AsImplementedInterfaces();
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// This should be the first middleware added to the IAppBuilder.
app.UseAutofacMiddleware(container);
// Make sure the Autofac lifetime scope is passed to Web API.
app.UseAutofacWebApi(config);
}
Here is the ValidatorHandler:
public class ValidatorHandler<TRequest, TResponse> : IAsyncRequestHandler<TRequest, TResponse> where TRequest : IAsyncRequest<TResponse>
{
private readonly IAsyncRequestHandler<TRequest, TResponse> _inner;
private readonly IValidator<TRequest>[] _validators;
public ValidatorHandler(
IAsyncRequestHandler<TRequest, TResponse> inner,
IValidator<TRequest>[] validators)
{
_inner = inner;
_validators = validators;
}
public async Task<TResponse> Handle(TRequest request)
{
var context = new ValidationContext(request);
var failures = _validators
.Select(v => v.Validate(context))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
if (failures.Any())
throw new ValidationException(failures);
return await _inner.Handle(request);
}
}
Here is a sample query:
[Validator(typeof(GetAccountRequestValidationHandler))]
public class GetAccountRequest : IAsyncRequest<GetAccountResponse>
{
public int Id { get; set; }
}
Here is the fluent validation handler:
public class GetAccountRequestValidationHandler : AbstractValidator<GetAccountRequest>
{
public GetAccountRequestValidationHandler()
{
RuleFor(m => m.Id).GreaterThan(0).WithMessage("Please specify an id.");
}
public Task Handle(GetAccountRequest request)
{
Debug.WriteLine("GetAccountPreProcessor Handler");
return Task.FromResult(true);
}
}
Here is the request handler:
public class GetAccountRequestHandler : IAsyncRequestHandler<GetAccountRequest, GetAccountResponse>
{
private readonly IRedStripeDbContext _dbContext;
public GetAccountRequestHandler(IRedStripeDbContext redStripeDbContext)
{
_dbContext = redStripeDbContext;
}
public async Task<GetAccountResponse> Handle(GetAccountRequest message)
{
return await _dbContext.Accounts.Where(a => a.AccountId == message.Id)
.ProjectToSingleOrDefaultAsync<GetAccountResponse>();
}
}
Finally here is the Web Api 2 HttpGet method:
[Route("{id:int}")]
[HttpGet]
public async Task<IHttpActionResult> GetById([FromUri] GetAccountRequest request)
{
var model = await _mediator.SendAsync<GetAccountResponse>(request);
return Ok(model);
}
I put breakpoints all over the place and when I hit this endpoint, the first thing I get into is the GetAccountRequestValidationHandler. Then I get into the ValidatorHandler's constructor. The problem is, the IValidator[] validators parameter to the constructor is always null.
I must be missing something with fluent validation and its registration via Autofac? Any help is much appreciated.
The validator types must be registered in the IoC. Adding the below to your ConfigureDependencyInjection method should do it.
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(t => t.Name.EndsWith("ValidationHandler"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
Related
I had previously asked a question that was answered properly, but the problem is that when my custom AuthenticationStateProvider is registered as a scoped
services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
I get the following error:
System.InvalidOperationException: GetAuthenticationStateAsync was called before SetAuthenticationState
But, when it is registered as a singleton, it works correctly, However, the single instance creates for the lifetime of the application domain by AddSingelton, and so this is not good.(Why? Because of :))
What should I do to register my custom AuthenticationStateProvider as a scoped, but its value is not null?
Edit:
According to #MrC aka Shaun Curtis Comment:
It's my CustomAuthenticationStateProvider:
public class CustomAuthenticationStateProvider : RevalidatingServerAuthenticationStateProvider
{
private readonly IServiceScopeFactory _scopeFactory;
public CustomAuthenticationStateProvider(ILoggerFactory loggerFactory, IServiceScopeFactory scopeFactory)
: base(loggerFactory) =>
_scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
protected override TimeSpan RevalidationInterval { get; } = TimeSpan.FromMinutes(30);
protected override async Task<bool> ValidateAuthenticationStateAsync(
AuthenticationState authenticationState, CancellationToken cancellationToken)
{
// Get the user from a new scope to ensure it fetches fresh data
var scope = _scopeFactory.CreateScope();
try
{
var userManager = scope.ServiceProvider.GetRequiredService<IUsersService>();
return await ValidateUserAsync(userManager, authenticationState?.User);
}
finally
{
if (scope is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
else
{
scope.Dispose();
}
}
}
private async Task<bool> ValidateUserAsync(IUsersService userManager, ClaimsPrincipal? principal)
{
if (principal is null)
{
return false;
}
var userIdString = principal.FindFirst(ClaimTypes.UserData)?.Value;
if (!int.TryParse(userIdString, out var userId))
{
return false;
}
var user = await userManager.FindUserAsync(userId);
return user is not null;
}
}
And it's a program configuration and service registration:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
#region Authentication
//Authentication
services.AddDbContextFactory<ApplicationDbContext>(options =>
{
options.UseSqlServer(
Configuration.GetConnectionString("LocalDBConnection"),
serverDbContextOptionsBuilder =>
{
var minutes = (int)TimeSpan.FromMinutes(3).TotalSeconds;
serverDbContextOptionsBuilder.CommandTimeout(minutes);
serverDbContextOptionsBuilder.EnableRetryOnFailure();
})
.AddInterceptors(new CorrectCommandInterceptor()); ;
});
//add policy
services.AddAuthorization(options =>
{
options.AddPolicy(CustomRoles.Admin, policy => policy.RequireRole(CustomRoles.Admin));
options.AddPolicy(CustomRoles.User, policy => policy.RequireRole(CustomRoles.User));
});
// Needed for cookie auth.
services
.AddAuthentication(options =>
{
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.SlidingExpiration = false;
options.LoginPath = "/";
options.LogoutPath = "/login";
//options.AccessDeniedPath = new PathString("/Home/Forbidden/");
options.Cookie.Name = ".my.app1.cookie";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = context =>
{
var cookieValidatorService = context.HttpContext.RequestServices.GetRequiredService<ICookieValidatorService>();
return cookieValidatorService.ValidateAsync(context);
}
};
});
#endregion
//AutoMapper
services.AddAutoMapper(typeof(MappingProfile).Assembly);
//CustomAuthenticationStateProvider
services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
.
.
}
Don't worry about the AddSingelton in the Blazor apps. Scoped dependencies act the same as Singleton registered dependencies in Blazor apps (^).
Blazor WebAssembly apps don't currently have a concept of DI scopes. Scoped-registered services behave like Singleton services.
The Blazor Server hosting model supports the Scoped lifetime across HTTP requests (Just for the Razor Pages or MVC portion of the app) but not across SignalR connection/circuit messages among components that are loaded on the client.
That's why there's a scope.ServiceProvider.GetRequiredService here to ensure the retrived user is fetched from a new scope and has a fresh data.
Actually this solution is taken from the Microsoft's sample.
Your problem is probably here:
var scope = _scopeFactory.CreateScope();
/...
var userManager = scope.ServiceProvider.GetRequiredService<IUsersService>();
You create a new IOC container and request the instance of IUsersService from that container.
If IUsersService is Scoped, it creates a new instance.
IUsersService requires various other services which the new container must provide.
public UsersService(IUnitOfWork uow, ISecurityService securityService, ApplicationDbContext dbContext, IMapper mapper)
Here's the definition of those services in Startup:
services.AddScoped<IUnitOfWork, ApplicationDbContext>();
services.AddScoped<IUsersService, UsersService>();
services.AddScoped<IRolesService, RolesService>();
services.AddScoped<ISecurityService, SecurityService>();
services.AddScoped<ICookieValidatorService, CookieValidatorService>();
services.AddScoped<IDbInitializerService, DbInitializerService>();
IUnitOfWork and ISecurityService are both Scoped, so it creates new instances of these in the the new Container. You almost certainly don't want that: you want to use the ones from the Hub SPA session container.
You have a bit of a tangled web so without a full view of everything I can't be sure how to restructure things to make it work.
One thing you can try is to just get a standalone instance of IUsersService from the IOC container using ActivatorUtilities. This instance gets instantiated with all the Scoped services from the main container. Make sure you Dispose it if it implements IDisposable.
public class CustomAuthenticationStateProvider : RevalidatingServerAuthenticationStateProvider
{
private readonly IServiceProvider _serviceProvider;
public CustomAuthenticationStateProvider(ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
: base(loggerFactory) =>
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(scopeFactory));
protected override TimeSpan RevalidationInterval { get; } = TimeSpan.FromMinutes(30);
protected override async Task<bool> ValidateAuthenticationStateAsync(
AuthenticationState authenticationState, CancellationToken cancellationToken)
{
// Get an instance of IUsersService from the IOC Container Service to ensure it fetches fresh data
IUsersService userManager = null;
try
{
userManager = ActivatorUtilities.CreateInstance<IUsersService>(_serviceProvider);
return await ValidateUserAsync(userManager, authenticationState?.User);
}
finally
{
userManager?.Dispose();
}
}
private async Task<bool> ValidateUserAsync(IUsersService userManager, ClaimsPrincipal? principal)
{
if (principal is null)
{
return false;
}
var userIdString = principal.FindFirst(ClaimTypes.UserData)?.Value;
if (!int.TryParse(userIdString, out var userId))
{
return false;
}
var user = await userManager.FindUserAsync(userId);
return user is not null;
}
}
For reference this is my test code using the standard ServerAuthenticationStateProvider in a Blazor Server Windows Auth project.
public class MyAuthenticationProvider : ServerAuthenticationStateProvider
{
IServiceProvider _serviceProvider;
public MyAuthenticationProvider(IServiceProvider serviceProvider, MyService myService)
{
_serviceProvider = serviceProvider;
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
MyService? service = null;
try
{
service = ActivatorUtilities.CreateInstance<MyService>(_serviceProvider);
// Do something with service
}
finally
{
service?.Dispose();
}
return base.GetAuthenticationStateAsync();
}
}
I have a .NET6 Blazor Server app that has a data service as follows. I thought the [Authorize] attribute will work on it just like it works in controllers. But apparently it does not. Is it possible to use [Authorize] here? How should I implement authorization in this case?
namespace Clinic.Models
{
public interface IData
{
public Task<List<Doctor>> GetAllDoctorsAsync();
public Task<List<Patient>> GetAllPatientsAsync();
}
[Authorize]
public class DataService : IData
{
private readonly IServiceScopeFactory _scopeFactory;
public DataService(IServiceScopeFactory serviceScopeFactory)
{
_scopeFactory = serviceScopeFactory;
}
public async Task<List<Doctor>> GetAllDoctorsAsync()
{
List<Doctor> docs = new List<Doctor>();
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
docs = await context.Doctors.AsNoTracking().ToListAsync();
context.ChangeTracker.Clear();
}
return docs;
}
[Authorize]
public async Task<List<Patient>> GetAllPatientsAsync()
{
List<Patient> patients = new List<Patient>();
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
context.ChangeTracker.Clear();
patients = await context.Patients
.Include(d => d.Doctor) //get related data as well. primary doc data
.Include(d => d.SecondaryDoctors) //get related data as well.
.AsNoTracking()
.OrderByDescending(d => d.PatientId)
.ToListAsync();
}
return patients;
}
}
}
Context: ASP.NET Core App (1.1).
I try to resolve the IRepository that i've already registered when i need a consumer instance, but it looks like i receive a ObjectDisposedException.
I created a bus factory method that auto activates when i call .Build() method of the ContainerBuilder.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<EventTypeResolver>().As<IEventTypeResolver>();
containerBuilder.RegisterType<Repository>().As<IRepository>();
containerBuilder.Register(c => EventStoreConnectionFactory.Create())
.As<IEventStoreConnection>();
containerBuilder.Register(BusFactoryMethod).AsSelf().SingleInstance().AutoActivate();
containerBuilder.Populate(services);
return new AutofacServiceProvider(containerBuilder.Build());
}
BusFactoryMethod looks like this:
private IBusControl BusFactoryMethod(IComponentContext componentContext)
{
var busControl = BusConfigurator.Instance.ConfigureBus((cfg, host) =>
{
cfg.ReceiveEndpoint(host, RabbitMQConstants.UserManagementQueue, e =>
{
e.Consumer(() => new CreateUserCommandConsumer(componentContext.Resolve<IRepository>()));
});
});
busControl.Start();
return busControl;
}
So i've found a package MassTransit.AutofacIntegration which already has a RegisterConsumers method.
Then i used Autofac's RegisterGeneric method in order to get a lifetime per message.
containerBuilder.RegisterGeneric(typeof(AutofacConsumerFactory<>))
.WithParameter(new NamedParameter("name", "message"))
.As(typeof(IConsumerFactory<>))
.InstancePerLifetimeScope();
And ended up with the following Module:
public class DefaultModule : Autofac.Module
{
protected override void Load(ContainerBuilder containerBuilder)
{
containerBuilder.Register(c => new EventTypeResolver(typeof(DefaultModule).GetTypeInfo().Assembly)).As<IEventTypeResolver>();
containerBuilder.Register(c => EventStoreConnectionFactory.Create())
.As<IEventStoreConnection>();
containerBuilder.RegisterType<Repository>().As<IRepository>();
containerBuilder.RegisterConsumers(typeof(DefaultModule).GetTypeInfo().Assembly).AsSelf().AsImplementedInterfaces();
containerBuilder.RegisterGeneric(typeof(AutofacConsumerFactory<>))
.WithParameter(new NamedParameter("name", "message"))
.As(typeof(IConsumerFactory<>))
.InstancePerLifetimeScope();
containerBuilder.Register((c) =>
{
var busControl = BusConfigurator.Instance.ConfigureBus((cfg, host) =>
{
ConfigureEndPoints(cfg, host, c);
});
busControl.Start();
return busControl;
}).SingleInstance().AutoActivate();
}
private void ConfigureEndPoints(IRabbitMqBusFactoryConfigurator cfg, IRabbitMqHost host, IComponentContext context)
{
cfg.ReceiveEndpoint(host, RabbitMQConstants.UserManagementQueue, e =>
{
e.Consumer(context.Resolve<IConsumerFactory<CreateUserCommandConsumer>>());
});
}
}
ConfigureServices method is now looking like this:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<DefaultModule>();
containerBuilder.Populate(services);
return new AutofacServiceProvider(containerBuilder.Build());
}
I've just started to use MediatR in an asp.net core project and am struggling to wire up validation ...
Here's my controller:
public class PersonController : Controller
{
IMediator mediator;
public PersonController(IMediator mediator)
{
this.mediator = mediator;
}
[HttpPost]
public async Task<ActionResult> Post([FromBody]CreatePerson model)
{
var success = await mediator.Send(model);
if (success)
{
return Ok();
}
else
{
return BadRequest();
}
}
}
... and the CreatePerson command, validation (via FluentValidation) and request handler:
public class CreatePerson : IRequest<bool>
{
public string Title { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
public class CreatePersonValidator : AbstractValidator<CreatePerson>
{
public CreatePersonValidator()
{
RuleFor(m => m.FirstName).NotEmpty().Length(1, 50);
RuleFor(m => m.Surname).NotEmpty().Length(3, 50);
}
}
public class CreatePersonHandler : IRequestHandler<CreatePerson, bool>
{
public CreatePersonHandler()
{
}
public bool Handle(CreatePerson message)
{
// do some stuff
return true;
}
}
I have this generic validation handler:
public class ValidatorHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
private readonly IRequestHandler<TRequest, TResponse> inner;
private readonly IValidator<TRequest>[] validators;
public ValidatorHandler(IRequestHandler<TRequest, TResponse> inner, IValidator<TRequest>[] validators)
{
this.inner = inner;
this.validators = validators;
}
public TResponse Handle(TRequest message)
{
var context = new ValidationContext(message);
var failures = validators
.Select(v => v.Validate(context))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
if (failures.Any())
throw new ValidationException(failures);
return inner.Handle(message);
}
}
... but I'm struggling to wire the validation up correctly in Startup.ConfigureServices using autofac:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
var builder = new ContainerBuilder();
builder.Register<SingleInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => c.Resolve(t);
});
builder.Register<MultiInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
});
builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof(CreatePersonHandler).GetTypeInfo().Assembly).AsClosedTypesOf(typeof(IRequestHandler<,>));
builder.RegisterGenericDecorator(typeof(ValidatorHandler<,>), typeof(IRequestHandler<,>), "Validator").InstancePerLifetimeScope();
builder.Populate(services);
var container = builder.Build();
return container.Resolve<IServiceProvider>();
}
When I run the app and POST /api/person
{
"title": "Mr",
"firstName": "Paul",
"surname": ""
}
I get a 200.
CreatePersonHandler.Handle() was called but CreatePersonValidator() is never called.
Am i missing something in Startup.ConfigureServices()?
I suggest that you read the official documentation on how to wire up decorators in Autofac.
Decorators use named services to resolve the decorated services.
For example, in your piece of code:
builder.RegisterGenericDecorator(
typeof(ValidatorHandler<,>),
typeof(IRequestHandler<,>),
"Validator").InstancePerLifetimeScope();
you're instructing Autofac to use ValidationHandler<,> as a decorator to IRequestHandler<,> services that have been registered with the Validator name, which is probably not what you want.
Here's how you could get it working:
// Register the request handlers as named services
builder
.RegisterAssemblyTypes(typeof(CreatePersonHandler).GetTypeInfo().Assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>))
.Named("BaseImplementation");
// Register the decorators on top of your request handlers
builder.RegisterGenericDecorator(
typeof(ValidatorHandler<,>),
typeof(IRequestHandler<,>),
fromKey: "BaseImplementation").InstancePerLifetimeScope();
I find specifying the name of the fromKey parameter helps in understanding how decorators work with Autofac.
Why I am not able to inject the SetterProperty via StructureMap to an MVC ActionFilter?
public class LockProjectFilter : ActionFilterAttribute
{
[SetterProperty]
public ISecurityService SecurityService { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var loggedinStaffId = SecurityService.GetLoggedInStaffId();
if (loggedinStaffId == 1)
throw new ArgumentNullException();
base.OnActionExecuting(filterContext);
}
}
public static IContainer Initialize()
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.AssemblyContainingType<ISecurityService>();
});
x.SetAllProperties(p => p.OfType<ISecurityService>());
//x.ForConcreteType<LockProjectFilter>().Configure
// .Setter(c => c.SecurityService).IsTheDefault();
});
return ObjectFactory.Container;
}
You need to utilize the 'BuildUp' method off the ObjectFactory.
http://docs.structuremap.net/ConstructorAndSetterInjection.htm#section4
[Test]
public void create_a_setter_rule_and_see_it_applied_in_BuildUp_through_ObjectFactory()
{
var theGateway = new DefaultGateway();
ObjectFactory.Initialize(x =>
{
x.ForRequestedType<IGateway>().TheDefault.IsThis(theGateway);
// First we create a new Setter Injection Policy that
// forces StructureMap to inject all public properties
// where the PropertyType is IGateway
x.SetAllProperties(y =>
{
y.OfType<IGateway>();
});
});
// Create an instance of BuildUpTarget1
var target = new BuildUpTarget1();
// Now, call BuildUp() on target, and
// we should see the Gateway property assigned
ObjectFactory.BuildUp(target);
target.Gateway.ShouldBeTheSameAs(theGateway);
}
Then you can create a new FilterAttributeFilterProvider like this:
public class DependencyResolverFilterProvider : FilterAttributeFilterProvider
{
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
foreach (var filter in filters)
{
//DI via Setter Injection
DependencyResolver.BuildUp(filter.Instance);
}
return filters;
}
}
Then finally add your custom filter provider to the .net pipeline.
private static void RegisterProviderAndFilters()
{
var oldProvider = FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider);
FilterProviders.Providers.Remove(oldProvider);
FilterProviders.Providers.Add(new DependencyResolverFilterProvider());
RegisterGlobalFilters(GlobalFilters.Filters);
}
Hope this helps!
wm