简体   繁体   中英

Chain of dependencies using Unity Dependency Injection and Xamarin.Forms

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 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.

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.

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 !
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM