Prism and Simple Injector - mvvm

I'm trying a simple HelloWorld using simple inject and prism.
Git Source
When application starts, this error come up
Failed to assign to property
'Prism.Windows.Mvvm.ViewModelLocator.AutoWireViewModel'. [Line: 8
Position: 5]"
Exception thrown: 'System.MissingMethodException' in Prism.Windows.dll
Exception thrown: 'Windows.UI.Xaml.Markup.XamlParseException' in
HelloWorldPrism.exe WinRT information: Failed to assign to property
'Prism.Windows.Mvvm.ViewModelLocator.AutoWireViewModel'. [Line: 8
Position: 5] An exception of type
'Windows.UI.Xaml.Markup.XamlParseException' occurred in
HelloWorldPrism.exe but was not handled in user code WinRT
information: Failed to assign to property
'Prism.Windows.Mvvm.ViewModelLocator.AutoWireViewModel'. [Line: 8
Position: 5] Additional information: The text associated with this
error code could not be found. Failed to assign to property
'Prism.Windows.Mvvm.ViewModelLocator.AutoWireViewModel'. [Line: 8
Position: 5]
e.StackTrace " at Windows.UI.Xaml.Application.LoadComponent(Object
component, Uri resourceLocator, ComponentResourceLocation
componentResourceLocation)\r\n at
HelloWorldPrism.Views.MainView.InitializeComponent()\r\n at
HelloWorldPrism.Views.MainView..ctor()" string
<Page
x:Class="HelloWorldPrism.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mvvm="using:Prism.Windows.Mvvm"
mvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
>
public MainViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
}
If I add a parameterless constructor it works normal.
public MainViewModel()
{
}
App.cs
protected override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
{
Window.Current.Activate();
return Task.FromResult(true);
}
protected override void CreateAndConfigureContainer()
{
Logger.Log("Creating and Configuring Container", Category.Debug, Priority.Low);
Container = CreateContainer();
}
protected override Container CreateContainer()
{
return new Container();
}
protected override UIElement CreateShell(Frame rootFrame)
{
var shell = Container.GetInstance<MainView>();
shell.SetFrame(rootFrame);
return shell;
}
protected override Type GetPageType(string pageToken)
{
var type = Type.GetType(string.Format(CultureInfo.InvariantCulture, GetType().AssemblyQualifiedName.Replace(GetType().FullName, GetType().Namespace + ".Views.{0}View"), pageToken));
if (type != null)
return type;
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, ResourceLoader.GetForCurrentView("/Prism.Windows/Resources/").GetString("DefaultPageTypeLookupErrorMessage"), pageToken, GetType().Namespace + ".Views"), nameof(pageToken));
}
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
Container.RegisterSingleton(SessionStateService);
Container.RegisterSingleton(DeviceGestureService);
Container.RegisterSingleton(NavigationService);
Container.RegisterSingleton(EventAggregator);
return Task.CompletedTask;
}
protected override void ConfigureViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => new
SimpleInjectorServiceLocatorAdapter(Container));
}

When application starts, this error come up
In the MainView.xaml, you defined the AutoWireViewModel property to true. Once this property is set to True, the ViewModelLocator will try to instantiate the corresponding ViewModel based on a particular convention. Since the names of your View and ViewModel met the convention, when you set this property to true, Prism will help you instantiate the corresponding ViewModel.
Inside Prism.mvvm namespace, the ViewModelLocationProvider class locates the view model for the view that has the AutoWireViewModelChangedattached property set to true. And the error is thrown by the following code line of ViewModelLocationProvider class:
/// <summary>
/// The default view model factory which provides the ViewModel type as a parameter.
/// </summary>
static Func<Type, object> _defaultViewModelFactory = type => Activator.CreateInstance(type);
System.MissingMethodException: 'No parameterless constructor defined for this object.'
So it is caused by the Activator.CreateInstance(Type) method requires a public constructor, please see MissingMethodException.
If I add a parameterless constructor it works normal.
It seems like this is the correct solution. If you just don't want a parameterless constructor for the ViewModel, you may try instantiate it and set the DataContext to the View by yourself. If you doubt this is an issue for Prism library maybe you can open a thread here.
Update:
According to #rubStackOverflow, it was missing ViewModelLocationProvider.SetDefaultViewModelFactory((viewMo‌​delType) => Container.GetInstance(viewModelType)); on OnInitializeAsync method.

Related

Warning CS0618: 'ComplexTypeModelBinder' is obsolete

In .NET Core 3 I extended ComplexTypeModelBinder class to create custom model binder for a specific class.
Main target was to override creation of model instance via CreateModel method.
public MyModelBinder: ComplexTypeModelBinder
{
// Constructor here...
protected override object CreateModel(ModelBindingContext bindingContext)
{
// Create model instance based on custom condition.
}
}
After an upgrade to .NET 5, I'm receiving a compilation warning to replace ComplexTypeModelBinder with ComplexObjectModelBinder:
warning CS0618: 'ComplexTypeModelBinder' is obsolete: 'This type is obsolete and will be removed in a future version. Use ComplexObjectModelBinder instead.'
The problem is that ComplexObjectModelBinder is sealed class and CreateModel method is internal, so subclass approach cannot work any more.
The solution I settled for is to create class MyModelBinder which implement IModelBinder interface and acts as a wrapper around ComplexObjectModelBinder. ComplexObjectModelBinder is passed in MyModelBinder as constructor parameter.
Same approach is used for MyModelBinderProvider and ComplexObjectModelProvider.
Later, instead of overriding CreateMethod of ComplexObjectModelBinder, which is not possible in .NET 5, just assign bindingContext.Model in the wrapper method BindAsyncModel of MyModelBinder, before passing the bindingContext to ComplexObjectModelBinder's BindAsyncModel. This will make ComplexObjectModelBinder use model that is passed in, instead of creating default new instance.
public class MyModelBinder: IModelBinder
{
private readonly ComplexObjectModelBinder _binder;
public MyModelBinder(ComplexObjectModelBinder binder)
{
_binder = binder;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
MyModel myModel = // custom instantiation logic here
bindingContext.Model = myModel;
return _binder.BindModelAsync(bindingContext);
}
}
For the registration of the custom model binder provider, first we find existing, automatically registered ComplexObjectBinderProvider, use it in costructor of MyModelBinderProvider and then insert MyModelBinderProvider in front of ComplexObjectBinderProvider.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options => {
// more configurations here...
var bindingProvider = options.ModelBinderProviders.First(provider =>
provider is ComplexObjectModelBinderProvider) as ComplexObjectModelBinderProvider;
var indexOf = options.ModelBinderProviders.IndexOf(bindingProvider);
options.ModelBinderProviders.Insert(indexOf, new MyModelBinderProvider(bindingProvider));
}
}

How to resolve public class with internal constructor on AutoFac

I have this class to be instantiated in a unittest:
public class Customer
{
internal Customer(Guid id) {
// initialize property
}
}
If I instantiate the test class from another (unittests) assembly with a new Customer() works because I added [assembly: InternalsVisibleTo("MyProject.Tests")]
var sut = new Customer(Guid.NewGuid()); // works
But when i setup an autofac container in the other (unittest) assembly
var builder = new ContainerBuilder();
builder.RegisterType<Customer>().AsSelf();
var container = builder.Build();
I can't resolve with autofac.
var theParam = new NamedParameter("id", Guid.NewGuid());
_sut = container.Resolve<Customer>(theParam); // throws exception
My best guess was that the internal constructor was not available. But adding [assembly: InternalsVisibleTo("Autofac")] next to the other doesn't help.
The exception thown by Autofac is
Autofac.Core.DependencyResolutionException:
An error occurred during the activation of a particular registration. See the inner exception for details.
Registration: Activator = Customer (ReflectionActivator),
Services = [MyProject.Customer],
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime,
Sharing = None,
Ownership = OwnedByLifetimeScope
---> No accessible constructors were found for the type 'MyProject.Customer'.
Can Autofac not handle internal constructors?
Autofac can't locate non-public constructors because it uses the DefaultConstructorFinder class which searches only for public constructors by default.
You have to create your custom implementation of the IConstructorFinder interface like this:
public class AllConstructorFinder : IConstructorFinder
{
private static readonly ConcurrentDictionary<Type, ConstructorInfo[]> Cache =
new ConcurrentDictionary<Type, ConstructorInfo[]>();
public ConstructorInfo[] FindConstructors(Type targetType)
{
var result = Cache.GetOrAdd(targetType,
t => t.GetTypeInfo().DeclaredConstructors.Where(c => !c.IsStatic).ToArray());
return result.Length > 0 ? result : throw new NoConstructorsFoundException(targetType);
}
}
Then you have to use the FindConstructorsWith extension method on type registration:
builder.RegisterType<Customer>()
.FindConstructorsWith(new AllConstructorFinder())
.AsSelf();
The InternalsVisibleToAttribute can't help in this case because it affects only the compile time.
if you want to register assembly types with at least only has one public constructor then you can use Autofac Where extension method like this.
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.GetConstructors().Length > 0) //only public constructors
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
Note: PublicOnly extension method registers only public types.

Autofac fails to resolve enumerable of typed HttpClients

I have a number of services which require usage of typed HttpClient from HttpClientFactory.
Though I can resolve one service I can't resolve IEnumerable of this services.
interface IMyHttpClient
{
}
class MyHttpClient: IMyHttpClient
{
public MyHttpClient(HttpClient client)
{
}
}
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddHttpClient()
.AddHttpClient<IMyHttpClient, MyHttpClient>();
var builder = new ContainerBuilder();
// Exception goes away when remove this line
builder.RegisterType<MyHttpClient>().As<IMyHttpClient>();
builder.Populate(services);
var provider = builder.Build();
// ============== This works
// provider.Resolve<IMyHttpClient>();
// ============== This throws exception
provider.Resolve<IEnumerable<IMyHttpClient>>();
}
}
Constructor will be called once and than exception is thrown:
```
DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'ConsoleApp2.MyHttpClient' can be invoked with the available services and parameters:
Cannot resolve parameter 'System.Net.Http.HttpClient client' of constructor 'Void .ctor(System.Net.Http.HttpClient)'.
```
The issue is that AddHttpClient adds it's own registration of IMyHttpClient. But I do want to register using Autofac only! Is there a way to use typed clients but still stay with Autofac?
The exception explains that Autofac can't resolve parameter 'System.Net.Http.HttpClient client'. I suppose this is because such type wasn't registered in your container for second resgistration of IMyHttpClient. To save an advantages of HttpClientFactory you can register excplicit constructor parameter for example like this:
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddHttpClient();
var builder = new ContainerBuilder();
// exclicit resolving client for constructor
builder.RegisterType<MyHttpClient>().As<IMyHttpClient>().WithParameter(
(p, ctx) => p.ParameterType == typeof(HttpClient),
(p, ctx) => ctx.Resolve<IHttpClientFactory>().CreateClient());
builder.Populate(services);
var provider = builder.Build();
// ============== This works
provider.Resolve<IMyHttpClient>();
// ============== This works too
provider.Resolve<IEnumerable<IMyHttpClient>>();
}
In this example Resolve<IEnumerable<IMyHttpClient>> return enumeration with single IMyHttpClient, that is initialized with HttpClient from core HttpClientFactory.
UPD: the post was updated by #norekhov comment

Ninject Conventions with Ninject Factory Extension To Bind Multiple Types To One Interface

I'm trying to expand on the scenario asked in the SO question titled Ninject Factory Extension Bind Multiple Concrete Types To One Interface by using Ninject Conventions for convention-based binding of the ICar implementations.
I'm working off the accepted answer authored by Akim and his Gist outlining the full example.
The difference is that I've replaced the explicit ICar bindings with convention-based bindings (or an attempt at it, at least ;)
public class CarModule : NinjectModule
{
public override void Load()
{
Bind<ICarFactory>()
.ToFactory(() => new UseFirstArgumentAsNameInstanceProvider());
// my unsuccessful binding
Kernel.Bind(scanner => scanner
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<ICar>()
.BindAllInterfaces());
//Bind<ICar>()
// .To<Mercedes>()
// .Named("Mercedes");
//Bind<ICar>()
// .To<Ferrari>()
// .Named("Ferrari");
}
}
When I attempt to instantiate the car variable in the test, I get an ActivationException:
Ninject.ActivationException was unhandled by user code
Message=Error activating ICar
No matching bindings are available, and the type is not self-bindable.
Activation path:
1) Request for ICar
Suggestions:
1) Ensure that you have defined a binding for ICar.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
Source=Ninject
StackTrace:
at Ninject.KernelBase.Resolve(IRequest request) in c:\Projects\Ninject\ninject\src\Ninject\KernelBase.cs:line 362
at Ninject.ResolutionExtensions.GetResolutionIterator(IResolutionRoot root, Type service, Func`2 constraint, IEnumerable`1 parameters, Boolean isOptional, Boolean isUnique) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 263
at Ninject.ResolutionExtensions.Get(IResolutionRoot root, Type service, String name, IParameter[] parameters) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 164
at Ninject.Extensions.Factory.Factory.InstanceResolver.Get(Type type, String name, Func`2 constraint, ConstructorArgument[] constructorArguments, Boolean fallback) in c:\Projects\Ninject\ninject.extensions.factory\src\Ninject.Extensions.Factory\Factory\InstanceResolver.cs:line 75
at Ninject.Extensions.Factory.StandardInstanceProvider.GetInstance(IInstanceResolver instanceResolver, MethodInfo methodInfo, Object[] arguments) in c:\Projects\Ninject\ninject.extensions.factory\src\Ninject.Extensions.Factory\Factory\StandardInstanceProvider.cs:line 78
at Ninject.Extensions.Factory.FactoryInterceptor.Intercept(IInvocation invocation) in c:\Projects\Ninject\ninject.extensions.factory\src\Ninject.Extensions.Factory\Factory\FactoryInterceptor.cs:line 57
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.ICarFactoryProxy.CreateCar(String carType)
at Ninject.Extensions.Conventions.Tests.NinjectFactoryTests.A_Car_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument() in C:\Programming\Ninject.Extensions.Conventions.Tests\NinjectFactoryTests.cs:line 33
InnerException:
How can I get this test to pass?
[Fact]
public void A_Car_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument()
{
// auto-module loading is picking up my CarModule - otherwise, use:
// using (StandardKernel kernel = new StandardKernel(new CarModule()))
using (StandardKernel kernel = new StandardKernel())
{
// arrange
string carTypeArgument = "Mercedes";
ICarFactory factory = kernel.Get<ICarFactory>();
// act
var car = factory.CreateCar(carTypeArgument);
// assert
Assert.Equal(carTypeArgument, car.GetType().Name);
}
}
Here's the rest of the code, as condensed as possible, so that you don't have to refer to the original question
public interface ICarFactory { ICar CreateCar(string carType); }
public interface ICar { void Drive(); void Stop(); }
public class Mercedes : ICar {
public void Drive() { /* mercedes drives */ }
public void Stop() { /* mercedes stops */ }
}
public class Ferrari : ICar {
public void Drive() { /* ferrari drives */ }
public void Stop() { /* ferrari stops */ }
}
public class UseFirstArgumentAsNameInstanceProvider : StandardInstanceProvider
{
protected override string GetName(MethodInfo methodInfo, object[] arguments)
{
return (string) arguments[0];
}
protected override ConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments)
{
return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray();
}
}
Looks like, you have to define binding differently and provide your custom implementation of IBindingGenerator for this case
Binding
All implementation of ICar will have custom binding
Kernel.Bind(scanner => scanner
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<ICar>()
.BindWith(new BaseTypeBindingGenerator<ICar>()));
Custom IBindingGenerator implementation
Searching for all implementations of interface and bind them by type name
public class BaseTypeBindingGenerator<InterfaceType> : IBindingGenerator
{
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
if (type != null && !type.IsAbstract && type.IsClass && typeof(InterfaceType).IsAssignableFrom(type))
{
yield return bindingRoot.Bind(typeof(InterfaceType))
.To(type)
.Named(type.Name) as IBindingWhenInNamedWithOrOnSyntax<object>;
}
}
ps: here is a full sample

Autofac parameterless constructor selection

"Autofac automatically chooses the constructor with the most parameters that are able to be obtained from the container." I want it to do otherwise and choose the default constructor instead. http://code.google.com/p/autofac/wiki/Autowiring
internal class ParameterlessConstructorSelector : IConstructorSelector
{
#region Implementation of IConstructorSelector
/// <summary>
/// Selects the best constructor from the available constructors.
/// </summary>
/// <param name="constructorBindings">Available constructors.</param>
/// <returns>
/// The best constructor.
/// </returns>
public ConstructorParameterBinding SelectConstructorBinding(ConstructorParameterBinding[] constructorBindings)
{
return constructorBindings.First();
}
#endregion
}
When I wire the class, I did this:
builder.RegisterType<EmployeeFactory>()
.As<IEmployeeFactory>().UsingConstructor(new ParameterlessConstructorSelector())
.SingleInstance();
The first binding in the constructorBindings list is always the one with paremeterless constructor. Not sure if it defined first or the way autofac scans the constructors but is this the right approach to wire for parameterless constructor?
Thanks
Autofac internally uses the Type.GetConstructors method to discover the constructors.
From the methods documentation:
The GetConstructors method does not return constructors in a
particular order, such as declaration order. Your code must not depend
on the order in which constructors are returned, because that order
varies.
So it was just luck that it worked with the First() in your case. In a proper implementation you need to explicitly search for the constructor with 0 arguments:
public class DefaultConstructorSelector : IConstructorSelector
{
public ConstructorParameterBinding SelectConstructorBinding(
ConstructorParameterBinding[] constructorBindings)
{
var defaultConstructor = constructorBindings
.SingleOrDefault(c => c.TargetConstructor.GetParameters().Length == 0);
if (defaultConstructor == null)
//handle the case when there is no default constructor
throw new InvalidOperationException();
return defaultConstructor;
}
}
You can test the theory with this very simple class:
public class MyClass
{
public readonly int i;
public MyClass(int i)
{
this.i = i;
}
public MyClass()
{
i = 1;
}
}
With your implementation:
var builder = new ContainerBuilder();
// register 22 for each integer constructor argument
builder.Register<int>(v => 22);
builder.RegisterType<MyClass>().AsSelf()
.UsingConstructor(new ParameterlessConstructorSelector());
var c = builder.Build();
var myClass = c.Resolve<MyClass>();
Console.WriteLine(myClass.i);
It outputs 22 e.g the constructor with the int argument is called:
With my implementation:
//...
builder.RegisterType<MyClass>().AsSelf()
.UsingConstructor(new DefaultConstructorSelector());
//...
var myClass = c.Resolve<MyClass>();
Console.WriteLine(myClass.i);
It outputs 1 e.g the default constructor is called.
Wouldn't it be simpler to just explicitly register the default constructor?
builder.Register<EmployeeFactory>(c => new EmployeeFactory())
.As<IEmployeeFactory>()
.SingleInstance();
With recent versions of Autofac, this is very simple :
builder.RegisterType<EmployeeFactory>()
.As<IEmployeeFactory>().UsingConstructor()
.SingleInstance();
Calling "UsingConstructor" with no parameters means "use parameterless constructor".
See https://autofac.org/apidoc/html/EB67DEC4.htm and related pages.