Reputation: 814
I'm new to Dependency Injection, and I'm developing an App using Xamarin.Forms, Prism and Unity. So as far as I know, when using DI you want to supply/inject services to classes so they don't have to get them. Resulting in classes that have no knowledge of service implementations.
That means using Constructor Injection instead of using the Container to resolve the services. Also seen here http://structuremap.github.io/quickstart.
My setup:
[assembly: Xamarin.Forms.Dependency(typeof(Foo))]
public class Foo : IFoo
{
public IBar Bar { get; private set; }
public Foo(IBar bar)
{
Bar = bar;
}
}
[assembly: Xamarin.Forms.Dependency(typeof(Bar))]
public class Bar : IBar
{ }
Now if I would try to resolve IFoo an exception is thrown: System.MissingMethodException: Default constructor not found for type Foo
What is going on here?
I have also tried adding an empty constructor to Foo, but this results in Bar being null
and forces me to resolve it from the IUnityContainer.
Upvotes: 0
Views: 963
Reputation: 814
Thanks to @snowCrabs, I learned that Xamarin.Forms does not facilitate Dependency Injection. So I decided to make a base class to enable the resolving of dependencies via reflection.
My main motivation for using the Xamarin DepedencyService is because I like the way you can use [assembly: Xamarin.Forms.Dependency(typeof(Foo))]
to register your classes. Which means in my App project, I don't have to do any registering. All I have to do it add a reference to my library project and the DependencyService will register it for me, allowing me to immediately resolve the interface for usage.
The code:
public ServiceBase()
{
IEnumerable<PropertyInfo> dependencyProperties;
var dependencyAttribute = typeof(Microsoft.Practices.Unity.DependencyAttribute);
// DependencyService.Get<> requires a type parameter so we have to call it by using reflection
var dependencyServiceGet = typeof(DependencyService).GetRuntimeMethod("Get", new Type[] { typeof(DependencyFetchTarget) });
// Get the properties from our derrived type (accessed by using "this")
dependencyProperties = this.GetType().GetTypeInfo().DeclaredProperties;
//Check if any properties have been tagged with the DependencyAttribute
dependencyProperties = dependencyProperties.Where(x =>
{
return x.CustomAttributes.Any(y => y.AttributeType == dependencyAttribute);
});
foreach (var prop in dependencyProperties)
{
// Add the type parameter to the Get-method
var resolve = dependencyServiceGet.MakeGenericMethod(prop.PropertyType);
// Now resolve the property via reflection: DependencyService.Get<PropertyType>();
var service = resolve.Invoke(null, new object[] { DependencyFetchTarget.GlobalInstance });
if (service == null)
throw new InvalidOperationException($"Herke.Forms.Core.ServiceBase: Dependency could not be resolved, did you forget to register?{Environment.NewLine}Type: {prop.PropertyType.FullName}{Environment.NewLine}Suggested code: \"[assembly: Dependency(typeof( >ImplementatingClass< ))]\"");
// Fill property value
prop.SetValue(this, service);
}
}
[assembly: Xamarin.Forms.Dependency(typeof(Bar))]
public class Bar : IBar
{ }
[assembly: Xamarin.Forms.Dependency(typeof(Foo))]
public class Foo : ServiceBase, IFoo
{
[Microsoft.Practices.Unity.Dependency]
public IBar Bar { get; set; }
// Foo can use Bar now !
}
Upvotes: 0
Reputation: 785
Roughly put as far as I can tell the Xamarin forms dependency services(which it looks like you are using) doesn't provide constructor injection. So if you want to do constructor based injection you will need to use a different container service.
IF you want to use the built in Forms service the following changes to your code should work..
[assembly: Xamarin.Forms.Dependency(typeof(Foo))]
public class Foo : IFoo
{
public IBar Bar { get; set; }
public Foo(IBar bar)
{
Bar = bar;
}
}
[assembly: Xamarin.Forms.Dependency(typeof(Bar))]
public class Bar : IBar
{ }
Then to get your object set:
var myFoo = Xamarin.Forms.Dependency.Get<IFoo>();
myFoo.Bar = Xamarin.Forms.Dependency.Get<IBar>();
Otherwise you might want to look into another DI framework.
Upvotes: 1