Adding Unity's Diagnostic extension in Prism Xamarin.Forms - xamarin.forms

I get a Unity.ResolutionFailedException and it hints that I can get more information by adding the Diagnostic extension:
Unity.ResolutionFailedException: For more information add Diagnostic extension: Container.AddExtension(new Diagnostic())
I've attempted to add the extension in App.xaml.cs' constructor and in RegisterTypes, but neither seems to work (same message).
public App(IPlatformInitializer initializer)
: base(initializer)
{
this.Container.GetContainer().AddExtension(new Diagnostic());
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.GetContainer().AddExtension(new Diagnostic());
...
On http://unitycontainer.org/tutorials/diagnostic.html, it states:
IMPORTANT
Extension methods will not work on IUnityContainer interface. It is only available on the UnityContainer itself.
Which would explain the behavior I am seeing. So is it possible to add the diagnostic extension?

So is it possible to add the diagnostic extension?
You could just cast to UnityContainer:
using Prism.Unity; // for the GetContainer extension method
((UnityContainer)containerRegistry.GetContainer()).AddExtension(new Diagnostic());
Alternatively, override PrismApplication.CreateContainerExtension and add the extension to the container directly:
protected override IContainerExtension CreateContainerExtension()
{
var container = new UnityContainer();
container.AddExtension( new Diagnostic() );
return new UnityContainerExtension( container );
}

Related

DryIoc and IServiceProvider on Prism for Xamarin.Forms (DryIoc.Microsoft.DependencyInjection)

I've got a Prism application with DryIoc as container.
I'd like IHttpClientFactory to provide HttpClients to my typed clients, which are like this:
public class ExampleService : IExampleService
{
private readonly HttpClient _httpClient;
public RepoService(HttpClient client)
{
_httpClient = client;
}
public async Task<IEnumerable<string>> GetExamplesAsync()
{
// Code deleted for brevity.
}
}
In App.xaml.cs I register my typed client so they can be injected in viewmodels with the following:
public partial class App
// ...
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// Code deleted for brevity.
containerRegistry.Register<IExampleService, ExampleService>();
}
And that's before trying to use IHttpClientFactory.
Now, to add it, we should AddHttpClient() on IServiceCollection. That's where I thought DryIoc.Microsoft.DependencyInjection was needed, so, still in App.xaml.cs, I wrote the following:
public partial class App
// ...
protected override IContainerExtension CreateContainerExtension()
{
var services = new ServiceCollection();
services.AddHttpClient<IExampleService, ExampleService>(c =>
{
c.BaseAddress = new Uri("https://api.example.com/");
});
var container = new Container(CreateContainerRules())
.WithDependencyInjectionAdapter(services);
return new DryIocContainerExtension(container);
}
The problem is that in my ExampleService I'm getting client with the following specs:
{
"DefaultRequestHeaders":[
],
"BaseAddress":null,
"Timeout":"00:01:40",
"MaxResponseContentBufferSize":2147483647
}
whilst I expected BaseAddress to be https://api.example.com/, so the REST API call fails.
What is the correct pattern to use IServiceProvider when using Prism for Xamarin.Forms with DryIoc? Unfortunately there's no documentation or open source code available on the following matter, and I am kind of lost.
Thanks you, and have a great day.
UPDATE #1
As per kind Dan S. guidance, DryIoc.Microsoft.DependencyInjection was uninstalled so the project came back at its state before trying to use IServiceCollection dependencies (in my case, IHttpClientFactory), then I installed Prism.Forms.Extended and later Prism.DryIoc.Extensions.
After that CreateContainerExtension() in App.xaml.cs became:
protected override IContainerExtension CreateContainerExtension()
{
var containerExtension = PrismContainerExtension.Current;
containerExtension.RegisterServices(s =>
{
s.AddHttpClient<IExampleService, ExampleService>(c =>
{
c.BaseAddress = new Uri("https://api.example.com/");
});
});
return containerExtension;
}
and containerRegistry.Register<IExampleService, ExampleService>(); was removed from RegisterTypes().
Now ExampleService finally gets its HttpClient injected and everything is working.
UPDATE #2
The only packages related to Prism I am using are Prism.DryIoc.Forms and Prism.DryIoc.Extensions.
I completely removed the override of CreateContainerExtension() in App.xaml.cs and refactored RegisterTypes() to
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// Code deleted for brevity.
containerRegistry.RegisterServices(s =>
{
s.AddHttpClient<IExampleService, ExampleService>(c =>
{
c.BaseAddress = new Uri("https://api.example.com/");
});
});
}
This way I get thrown a NotImplementedException.
However, by overriding CreateContainerExtension() with the following:
protected override IContainerExtension CreateContainerExtension() => PrismContainerExtension.Current;
Everything is finally back to working!
If you want to use IServiceCollection extensions such as AddHttpClient I would suggest that you use the Prism Container Extensions. In your case it would be Prism.DryIoc.Extensions. The Container Extensions provide a lot of additional support including support for registering services with via the Service Collection extensions.
You can either install Prism.Forms.Extended and it will all just work, or you can update your App as follows:
protected override IContainerExtension CreateContainerExtension() =>
PrismContainerExtension.Current;
Adding as this is the only post I've found in weeks of searching that explains how to do this.
I'm using Unity rather than Dryloc but the solution is the same.
Install ONLY these additional packages:
Prism.Forms.Extended
Prism.Unity.Extensions
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//Omitted Code
containerRegistry.RegisterServices(serviceCollection =>
{
serviceCollection.AddHttpClient<IApiService, ApiService>(client =>
{
client.BaseAddress = new Uri("Your Address Here");
});
});
}
public ApiService(HttpClient client)
{
//Do Stuff
}

PRISM module with DI unable to navigate view

Quite new to Prism's module and I am using prism 7, I have a module that registers its View/ViewModel and other services:
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<LoginView, LoginViewModel>();
containerRegistry.RegisterSingleton<ILoginManager, LoginManager>();
containerRegistry.RegisterSingleton<ILoginAPIService, LoginAPIService>();
}
The other services that are included inside the manager/apiservice are registered in my App.xaml.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IServiceEntityMapper, ServiceEntityMapper>();
}
Who has a ViewModel constructor
ILoginManager LoginManager;
public LoginViewModel(INavigationService navigationService, IPageDialogService pageDialogService, ILoginManager loginManager) : base(navigationService, pageDialogService)
{
LoginManager = loginManager;
}
However, whenever I inject the ILoginManager seems like I cannot navigate to it, it doesn't give me any exception nor crashes, is there something wrong with how I register my services? Also, removing the ILoginManager able me to navigate to it again
Fixed my problem, it was my APIServiceBase that was causing the error as my empty string base address cannot be converted into Uri.

What is the proper way to register view with viewmodels in Prism.DryIoc

I'm using the Prism Template Pack for Visual Studio for Mac to generate a new project (tried both shared and pcl) then updating to 7.0.0.340-ci. Is registration of the views to view models done by convention?
When I try to run this app it throws this exception: Objective-C exception thrown. Name: NSInternalInconsistencyException Reason: Application windows are expected to have a root view controller at the end of application launch.
Here is the code for the main app.
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
NavigationService.NavigateAsync("MainPage?title=Hello%20from%20Xamarin.Forms");
}
protected override void RegisterTypes(Prism.Ioc.IContainerRegistry containerRegistry)
{
Prism.Mvvm.ViewModelLocationProvider.Register<MainPage,MainPageViewModel>();
}
//protected override void RegisterTypes()
//{
// Container.RegisterTypeForNavigation<MainPage>();
//}
}
I had to comment out the bottom lines and redo the override due to incompatible signatures.
Where did the Container.RegisterTypeForNavigation go or what is it's replacement?
I also tried it without any code in the RegisterTypes method.
In a debug session exploring the NavigationService properties says MainPage is null.
The RegisterTypes method should look like this.
In prism forms 7.x pages used for navigation must be registered via RegisterForNavigation<>() or RegisterForNavigation().
protected override void RegisterTypes(Prism.Ioc.IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MainPage,MainPageViewModel>();
}

Nancyfx using Json.net, registering dependencies error

I'm trying to use nancy with JSON.net, follow the 2 ways that i found to register the dependencies but all way get me to an InvalidOperationException with a message "Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and inspect the innerexception for more details." with an inner exection of {"Unable to resolve type: Nancy.NancyEngine"}.
I'm using self hosting to run nancy and jeep everything really simple to been able just to test.
public static void Main(string[] args)
{
try
{
var host = new NancyHost(new Uri("http://localhost:8888/"));
host.Start(); // start hosting
Console.ReadKey();
host.Stop(); // stop hosting
}
catch
{
throw;
}
}
First I create a customSerializer
public class CustomJsonSerializer : JsonSerializer
{
public CustomJsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver();
Formatting = Formatting.Indented;
}
}
and then i tried 2 ways of registering
Using IRegistrations:
public class JsonRegistration : IRegistrations
{
public IEnumerable<TypeRegistration> TypeRegistrations
{
get
{
yield return new TypeRegistration(typeof(JsonSerializer), typeof(CustomJsonSerializer));
}
}
public IEnumerable<CollectionTypeRegistration> CollectionTypeRegistrations { get; protected set; }
public IEnumerable<InstanceRegistration> InstanceRegistrations { get; protected set; }
}
And also using Bootstrapper
public class NancyBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
container.Register<JsonSerializer, CustomJsonSerializer>();
}
}
Which means that when self hosting I add the custom bootstrapper
var host = new NancyHost(new Uri("http://localhost:8888/"), new NancyBootstrapper());
Both way return the same error.
Problem is actually the versions, the nancy json.net package is using Newton.Json 6.0.0.0, BUT when installing the package it will install automatically newer version that will create this problem. Not sure what has change in the Newton.JSON that will actually create this.
https://github.com/NancyFx/Nancy.Serialization.JsonNet/issues/27
Just to add my hard won knowledge in this area, after a greatly frustrating few hours using Nancy 1.4.1.
If you use a custom bootstrapper, make sure you make the call to base.ConfigureApplicationContainer(container); before you start your custom registrations.
So, not:
public class MyCustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
// MY BITS HERE...
base.ConfigureApplicationContainer(container);
}
}
but,
public class MyCustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container); // Must go first!!
// MY BITS HERE...
}
}
If you don't do this you will get the following error:
Something went wrong when trying to satisfy one of the dependencies
during composition, make sure that you've registered all new
dependencies in the container and inspect the innerexception for more
details.
with a helpful inner exception of:
Unable to resolve type: Nancy.NancyEngine
The solution of changing the order of these C# statements was actually alluded to in #StevenRobbins' excellent answer here (which I could have saved myself several hours of pain if I'd only read properly the first time).
Says Steven:
By calling "base" after you've made a manual registration you are
effectively copying over your original registration by autoregister.
Either don't call base, or call it before you do your manual
registrations.

Ninject.Web.PageBase still resulting in null reference to injected dependency

I have an ASP.NET 3.5 WebForms application using Ninject 2.0. However, attempting to use the Ninject.Web extension to provide injection into System.Web.UI.Page, I'm getting a null reference to my injected dependency even though if I switch to using a service locator to provide the reference (using Ninject), there's no issue.
My configuration (dumbed down for simplicity):
public partial class Default : PageBase // which is Ninject.Web.PageBase
{
[Inject]
public IClubRepository Repository { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
var something = Repository.GetById(1); // results in null reference exception.
}
}
...
//global.asax.cs
public class Global : Ninject.Web.NinjectHttpApplication
{
/// <summary>
/// Creates a Ninject kernel that will be used to inject objects.
/// </summary>
/// <returns>
/// The created kernel.
/// </returns>
protected override IKernel CreateKernel()
{
IKernel kernel =
new StandardKernel(new MyModule());
return kernel;
}
..
...
public class MyModule : NinjectModule
{
public override void Load()
{
Bind<IClubRepository>().To<ClubRepository>();
//...
}
}
Getting the IClubRepository concrete instance via a service locator works fine (uses same "MyModule"). I.e.
private readonly IClubRepository _repository = Core.Infrastructure.IoC.TypeResolver.Get<IClubRepository>();
What am I missing?
[Update] Finally got back to this, and it works in Classic Pipeline mode, but not Integrated. Is the classic pipeline a requirement?
[Update 2] Wiring up my OnePerRequestModule was the problem (which had removed in above example for clarity):
protected override IKernel CreateKernel()
{
var module = new OnePerRequestModule();
module.Init(this);
IKernel kernel = new StandardKernel(new MyModule());
return kernel;
}
...needs to be:
protected override IKernel CreateKernel()
{
IKernel kernel = new StandardKernel(new MyModule());
var module = new OnePerRequestModule();
module.Init(this);
return kernel;
}
Thus explaining why I was getting a null reference exception under integrated pipeline (to a Ninject injected dependency, or just a page load for a page inheriting from Ninject.Web.PageBase - whatever came first).
This is fairly puzzling because from what I can tell it appears that you have everything configured correctly. From the fact that you are getting a Null Reference Exception instead of an ActivationException, it would seem that the page level injection does not appear to be happening. Typically this is due to the protection level of the property being injected, but based on your code there is no issue there. Here are some things you can try to help track down what this issue is:
The call to Kernel.Inject(this), which initiates the property injection for Ninject is done in the OnInit method of the PageBase class. If for some reason this method is not getting executed it could result in the issue your seeing. You can do some further investigation by overriding the RequestActivation() method, which is the method called to do the actual injection (be sure to call base.RequestActivation()). If your override is never called, then there is an issue with the OnInit.
The InjectAttribute is set up in the default kernel configuration, so there should not be any need to specify it, however if you wanted to be extra certain, you could set up the attribute mapping in your kernel set up by doing something like:
IKernel kernel = new StandardKernel(new NinjectSettings { InjectAttribute = typeof(InjectAttribute) },new MyModule());
The kernel instance used by the PageBase class for the injection (and likewise the one that should be instantiated by your CreateKernel override in your Global.asax.cs) is stored in a service locator type object in Ninject.Web.KernelContainer. I would make sure you can see the Kernel property on KernelContainer and that it is not null from your Page_Load method.
Thats all I've got at the moment as far as insight. Like I said it appears from here that you have all of your ducks dressed and put in rows, so they should be working....
Good luck tracking down the issue.
This may not be specific to Ninject. I can get the same exception running in integrated mode with no IoC. I just have a simple asp.net app that just contains one aspx page with no logic.
In my global.asax file i have the following:
public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
this.EndRequest += new EventHandler(Global_EndRequest);
}
void Global_EndRequest(object sender, EventArgs e)
{
// do stuff
}
Basically subscribing to an event in the application_start causes this exception for me when running in integrated pipeline mode. Switching to classic pipeline or removing the event subscription and handler makes the error go away. I'm running IIS 7.5 on Win7 Enterprise 64bit.
This may not solve your specific problem but i'm posting here as this is the only page that came up when i pasted the exception into google! I'll move my answer into a separate question when i'm allowed to ask one. I have no stackoverflow kudos yet :(

Resources