Get the HubContext using SimpleInjector - c#

This guide does not appear to work for SimpleInjector.
My OWIN startup looks like this:
container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
container.RegisterSingleton(() => new SimpleInjectorSignalRDependencyResolver(_container));
container.RegisterSingleton(() =>
new HubConfiguration()
{
EnableDetailedErrors = true,
Resolver = _container.GetInstance<SimpleInjectorSignalRDependencyResolver>()
});
container.RegisterSingleton<IHubActivator, SimpleInjectorHubActivator>();
container.RegisterSingleton<IStockTicker,StockTicker>();
container.RegisterSingleton<HubContextAdapter<StockTickerHub, IStockTickerHubClient>>();
container.RegisterSingleton(() => GlobalHost.ConnectionManager);
container.Verify();
GlobalHost.DependencyResolver = container.GetInstance<SimpleInjectorSignalRDependencyResolver>();
app.Use(async (context, next) =>
{
using (container.BeginExecutionContextScope())
{
await next();
}
});
app.MapSignalR(container.GetInstance<HubConfiguration>());
And The HubContextAdapter looks like this:
public class HubContextAdapter<THub, TClient>
where THub : Hub
where TClient : class
{
private readonly IConnectionManager _manager;
public HubContextAdapter(IConnectionManager manager)
{
_manager = manager;
}
public IHubContext<TClient> GetHubContext()
{
return _manager.GetHubContext<THub, TClient>();
}
}
And SimpleInjectorSignalRDependencyResolver looks like:
public class SimpleInjectorSignalRDependencyResolver : DefaultDependencyResolver
{
public SimpleInjectorSignalRDependencyResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public override object GetService(Type serviceType)
{
return _serviceProvider.GetService(serviceType) ?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var #this = (IEnumerable<object>)_serviceProvider.GetService(
typeof(IEnumerable<>).MakeGenericType(serviceType));
var #base = base.GetServices(serviceType);
return #this == null ? #base : #base == null ? #this : #this.Concat(#base);
}
private readonly IServiceProvider _serviceProvider;
}
And StockTicker looks like:
public class StockTicker : IStockTicker
{
private readonly HubContextAdapter<StockTickerHub, IStockTickerHubClient> _context;
public StockTicker(HubContextAdapter<StockTickerHub, IStockTickerHubClient> context)
{
_context = context;
}
}
When the StockTicker ticks and calls all clients to update the client method is not invoked and there is no network traffic.

SimpleInjector wants to instantiate the singletons after verification or after the first GetInstance call. This is too early for SignalR and the StockTicker and it will take an instance of GlobalHost.ConnectionManager before SimpleInjectorSignalRDependencyResolver is the resolver.
I chose to change the dependency on IConnectionManager to be Lazy<IConnectionManager> and the dependency on IStockTicker to be Lazy<IStockTicker> so that registration became like the following:
container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
container.RegisterSingleton(() => new SimpleInjectorSignalRDependencyResolver(_container));
container.RegisterSingleton(() =>
new HubConfiguration()
{
EnableDetailedErrors = true,
Resolver = _container.GetInstance<SimpleInjectorSignalRDependencyResolver>()
});
container.RegisterSingleton<IHubActivator, SimpleInjectorHubActivator>();
container.RegisterSingleton<IStockTicker,StockTicker>();
container.RegisterSingleton<Lazy<IStockTicker>>(() => new Lazy<IStockTicker>(() => container.GetInstace<IStockTicker>()) );
container.RegisterSingleton<HubContextAdapter<StockTickerHub, IStockTickerHubClient>>();
container.RegisterSingleton(() => new Lazy<IConnectionManager>(() => GlobalHost.ConnectionManager));
container.Verify();

Related

Is there a way to register MediatR in Topshelf Host Factory

I am new to this and I am trying to register MediatR in an application that uses TopShelf. Whatever I have currently keeps returning null. Any help will be highly appreciated.
class Program
{
static void Main(string[] args)
{
...
// Create your container
var container = new UnityContainer();
container.RegisterType<ServiceContext>(TypeLifetime.Singleton);
container.RegisterType<ISettingsService, SettingsService>();
container.RegisterType<IMediator, Mediator>();
container.RegisterType<IMapper, Mapper>();
var serviceProvider = new ServiceCollection()
.AddMediatR(typeof(NormalUserCreateHandlerAsync).Assembly)
.AddAutoMapper(typeof(CountryMappingProfile).Assembly)
.BuildServiceProvider();
HostFactory.Run(x =>
{
x.UseSerilog(configuration);
x.UseUnityContainer(container);
x.Service<Service>(s =>
{
//s.ConstructUsing(() => new Service());
s.ConstructUsingUnityContainer();
s.WhenStarted((c, hostControl) => c.Start(hostControl));
s.WhenStopped((c, hostControl) => c.Stop(hostControl));
});
x.RunAsLocalSystem();
...
});
}
}
My implemenation method
public Equities(ISettingsService settingsService, IMediator mediator, IMapper mapper, CancellationToken token)
{
this.settingsService = settingsService;
this._mediator = mediator;
this._mapper = mapper;
}

How to register typed httpClient service with autofac?

I'm creating MVC web application which calls an api using .net core 2.2 using separate HttpClients to call each controller (same api).
Ex:
For user controller actions : UserService (httpclient)
For post controller actions : PostService (httpclient)
In startup.cs I use DI as:
services.AddHttpClient<IUserService, UserService>();
services.AddHttpClient<IPostService, PostService>();
In my handler :
public class CommandHandler : IRequestHandler<Command, BaseResponse>
{
private readonly IUserService _userService;
public CommandHandler(IUserService userService)
{
_userService = userService;
}
public Task<BaseResponse> Handle(Command request, CancellationToken cancellationToken)
{
throw new System.NotImplementedException();
}
}
But when invoking command handler I get this error:
None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'xxx.Application.Services.Users.UserService' can be invoked with
the available services and parameters: Cannot resolve parameter
'System.Net.Http.HttpClient httpClient' of constructor 'Void
.ctor(System.Net.Http.HttpClient,
xxx.Application.Configurations.IApplicationConfigurations,
Microsoft.Extensions.Logging.ILogger`1[xxx.Application.Services.Users.UserService])'.
But I've registered services in autofac module:
public class ServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(ServiceModule).Assembly)
.Where(t => t.Namespace.StartsWith("xxx.Application.Services"))
.AsImplementedInterfaces().InstancePerLifetimeScope();
}
}
Here is my UserService class constructor:
public UserService (HttpClient httpClient, IApplicationConfigurations applicationConfig, ILogger<UserService> logger)
{
_httpClient = httpClient;
_applicationConfig = applicationConfig;
_logger = logger;
_remoteServiceBaseUrl = $"{_applicationConfig.WebApiBaseUrl}";
}
I have two questions:
What does the above error mean?
Is it good practice to use separate httpclients for different controllers in api?
By doing
services.AddHttpClient<IUserService, UserService>();
You will configure the native .net core dependency injection to inject HttpClient to UserService when a IUserService is requested.
Then you do
builder.RegisterAssemblyTypes(typeof(ServiceModule).Assembly)
.Where(t => t.Namespace.StartsWith("xxx.Application.Services"))
.AsImplementedInterfaces().InstancePerLifetimeScope();
which will erase the native dependency injection configuration for IUserService. The IUserService is now registered with UserService without any HttpClient in mind.
The simplest way to add HttpClient would be to register it like this :
builder.Register(c => new HttpClient())
.As<HttpClient>();
or
services.AddHttpClient(); // register the .net core IHttpClientFactory
builder.Register(c => c.Resolve<IHttpClientFactory>().CreateClient())
.As<HttpClient>();
If you want to configure your httpclient for a specific service you can create an autofac module which add parameters like this :
public class HttpClientModule<TService> : Module
{
public HttpClientModule(Action<HttpClient> clientConfigurator)
{
this._clientConfigurator = clientConfigurator;
}
private readonly Action<HttpClient> _clientConfigurator;
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
base.AttachToComponentRegistration(componentRegistry, registration);
if (registration.Activator.LimitType == typeof(TService))
{
registration.Preparing += (sender, e) =>
{
e.Parameters = e.Parameters.Union(
new[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof(HttpClient),
(p, i) => {
HttpClient client = i.Resolve<IHttpClientFactory>().CreateClient();
this._clientConfigurator(client);
return client;
}
)
});
};
}
}
}
Then
builder.RegisterModule(new HttpClientModule<UserService>(client =>
{
client.BaseAddress = new Uri("https://api.XXX.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.XXX.v3+json");
client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-XXX");
}));
Cyril's implementation of using an Autofac module works wonderfully, but unfortunately is not compatible with Autofac 6.0+.
In order to configure an HttpClient in Autofac 6.0+ for a specific service, an Autofac middleware needs to be implemented:
public class HttpClientMiddleware<TService> : IResolveMiddleware
{
private readonly Action<HttpClient> _clientConfigurator;
public HttpClientMiddleware(Action<HttpClient> clientConfigurator)
{
_clientConfigurator = clientConfigurator;
}
public PipelinePhase Phase => PipelinePhase.ParameterSelection;
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
if (context.Registration.Activator.LimitType == typeof(TService))
{
context.ChangeParameters(context.Parameters.Union(
new[]
{
new ResolvedParameter(
(p, _) => p.ParameterType == typeof(HttpClient),
(_, i) => {
var client = i.Resolve<IHttpClientFactory>().CreateClient();
_clientConfigurator(client);
return client;
}
)
}));
}
next(context);
}
}
Then the service can be registered, utilizing the middleware:
builder.RegisterType<UserService>()
.As<IUserService>()
.ConfigurePipeline(p =>
{
p.Use(new HttpClientMiddleware<UserService>(client =>
{
client.BaseAddress = new Uri("https://api.XXX.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.XXX.v3+json");
client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-XXX");
}));
});
You can register any type with httpclient as follows extension method.
public static ContainerBuilder RegisterWithHttpClient<TInterface, TClass>(this ContainerBuilder builder, Action<IComponentContext, HttpClient> config)
where TClass: class
{
builder
.RegisterType<TClass>()
.AsSelf()
.As<TInterface>()
.WithParameter(new ResolvedParameter(
(info, context) => info.ParameterType.IsAssignableFrom(typeof(HttpClient)),
(info, context) =>
{
var httpClient = context.Resolve<IHttpClientFactory>().CreateClient();
config.Invoke(context, httpClient);
return httpClient;
}
))
.InstancePerLifetimeScope();
return builder;
}
and register your type.
//in startup.cs or autofac module.
public void ConfigureContainer(ContainerBuilder container)
{
container.RegisterWithHttpClient<IEmailSender, MyEmailSender>((context, client) =>
{
var settings = context.Resolve<IOptionsSnapshot<EmailSenderSettings>>().Value;
client.BaseAddress = new Uri($"{settings.ApiBaseUrl.TrimEnd('/')}/");
client.Timeout = TimeSpan.FromSeconds(settings.TimeoutSeconds);
});
}

Best way to Inject SeedData into Custom WebApplicationFactory

I have plagiarized the below code from the mightysoft docs site on integration testing and adapted it slightly to meet my needs:
public class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup : class
{
private readonly SeedDataClass _seed;
public CustomWebApplicationFactory(SeedDataClass seed)
{
_seed = seed;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.UseEnvironment("Development");
builder.ConfigureServices(services =>
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddSingleton(_seed);
services.AddDbContextPool<GatewayContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
options.UseInternalServiceProvider(serviceProvider);
options.EnableSensitiveDataLogging();
});
var sp = services.BuildServiceProvider();
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<GatewayContext>();
var logger = scopedServices
.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
var seed = scopedServices.GetRequiredService<SeedDataClass>();
db.Database.EnsureCreated();
try
{
seed.InitializeDbForTests(db);
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred seeding the database with test messages. Error: {ex.Message}");
}
}
});
}
}
To be used like in a test like:
_client = new CustomWebApplicationFactory<Startup>(new SeedDataClass()).CreateClient();
And this all works but I am looking to add generics to the custom web app factory class and move this code into a nuget package I am working on for internal testing work.
Something like this:
public class CustomWebApplicationFactory<TStartup, TContext>
: WebApplicationFactory<TStartup>
where TStartup : class
where TContext : DbContext
I am stuck on how to provide/inject the SeedDataClass class instance into my new generic custom web app factory.
If you are just trying to adapt a similar constructor to the former implementation of your CustomWebApplicationFactory<TStartup> class
_client = new CustomWebApplicationFactory<Startup>(new SeedDataClass()).CreateClient();
then your new constructor would look like so:
public class CustomWebApplicationFactory<TStartup, TContext> : WebApplicationFactory<TStartup>
where TStartup : class
where TContext : DbContext
{
private readonly SeedDataClass _seed;
public CustomWebApplicationFactory(SeedDataClass seed)
{
if (seed == null) throw new ArgumentNullException(nameof(seed));
_seed = seed;
}
}
and then update your call to the constructor like so
new CustomWebApplicationFactory<Startup, YourDbContext>(new SeedDataClass()).CreateClient();
This is where I was going with this:
Ammended factory:
public class GenericWebApplicationFactory<TStartup, TContext, TSeed>
: WebApplicationFactory<TStartup>
where TStartup : class
where TContext : DbContext
where TSeed : class, ISeedDataClass
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.UseEnvironment("Development");
builder.ConfigureServices(services =>
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddSingleton<ISeedDataClass,TSeed >();
services.AddDbContextPool<TContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
options.UseInternalServiceProvider(serviceProvider);
options.EnableSensitiveDataLogging();
});
var sp = services.BuildServiceProvider();
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<TContext>();
var logger = scopedServices.GetRequiredService<ILogger<GenericWebApplicationFactory<TStartup, TContext, TSeed>>>();
var seeder = scopedServices.GetRequiredService<ISeedDataClass>();
db.Database.EnsureCreated();
try
{
seeder.InitializeDbForTests();
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred seeding the database with test messages. Error: {ex.Message}");
}
}
});
}
}
Ammended usage:
_client = new GenericWebApplicationFactory<Startup, GatewayContext, SeedDataClass>().CreateClient();
With example seed class:
public interface ISeedDataClass
{
void InitializeDbForTests();
}
public class SeedDataClass : ISeedDataClass
{
private readonly GatewayContext _db;
public SeedDataClass(GatewayContext db)
{
_db = db;
}
public void InitializeDbForTests()
{
_db.Users.AddRange(
// add some users here
);
_db.SaveChanges(true);
}
}
Now, I can seed the in memory database however I see fit, per project where it is employed and my GenericWebApplicationFactory can now be pushed into a helper lib/nuget package which be re-used in other projects.

Autofac Property Injection in Nancy Module

I am using Autofac for DI and i have NacyModule like:
public class TestModule: NancyModule
{
public ISessionFactory SessionFactory { get; set; }
public IMapper Mapper { get; set; }
public TestModule(ITestRepository testRepository)
{
Get("hello", _ => "hello world");
}
}
My AutoFac configuration
In Startup.cs
var builder = new ContainerBuilder();
builder.RegisterModule(new ServicesModule());
builder.RegisterModule(new NHibernateModule(configuration.GetConnectionString("DefaultConnection")));
builder.RegisterModule(new AutomapperModule());
builder.Populate(services);
container = builder.Build();
return new AutofacServiceProvider(container);
in ServiceModule.cs
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(t => new[]
{
"Processor",
"Process",
"Checker",
"Indexer",
"Searcher",
"Translator",
"Mapper",
"Exporter",
"Repository" }.Any(y =>
{
var a = t.Name;
return a.EndsWith(y);
}))
.AsSelf()
.AsImplementedInterfaces()
.PropertiesAutowired()
.InstancePerLifetimeScope();
in NHibernateModule.cs
builder.Register(c => CreateConfiguration(connectionString)).SingleInstance();
builder.Register(c => c.Resolve<Configuration>().BuildSessionFactory()).As<ISessionFactory>().SingleInstance().PropertiesAutowired();
And in my nancy bootstraper I have something like this
public class Bootstrapper : AutofacNancyBootstrapper
{
private static readonly ILogger logger = LogManager.GetLogger(typeof(Bootstrapper).FullName);
private readonly ILifetimeScope _container;
public Bootstrapper(ILifetimeScope container)
{
_container = container;
}
protected override ILifetimeScope GetApplicationContainer()
{
return _container;
}
public override void Configure(INancyEnvironment environment)
{
base.Configure(environment);
environment.Tracing(false, true);
}
protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
{
container.Update(builder =>
{
builder.Register(c =>
{
var sf = c.Resolve<ISessionFactory>();
return new Lazy<NHibernate.ISession>(() =>
{
var s = sf.OpenSession();
s.BeginTransaction();
return s;
});
}).InstancePerLifetimeScope();
builder.Register(c => c.Resolve<Lazy<NHibernate.ISession>>().Value).As<NHibernate.ISession>();
});
}
}
I now about constructor injection, works ok, and property injection works ok in other classes, but not works in nancy modules
Note I tried adding .PropertiesAutowired() in ConfigureRequestContainer after the container update
thanks.
The AutofacNancyBootstrapper class automatically register the module in Autofac even if the service is already registered :
AutofacNancyBootstrapper.cs
protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
{
return container.Update(builder => builder.RegisterType(moduleType)
.As<INancyModule>())
.Resolve<INancyModule>();
}
With the default implementation the module is always registered and PropertiesAutoWired is not applied.
To change this, you can override the method like this :
protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
{
return container.Update(builder => builder.RegisterType(moduleType)
.As<INancyModule>())
.Resolve<INancyModule>()
.PropertiesAutoWired();
}
Or change it like this :
protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
{
INancyModule module = null;
if (container.IsRegistered(moduleType))
{
module = container.Resolve(moduleType) as INancyModule;
}
else
{
IEnumerable<IComponentRegistration> registrations = container.ComponentRegistry.RegistrationsFor(new TypedService(typeof(INancyModule)));
IComponentRegistration registration = registrations.FirstOrDefault(r => r.Activator.LimitType == moduleType);
if (registration != null)
{
module = container.ResolveComponent(registration, Enumerable.Empty<Parameter>()) as INancyModule;
}
else
{
module = base.GetModule(container, moduleType);
}
}
return module;
}
and then register the module in your composition root
builder.RegisterType<TestModule>()
.As<INancyModule>()
.PropertiesAutoWired()

Mapper not initialized , error with my unit test Core.Net 2.0

I have a WebApi done in Core.net 2.0, with UOW , and automapper.
Everything is working fine, but now I want to implement Unit Test with Nunit, and I have this error of automapper
Message: System.InvalidOperationException : Mapper not initialized.
Call Initialize with appropriate configuration. If you are trying to
use mapper instances through a container or otherwise, make sure you
do not have any calls to the static Mapper.Map methods, and if you're
using ProjectTo or UseAsDataSource extension methods, make sure you
pass in the appropriate IConfigurationProvider instance.
How can I solve this. Thanks in advance .
Jolynice
Class AutoMapperProfile.cs
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Cars, CarsDTO>()
.ReverseMap();
}
}
class Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
//removed configurations
// Add cors
services.AddCors();
// Add framework services.
services.AddMvc();
Mapper.Initialize(cfg =>
{
cfg.AddProfile<AutoMapperProfile>();
});
// Repositories
services.AddScoped<IUnitOfWork, HttpUnitOfWork>();
services.AddScoped<IAccountManager, AccountManager>();
}
}
class carsController.cs
[Authorize]
[Route("api/[controller]")]
public class CarsController : Controller
{
private IUnitOfWork _unitOfWork;
readonly ILogger _logger;
private readonly IAccountManager _accountManager;
public CarsController(
IUnitOfWork unitOfWork,
ILogger<CarsController> logger,
IAccountManager accountManager)
{
_unitOfWork = unitOfWork;
_logger = logger;
_accountManager = accountManager;
}
[HttpGet]
public IActionResult GetAll()
{
var allCars = _unitOfWork.CarsRepository.GetAllCarsData();
if (allCars == null)
{
return NotFound();
}
return Ok(Mapper.Map<IEnumerable<CarsDTO>>(allCars));
}
and this is my unit test
[TestFixture]
public class CarsControllerTest
{
#region private variables
List<Cars> cars = new List<Cars>();
CarsController _carsController = null;
IUnitOfWork _unitOfWork;
ICarsRepository _carsRepository;
#endregion
[SetUp]
public void SetUp()
{
cars = new List<Cars>
{
new Cars
{
Alias = "406Moq",
BrandId = 1,
ModelId = 1,
Plate = "00-00-01",
AltranVehicle = 0,
DefaultCar = 0,
Active = 1,
ColorId = 1
}
};
}
[Test]
public void GetAllCarsControllerTest()
{
//Arrange
_carsRepository = SetupCarsRepository();
var unityOfWork = new Mock<IUnitOfWork>();
var _logger = new Mock<ILogger<CarsController>>();
var accountManager = new Mock<IAccountManager>();
unityOfWork.SetupGet(c => c.CarsRepository).Returns(_carsRepository);
_unitOfWork = unityOfWork.Object;
_carsController = new CarsController(_unitOfWork, _logger.Object, accountManager.Object);
//Act
var carsResult = _carsController.GetAll();
//Assert
carsResult.StatusCode.Should().Be(HttpStatusCode.OK);
}
private ICarsRepository SetupCarsRepository()
{
//initialize repository
var mockRepo = new Mock<ICarsRepository>(MockBehavior.Default);
//Setup mocking behavior
mockRepo.Setup(c => c.GetAllCarsData()).Returns(cars);
return mockRepo.Object;
}
//Cleanup
[TearDown]
public void TearDown()
{
cars = null;
}
}
}
You are missing initialization of your mapper in your unit test. The following initializes the mapper in the CarsControllerTest class constructor.
[TestFixture]
public class CarsControllerTest
{
public CarsControllerTest()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<AutoMapperProfile>();
});
}
}

Categories