Replace default IoC container in MvvmCross - xamarin

Since MvvmCross v7 sticks on its own IoC container, I would like to replace it with the .NET Core one in order to have an easier life when registering third party libraries such as IHttpClientFactory, Polly, Automapper, etc. through already built-in extensions methods.
To achieve this, I've successfully created a class that implementas MvxSingleton<IMvxIoCProvider> described as follow:
public class HostingAdapter : MvxSingleton<IMvxIoCProvider>, IMvxIoCProvider
{
private IServiceProvider ServiceProvider;
private IServiceCollection ServiceCollection;
public HostingAdapter()
{
var host = Host
.ConfigureServices((context, serviceCollection) =>
{
// Configure local services
ConfigureServices(context, serviceCollection);
ServiceCollection = serviceCollection;
ServiceProvider = ServiceCollection.BuildServiceProvider();
})
.Build();
}
public void RegisterType<TFrom, TTo>() where TFrom : class where TTo : class, TFrom
{
ServiceCollection.AddTransient<TFrom, TTo>();
ServiceProvider = ServiceCollection.BuildServiceProvider();
}
public T GetSingleton<T>() where T : class
{
return ServiceProvider.GetRequiredService<T>();
}
public object GetSingleton(Type type)
{
return ServiceProvider.GetRequiredService(type);
}
.. and all the required methods requested by the interface.
Then on the platform specific side I override the IoC creation as follow:
protected override IMvxIoCProvider CreateIocProvider()
{
var hostingAdapter = new HostingAdapter();
return hostingAdapter;
}
The code seems to work but as soon as the app starts Mvx registers its own "extra" services such as the IMvxLoggerProvider, IMvxSettings and so on. And here issues come:
ServiceProvider = ServiceCollection.BuildServiceProvider(); is called during the Host initialization but Mvx still continue to register services after that. This means IServiceProvider is not 'in sync' with IServiceCollection and a new ServiceCollection.BuildServiceProvider(); call is needed. I temporarily solved updating the provider at each collection registration (like the code above) but I'm aware this affects performances. Anyone knows how to workaround this?
There are plenty of Mvx services that are not registered so the app fails to start. These are the IMvxLogProvider, IMvxAndroidLifetimeMonitor, IIMvxSettings, IMvxStart, etc. I just wonder, why? How can let Mvx handle the registration in my container of all what it needs to start? I partially solved some of them such as the logger thing replacing the default with a custom one, but other callbacks like InitializeLifetimeMonitor are called too late for being registered.
Do I need to change anything in my MvxApplication than the most standard implementation?
Am I really forced to replace the standard IoC container? How can I handle the IServiceCollection's extension methods that 3rd party libraries expose like services.AddHttpClient();?
If it needs, I am on Xamarin classic using the Droid platform. Thanks

Deliberately inspired by Unity.Microsoft.DependencyInjection repository I've workarounded this approaching the problem the other way round: instead of replacing the default IoC container, I manually initialize an IServiceCollection instance and I add it to the Mvx's IoC provider.
To achieve this, I've used the following code:
public class App : MvxApplication
{
public override void Initialize()
{
base.Initialize();
InitializeServiceCollection();
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
RegisterAppStart<HomeViewModel>();
}
private static void InitializeServiceCollection()
{
IServiceCollection serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
MapServiceCollectionToMvx(serviceProvider, serviceCollection);
}
private static void ConfigureServices(IServiceCollection serviceCollection)
{
serviceCollection.AddHttpClient();
}
private static void MapServiceCollectionToMvx(IServiceProvider serviceProvider,
IServiceCollection serviceCollection)
{
foreach (var serviceDescriptor in serviceCollection)
{
if (serviceDescriptor.ImplementationType != null)
{
Mvx.IoCProvider.RegisterType(serviceDescriptor.ServiceType, serviceDescriptor.ImplementationType);
}
else if (serviceDescriptor.ImplementationFactory != null)
{
var instance = serviceDescriptor.ImplementationFactory(serviceProvider);
Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType, instance);
}
else if (serviceDescriptor.ImplementationInstance != null)
{
Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType, serviceDescriptor.ImplementationInstance);
}
else
{
throw new InvalidOperationException("Unsupported registration type");
}
}
}
}

Related

Issue with Simple Injector while using with Web API

I am having issue using Simple Injector with WebAPI project that gets created default with VS 2015.
I am having the AccountController having the below constructor
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
{
UserManager = userManager;
AccessTokenFormat = accessTokenFormat;
}
In order to register these I used the below code in Simple Injector
// Create the container.
var apiContainer = new Container();
apiContainer.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
apiContainer.Options.ConstructorResolutionBehavior = new ConstructorBehavior();
//register the classes that we are going to use for dependency injection
apiContainer.Register<IUserStore<ApplicationUser>>(() => new UserStore<ApplicationUser>(new ApplicationDbContext()),Lifestyle.Scoped);
apiContainer.Register<IDataProtector>(() => new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider().Create("ASP.NET Identity"),Lifestyle.Transient);
apiContainer.Register<ISecureDataFormat<AuthenticationTicket>, SecureDataFormat<AuthenticationTicket>>(Lifestyle.Transient);
apiContainer.Register<ITextEncoder, Base64UrlTextEncoder>(Lifestyle.Scoped);
apiContainer.Register<IDataSerializer<AuthenticationTicket>, TicketSerializer>(Lifestyle.Scoped);
//apiContainer.RegisterCommonClasses();
//register the webapi controller
apiContainer.RegisterWebApiControllers(configuration);
but after this I am getting the warning message that says
[Disposable Transient Component] ApplicationUserManager is registered as transient, but implements IDisposable.
Can someone Please help me with this how to resolve this ? With Default Web api project with VS 2015 it adds Account controller and that use ApplicationUserManager and has below details
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
Another issue I am getting as below
The constructor of type HttpConfiguration contains the parameter with name 'routes' and type HttpRouteCollection that is not registered. Please ensure HttpRouteCollection is registered, or change the constructor of HttpConfiguration.
This is with the HelpController as it uses the below details:
public HelpController()
: this(GlobalConfiguration.Configuration)
{
}
public HelpController(HttpConfiguration config)
{
Configuration = config;
}

How to unit test an action filter attribute for web api in asp.net core?

I have written an action filter for a web api. If a method in the api controller throws an unhandled exception, then the filter creates an internal error 500 response.
I need to know how to test the filter?
I have researched extensively but could not create a suitable test. I tried context mocking, a service locator implementation and even an integration test using a test server.
The web api controller looks like this:
namespace Plod.Api.ApiControllers
{
[TypeFilter(typeof(UnhandledErrorFilterAttribute))]
[Route("api/[controller]")]
public class GamesController : BaseApiController
{
public GamesController(IGameService repository,
ILogger<GamesController> logger,
IGameFactory gameFactory
) : base(
repository,
logger,
gameFactory
)
{ }
// ..... controller methods are here
}
}
The complete controller is found here.
The filter is this:
namespace Plod.Api.Filters
{
public class UnhandledErrorFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception != null)
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.ExceptionHandled = true;
}
}
}
}
I even welcome changes to the filter implementation as a possible work around. Any help or ideas would be much appreciated. Thanks.
You probably can't. However, what you can do is spin up a TestServer and then hit it with a HttpClient. This really is an integration test and not a unit test. However, it's the good kind of integration test because it can be run safely in pipelines.
This document explains how to do this:
https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1
The issue you are going to face is that you will need to mock the underlying services inside your app. If you don't do that, your whole server will spin up and attempt to hit the database etc. Here is an example. This is using Moq. Incidentally I am sharing the ConfigureServices method with unit tests so they use the same object mesh of mocked services. You can still use the full functionality of Moq or NSubstitute to test the back-end (or even front -end).
I can hit my attributes in the test with breakpoint.
private void ConfigureServices(IServiceCollection services)
{
var hostBuilder = new WebHostBuilder();
hostBuilder.UseStartup<TestStartup>();
hostBuilder.ConfigureServices(services =>
{
ConfigureServices(services);
});
_testServer = new TestServer(hostBuilder);
_httpClient = _testServer.CreateClient();
}
private void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_storageManagerFactory.Object);
services.AddSingleton(_blobReferenceManagerMock.Object);
services.AddSingleton(_ipActivitiesLoggerMocker.Object);
services.AddSingleton(_loggerFactoryMock.Object);
services.AddSingleton(_hashingService);
services.AddSingleton(_settingsServiceMock.Object);
services.AddSingleton(_ipActivitiesManager.Object);
services.AddSingleton(_restClientMock.Object);
_serviceProvider = services.BuildServiceProvider();
}
public class TestStartup
{
public void Configure(
IApplicationBuilder app,
ISettingsService settingsService)
{
app.Configure(settingsService.GetSettings());
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var mvc = services.AddMvc(option => option.EnableEndpointRouting = false);
mvc.AddApplicationPart(typeof(BlobController).Assembly);
services.AddSingleton(new Mock<IHttpContextAccessor>().Object);
return services.BuildServiceProvider();
}
}

Using FluentScheduler - ASP.NET Core MVC

I currently have a simple website setup with ASP.NET Core MVC (.NET 4.6.1), and I would like to periodically do some processes like automatically send emails at the end of every day to the registered members.
After doing some searching, I came across two common solutions - Quartz.NET and FluentScheduler.
Based on this SO thread, I found the approach of using FluentScheduler more easier to digest and use for my simple task. After quickly implementing the following lines of code into my Program.cs class, I had the emails going out successfully every minute (for testing purposes).
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
var registry = new Registry();
JobManager.Initialize(registry);
JobManager.AddJob(() => MyEmailService.SendEmail(), s => s
.ToRunEvery(1)
.Minutes());
host.Run();
}
}
However, now apart from sending emails I also need to do some back-end processing for e.g. updating the user records in the DB when mails are being sent out. For this, I normally inject my Entity Framework Context into the constructor of my controllers and use it to get/update SQL records.
My question is, since I cannot really inject these services into the main method, where would be the appropriate place to initialize the registry and add jobs for scheduling?
Thanks for the help, I am a little new to this so a little guidance would be much appreciated!
Instead of Program's Main function, I initialized the same in Startup.cs before app.UseMvc..
public void Configure(...., IDependencyObject dependencyObject)
{
....
JobManager.Initialize(new MyRegistry(dependencyObject));
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "api/{controller}/{action}/{id?}");
});
}
My registry class looks like this:
public class MyRegistry: Registry
{
public MyRegistry(IDependencyObject dependencyObject)
{
Schedule(() => new SyncUpJob(dependencyObject)).ToRunNow().AndEvery(10).Seconds();
}
}
My Job class looks like this:
public class SyncUpJob: IJob
{
public SyncUpJob(IDependencyObject dependencyObject)
{
DependencyObject= dependencyObject;
}
public IDependencyObject DependencyObject{ get; set; }
public void Execute()
{
// call the method to run weekly here
}
}
You can define all your jobs and their schedules, by subclassing from FluentScheduler Registry class. something like:
public class JobRegistry : Registry {
public JobRegistry() {
Schedule<EmailJob>().ToRunEvery(1).Days();
Schedule<SomeOtherJob>().ToRunEvery(1).Seconds();
}
}
public class EmailJob : IJob {
public DbContext Context { get; } // we need this dependency, right?!
public EmailJob(DbContext context) //constructor injection
{
Context = context;
}
public void Execute()
{
//Job implementation code: send emails to users and update database
}
}
For injecting dependencies into jobs, you need to implement FluentScheduler IJobFactory interface. GetJobIntance method is called by FluentScheduler for creating job instances. Here you can use any DI library you want; In this sample implementation, I'm going to assume that you use Ninject:
public class MyNinjectModule : NinjectModule {
public override void Load()
{
Bind<DbContext>().To<MyDbContextImplemenation>();
}
}
public class JobFactory : IJobFactory {
private IKernel Kernel { get; }
public JobFactory(IKernel kernel)
{
Kernel = kernel;
}
public IJob GetJobInstance<T>() where T : IJob
{
return Kernel.Get<T>();
}
}
Now you can start your jobs in main method by calling:
JobManager.JobFactory = new JobFactory(new StandardKernel(new MyNinjectModule()));
JobManager.Initialize(new JobRegistry());

How to fake an HttpContext and its HttpRequest to inject them in a service constructor

In a console application, I would like to use a service that would normally need the current http context to be passed to its constructor. I am using Ninject, and I think I can simply fake an http context and define the proper binding, but I have been struggling with this for a few hours without success.
The details:
The service is actually a mailing service that comes from an ASP.Net MVC project. I am also using Ninject for IoC. The mail service needs the current http context to be passed to its constructor. I do the binding as follows:
kernel.Bind<IMyEmailService>().To<MyEmailService>()
.WithConstructorArgument("httpContext", ninjectContext => new HttpContextWrapper(HttpContext.Current));
However, I would like now to use this mailing service in a console application that will be used to run automated tasks at night. In order to do this, I think I can simply fake an http context, but I have been struggling for a few hours with this.
All the mailing service needs from the context are these two properties:
httpContext.Request.UserHostAddress
httpContext.Request.RawUrl
I thought I could do something like this, but:
Define my own fake request class:
public class AutomatedTaskHttpRequest : SimpleWorkerRequest
{
public string UserHostAddress;
public string RawUrl;
public AutomatedTaskHttpRequest(string appVirtualDir, string appPhysicalDir, string page, string query, TextWriter output)
: base(appVirtualDir, appPhysicalDir, page, query, output)
{
this.UserHostAddress = "127.0.0.1";
this.RawUrl = null;
}
}
Define my own context class:
public class AutomatedTasksHttpContext
{
public AutomatedTaskHttpRequest Request;
public AutomatedTasksHttpContext()
{
this.Request = new AutomatedTaskHttpRequest("", "", "", null, new StringWriter());
}
}
and bind it as follows in my console application:
kernel.Bind<IUpDirEmailService>().To<UpDirEmailService>()
.WithConstructorArgument("httpContext", ninjectContext => new AutomatedTasksHttpContext());
Unfortunately, this is not working out. I tried various variants, but none was working. Please bear with me. All that IoC stuff is quite new to me.
I'd answered recently about using a HttpContextFactory for testing, which takes a different approach equally to a console application.
public static class HttpContextFactory
{
[ThreadStatic]
private static HttpContextBase _serviceHttpContext;
public static void SetHttpContext(HttpContextBase httpContextBase)
{
_serviceHttpContext = httpContextBase;
}
public static HttpContextBase GetHttpContext()
{
if (_serviceHttpContext!= null)
{
return _serviceHttpContext;
}
if (HttpContext.Current != null)
{
return new HttpContextWrapper(HttpContext.Current);
}
return null;
}
}
then in your code to this:
var rawUrl = HttpContextFactory.GetHttpContext().Request.RawUrl;
then in your tests use the property as a seam
HttpContextFactory.SetHttpContext(HttpMocks.HttpContext());
where HttpMocks has the following and would be adjusted for your tests:
public static HttpContextBase HttpContext()
{
var context = MockRepository.GenerateMock<HttpContextBase>();
context.Stub(r => r.Request).Return(HttpRequest());
// and stub out whatever else you need to, like session etc
return context;
}
public static HttpRequestBase HttpRequest()
{
var httpRequest = MockRepository.GenerateMock<HttpRequestBase>();
httpRequest.Stub(r => r.UserHostAddress).Return("127.0.0.1");
httpRequest.Stub(r => r.RawUrl).Return(null);
return httpRequest;
}

How to Integrate Prism, Unity, and Enterprise Library

I'm building a WPF application. I'm using Prism 4, and Unity. I want to add two Enterprise Library 5 blocks to the application, Logging and Exception Handling. I have a singleton LoggerFacadeCustom.cs in my Infrastructure class that supports the ILoggerFacade and I've created it in my bootstrapper, and it is generating log files. It "news" up a unity container in its constructor (second code block)
Where do I add the container.resolve for ExceptionManager? How do I connect the Exception handling block to ILoggerFacade in my bootstrapper? How do I get all the exceptions to come out in the same log? Here is my existing bootstrapper.cs
public class Bootstrapper : UnityBootstrapper {
protected override ILoggerFacade CreateLogger() {
return LoggerFacadeCustom.Instance;
}
protected override DependencyObject CreateShell() {
return Container.Resolve<Shell>();
}
protected override void InitializeShell() {
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
//Other shell stuff...
}
protected override IModuleCatalog CreateModuleCatalog() {
var catalog = new ModuleCatalog();
//These primary modules must register their own services as if they were acting independantly
catalog.AddModule(typeof(XmlCommentMergeModule));
//These support modules require at least one primary module above to be added first
catalog.AddModule(typeof(ToolboxHeaderModule));
catalog.AddModule(typeof(ToolboxFooterModule));
catalog.AddModule(typeof(ToolboxStartModule));
return catalog;
}
}
LoggerFacadeCustom:
public class LoggerFacadeCustom : ILoggerFacade {
private static readonly LoggerFacadeCustom _instance = new LoggerFacadeCustom();
public static LoggerFacadeCustom Instance { get { return _instance; } }
private LoggerFacadeCustom() {
var container = new UnityContainer();
container.AddNewExtension<EnterpriseLibraryCoreExtension>();
_logWriter = container.Resolve<LogWriter>();
}
private readonly LogWriter _logWriter;
public void Write(string message) { Write(message, null); }
public void Write(string message, string category, int priority) {
_logWriter.Write(message, category, priority);
}
public void Write(string message, Dictionary<string, object> properties) {
_logWriter.Write(message, LiteralString.LogCategoryProcess, properties);
}
#region ILoggerFacade Members
public void Log(string message, Category category, Priority priority) {
throw new NotImplementedException();
}
#endregion
}
Your bootstrapper is the Composition Root of your application. You should register all dependencies there. And only there. You should never reference the container directly outside the composition root.
If your classes have a dependency you should inject that dependency using a pattern like constructor injection.
Don't use static classes. Static kills dependency injection and testability and it hides dependencies to a point where everything is referenced from everywhere.
Make your logger facade a constructor parameter. You can do the same with the error handling block.
Don't use the container as a ServiceLocator. That is considered an anti-pattern in modern software architecture.

Resources