I am using in my project MediatR. This is what I have in my service class:
public class WebShopServices : IWebShopServices
{
private readonly IMediator _mediator;
public WebShopServices(IMediator mediator)
{
_mediator = mediator;
}
public void UpdateCustomerAddress(UpdateAddressInformationCommand updateAddressInformation)
{
_mediator.Send(updateAddressInformation);
}
}
This is my handler
public class UpdateCustomerAddressHandler : RequestHandler<UpdateAddressInformationCommand>
{
private readonly OnlineSalesService _client;
private readonly IDataContractsFactory _factory;
private readonly IParameterValidator _validator;
public UpdateCustomerAddressHandler(OnlineSalesService client,
IDataContractsFactory factory, IParameterValidator validator)
{
_client = client;
_factory = factory;
_validator = validator;
}
protected override void HandleCore(
UpdateAddressInformationCommand updateAddressInformation)
{
_validator.Validate(updateAddressInformation);
var updateAddressCommand =
_factory.CreateCustomerDefaultAddressCommand(updateAddressInformation);
try
{
_client.SetCustomerDefaultAddress(updateAddressCommand);
}
catch (Exception ex)
{
throw new CustomerException("Something happened with the service.", ex);
}
}
}
This is my model class:
public class UpdateAddressInformationCommand : IRequest
{
public string CustomerNumber { get; set; }
public AddressInformation AddressInformation { get; set; }
}
And this is what I wrote in dependency injection config:
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof (IMediator).Assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof (Ping).Assembly).AsImplementedInterfaces();
builder.RegisterInstance(Console.Out).As<TextWriter>();
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.RegisterType<GetCustomerDetailsResultHandler>()
.As<IRequestHandler<GetCustomerDetailsQuery,GetCustomerDetailsResult>>();
builder.RegisterType<UpdateCustomerAddressHandler>()
.As<RequestHandler<UpdateAddressInformationCommand>>();
And it keeps giving me this exception:
{"The requested service
'MediatR.IRequestHandler`2[[Service.DataContracts.UpdateAddressInformationCommand,
Service, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null],[MediatR.Unit, MediatR, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=null]]' has not been registered. To
avoid this exception, either register a component to provide the
service, check for service registration using IsRegistered(), or use
the ResolveOptional() method to resolve an optional dependency."}
Does anyone know how to fix this problem?
Try this to registering IRequestHandler via:
builder.RegisterAssemblyTypes(assembly).As(t => t.GetInterfaces()
.Where(i => i.IsClosedTypeOf(typeof(IRequestHandler<,>)))
.Select(i => new KeyedService(HandlerKey, i)));
To make it easier to use mediatr and Autofac try the MediatR.Extensions https://github.com/bernos/MediatR.Extensions
I solved the problem by creating an empty class UpdateAddressInformationResult, and in UpdateCustomerAddressHandler I implemented interface IRequestHandler.
I really don't know how to solve the RequestHandler issue. Thanks all.
Related
i'm trying to test my controllers with IClassFixture<WebApplicationFactory<Startup>>, but when i run multiples tests i get the error:
System.InvalidOperationException : The container can't be changed
after the first call to GetInstance, GetAllInstances, Verify, and some
calls of GetRegistration. Please see https://simpleinjector.org/locked
to understand why the container is locked. The following stack trace
describes the location where the container was locked:
public class TestControllerTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly HttpClient _httpClient;
private readonly TestDbContext _dbContext;
private readonly AuthenticationClientBuilder<MicrosoftPatternAdministratorAuthHandler, Startup> _builder;
private readonly WebApplicationFactory<Startup> _factory;
public LicenseControllerTests(WebApplicationFactory<Startup> factory)
{
_factory = factory;
_builder = new AuthenticationClientBuilder<MicrosoftPatternAdministratorAuthHandler, Startup>();
_dbContext = new TestDbContext(new DbContextOptions<TestDbContext>());
_dbContext.Database.SetConnectionString(_builder.GetConnectionString());
DbInitializer.Initialize(_dbContext);
_httpClient = _builder.BuildAuthenticatedClient(_factory);
}
}
In the callstack, i see that the error ocourred in the line: _httpClient = _builder.BuildAuthenticatedClient(_factory);
The code of this class is:
namespace Namespace_X
{
public class AuthenticationClientBuilder<TAuthenticationHandler, TStartup> : IDisposable
where TAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
where TStartup : class
{
private WebApplicationFactory<TStartup> _factory;
private readonly string _connectionString;
public AuthenticationClientBuilder()
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
_connectionString = config["AppSettings:ConnectionString"];
}
public HttpClient BuildAuthenticatedClient(WebApplicationFactory<TStartup> factory)
{
_factory = factory;
return _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication("TestAuthentication")
.AddScheme<AuthenticationSchemeOptions, TAuthenticationHandler>("TestAuthentication", null);
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(TestDbContext));
if (descriptor != null)
{
services.Remove(descriptor);
services.AddDbContext<TestDbContext>((options, context) =>
{
context.UseSqlServer(_connectionString);
});
}
});
}).CreateClient();
}
public string GetConnectionString()
{
return _connectionString;
}
public void Dispose()
{
_factory.Dispose();
}
}
}
In the startup the exception is throw when the container try to register the DbContext:
container.Register(() =>
{
var options = new DbContextOptionsBuilder<TestDbContext>().UseSqlServer().Options;
return new TestDbContext(options);
}, Lifestyle.Transient);
When i run one test per time they work.
Any hint? Thx in advance
Currently I'm able to handle IServiceCollection to inject mocks for particular services in the following manner.
public class TestClass
{
private IMediator _mediatr;
private void SetupProvider(IUnitOfWork unitOfWork, ILogger logger)
{
configuration = new ConfigurationBuilder().Build();
_services = new ServiceCollection();
_services.AddSingleton(configuration);
_services.AddScoped(x => unitOfWork);
_services.AddSingleton(logger);
_services.AddMediatR(Assembly.Load("Application"));
_services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggerBehaviour<,>));
_mediator = _services.BuildServiceProvider().GetService<IMediator>();
}
[Fact]
public async void UnitTest_Success()
{
var unitOfWork = new Mock<IUnitOfWork>();
var logger = new Mock<ILogger>();
SetupProvider(unitOfWork.Object, logger.Object);
var fixture = new Fixture();
var command = fixture.Create<MediatorCommand>();
unitOfWork.Setup(x => x.Repository.FindAll(It.IsAny<IList<long>>(), It.IsAny<bool?>()))
.ReturnsAsync(new List<Domain.Model>());
var response = await _mediatr.Send(command);
using (new AssertionScope())
{
response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}
}
}
For the following subject under test
public class MediatorCommand : IRequest<CommandResponse>
{
public string Name { get; set ;}
public string Address { get; set; }
}
public class MediatorCommandHandler : IRequestHandler<MediatorCommand, CommandResponse>
{
private readonly ILogger _logger;
private readonly IUnitOfWork _unitOfWork;
public MediatorCommandHandler(IUnitOfWork unitOfWork, ILogger logger)
{
_logger = logger;
_unitOfWork = unitOfWork;
}
public async Task<CommandResponse> Handle(MediatorCommand command, CancellationToken cancellationToken)
{
var result = new CommandResponse { IsSuccess = false };
try
{
var entity = GetEntityFromCommand(command);
await _unitOfWork.Save(entity);
result.IsSuccess = true;
}
catch(Exception ex)
{
_logger.LogError(ex, ex.Message);
}
return result;
}
}
This test runs fine and the unitOfWork and logger mocks are used in the command handlers.
I'm try to move this so that the IServiceCollection construction happens per class instead of each test using the following:
public class SetupFixture : IDisposable
{
public IServiceCollection _services;
public IMediator Mediator { get; private set; }
public Mock<IUnitOfWork> UnitOfWork { get; private set; }
public SetupFixtureBase()
{
UnitOfWork = new Mock<IUnitOfWork>();
configuration = new ConfigurationBuilder().Build();
_services = new ServiceCollection();
_services.AddSingleton(configuration);
_services.AddScoped(x => UnitOfWork);
_services.AddSingleton(new Mock<ILogger>().Object);
_services.AddMediatR(Assembly.Load("Application"));
_services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggerBehaviour<,>));
Mediator = _services.BuildServiceProvider().GetService<IMediator>();
}
public void Dispose()
{
Mediator = null;
_services.Clear();
_services = null;
}
}
public class TestClass : IClassFixture<SetupFixture>
{
protected readonly SetupFixture _setupFixture;
public UnitTestBase(SetupFixture setupFixture)
{
_setupFixture = setupFixture;
}
[Fact]
public async void UnitTest_Success()
{
var fixture = new Fixture();
var command = fixture.Create<MediatorCommand>();
_setupFixture.UnitOfWork.Setup(x => x.Repository.FindAll(It.IsAny<IList<long>>(), It.IsAny<bool?>()))
.ReturnsAsync(new List<Domain.Model>());
var response = await _mediatr.Send(command);
using (new AssertionScope())
{
response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}
}
}
Unfortunately with this method my mocks do not get injected on the command handler. Is there a way to get this to work?
Thank you,
I found the issue and it is not related to moving to IClassFixuture<>. The issue was that I was initializing Mediator on a base class an then adding the mock UnitOfWork on a derived class.
This cause the Mediator initialization to fail because one of the beheviours expected the UnitOfWork which at the time was not yet on the container.
Moving the initialization of Mediator after all the services have been added helped me resolve the issue and now all works as expected.
If you try the same thing, please make sure to include all the services in the container before initializing any objects that require those dependencies.
Thank you all those who had input.
Parts of my code need to use a ServiceLocator because constructor injection isn't supported.
My startup class configures the services. I have some that are transient, others which are singleton and others scoped.
For example:
services.AddScoped<IAppSession, AppSession>();
services.AddScoped<IAuthentication, Authentication>();
services.AddScoped<NotificationActionFilter>();
At the end of my service definitions, I have the following block of code, which sets up the service locator.
var serviceProvider = services.BuildServiceProvider();
DependencyResolver.Current = new DependencyResolver();
DependencyResolver.Current.ResolverFunc = (type) =>
{
return serviceProvider.GetService(type);
};
I noticed that in a given request, I am not receiving the same instance from the service locator that I am from the constructor injection. Instances returned from the service locator appear to be singletons and do not respect the scoping.
The code for DependencyResolver is as follows:
public class DependencyResolver
{
public static DependencyResolver Current { get; set; }
public Func<Type, object> ResolverFunc { get; set; }
public T GetService<T>()
{
return (T)ResolverFunc(typeof(T));
}
}
How can I fix this?
I would suggest creating a middleware which will set ServiceProvider to the one which is used in other places:
public class DependencyResolverMiddleware
{
private readonly RequestDelegate _next;
public DependencyResolverMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
DependencyResolver.Current.ResolverFunc = (type) =>
{
return httpContext.RequestServices.GetService(type);
};
await _next(httpContext);
}
}
Also, DependencyResolver should be updated to support such behavior:
public class DependencyResolver
{
private static readonly AsyncLocal<Func<Type, object>> _resolverFunc = new AsyncLocal<Func<Type, object>>();
public static DependencyResolver Current { get; set; }
public Func<Type, object> ResolverFunc
{
get => _resolverFunc.Value;
set => _resolverFunc.Value = value;
}
public T GetService<T>()
{
return (T)ResolverFunc(typeof(T));
}
}
Don't forget to register it in Configure method in Startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseMiddleware<DependencyResolverMiddleware>();
}
I'm trying to use FluentValidation in a WebApi project (not asp.net Core).
I have the following code:
public static class UnityConfig
{
public static void RegisterComponents(UnityContainer container)
{
// Register validators
RegisterValidators(container);
// Mediatr
container.RegisterType<IMediator, Mediator>();
container.RegisterTypes(AllClasses.FromAssemblies(true, Assembly.GetExecutingAssembly()), WithMappings.FromAllInterfaces, GetName, GetLifetimeManager);
container.RegisterInstance<SingleInstanceFactory>(t => container.Resolve(t));
container.RegisterInstance<MultiInstanceFactory>(t => container.ResolveAll(t));
// Automapper profiles
var profileTypes = typeof(BaseProfile).Assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(BaseProfile)));
var config = new MapperConfiguration(cfg => new MapperConfiguration(x =>
{
foreach (var type in profileTypes)
{
var profile = (BaseProfile)Activator.CreateInstance(type);
cfg.AddProfile(profile);
}
}));
container.RegisterInstance<IConfigurationProvider>(config);
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
static LifetimeManager GetLifetimeManager(Type type)
{
return IsNotificationHandler(type) ? new ContainerControlledLifetimeManager() : null;
}
static string GetName(Type type)
{
return IsNotificationHandler(type) ? string.Format("HandlerFor" + type.Name) : string.Empty;
}
private static void RegisterValidators(IUnityContainer container)
{
var validators = AssemblyScanner.FindValidatorsInAssembly(Assembly.GetExecutingAssembly());
validators.ForEach(validator => container.RegisterType(validator.InterfaceType, validator.ValidatorType));
}
}
I'm scanning the assemblies and registrering the validators, of which there's only one right now, it sits here: (don't mind the weird validations, I'm trying to have it fail)
public class Query : IRequest<Result>
{
public Guid? Id { get; set; }
}
public class QueryValidator : AbstractValidator<Query>
{
public QueryValidator()
{
RuleFor(q => q.Id).Empty();
RuleFor(q => q.Id).Equal(Guid.NewGuid());
}
}
My Application_start looks like this:
protected void Application_Start()
{
var container = new UnityContainer();
UnityConfig.RegisterComponents(container);
GlobalConfiguration.Configure(WebApiConfig.Register);
var factory = new UnityValidatorFactory2(GlobalConfiguration.Configuration);
FluentValidationModelValidatorProvider.Configure(GlobalConfiguration.Configuration, x => x.ValidatorFactory = factory);
}
And I have the following validatorFactory:
public class UnityValidatorFactory2 : ValidatorFactoryBase
{
private readonly HttpConfiguration _configuration;
public UnityValidatorFactory2(HttpConfiguration configuration)
{
_configuration = configuration;
}
public override IValidator CreateInstance(Type validatorType)
{
var validator = _configuration.DependencyResolver.GetService(validatorType) as IValidator;
return validator;
}
}
Now; when I call the action on the controller, 'CreateInstance' tries to resolve a validatorType of the type:
IValidator<Guid>
instead of:
IValidator<Query>
and of course finds nothing, this means that my validations does not run.
Does anyone have an ideas as to why this is? it seems faily straight forward, so I have trouble seeing what goes wrong.
After having slept on it, I found the answer myself.
I was posting a Guid to my controller instead of the model I was trying to validate (which only contains a guid)
After posting the right model, it now validates correctly.
I have a Web Api app that consumes another REST Api client. I wrapped the REST API client into a service.
myproj/services/PostDataService.cs
public interface IPostDataService
{
Task<IList<Post>> GetAllPosts();
}
public class PostDataService : IPostDataService
{
private static IDataAPI NewDataAPIClient()
{
var client = new DataAPI(new Uri(ConfigurationManager.AppSettings["dataapi.url"]));
return client;
}
public async Task<IList<Post>> GetAllPosts()
{
using (var client = NewDataAPIClient())
{
var result = await client.Post.GetAllWithOperationResponseAsync();
return (IList<Post>) result.Response.Content;
}
}
}
....
I am using AutoFac and injecting the service in the controller
myproj/controllers/PostController.cs
public class PostController : ApiController
{
private readonly IPostDataService _postDataService;
public PostController(IPostDataService postDataService)
{
_postDataService = postDataService;
}
public async Task<IEnumerable<Post>> Get()
{
return await _postDataService.GetAllPosts();
}
}
But I am getting this error.
An error occurred when trying to create a controller of type
'PostController'. Make sure that the controller has a parameterless
public constructor.
Here is my Global.asax.cs
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
ContainerConfig.Configure();
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
public static class ContainerConfig
{
private static IContainer _container;
public static IContainer GetContainer()
{
if (_container != null)
return _container;
var builder = new ContainerBuilder();
builder.RegisterType<PostDataService>()
.AsSelf()
.InstancePerLifetimeScope()
.AsImplementedInterfaces();
_container = builder.Build();
return _container;
}
public static IContainer Configure()
{
var container = GetContainer();
var webApiResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = webApiResolver;
return container;
}
Can someone spot what I am missing here?
Thanks
I'm missing
builder.RegisterApiControllers(typeof(PostController).Assembly).
Apparently, the controller also needs to be registered.