ipjohnson / Grace

Grace is a feature rich dependency injection container library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question]: How to perform property injection based on specific type?

Andilie opened this issue · comments

Normally, an interface is defined, but it may have multiple implementation types. I want the container to inject according to the specific type defined on ImportAtrribute. The following is some code. How can I implement it in Grace?

public class ImportAttribute : Attribute
{
    [CanBeNull]
    public Type Type { get; set; }

    [CanBeNull]
    public object Key { get; set; }

    public bool IsRequired { get; set; }

    public ImportAttribute()
    {
    }

    public ImportAttribute(Type type)
    {
        Type = type;
    }
}


public interface IFoo
{
}

public class FooA : IFoo
{
}

public class FooB : IFoo
{
}

public class Boo
{
    [Import(typeof(FooA))]
    public IFoo FooA { get; set; }
    
    [Import(typeof(FooB))]
    public IFoo FooB { get; set; }
}

After getting the Boo instance from the container, the two property of Boo are imported according to the specific type declared on ImportAttribute

I tried to write my own IMemberInjectionSelector, but it doesn't work.

public class MyMemberInjectionSelector : IMemberInjectionSelector
{
    public IEnumerable<MemberInjectionInfo> GetPropertiesAndFields(Type type, IInjectionScope injectionScope, IActivationExpressionRequest request)
    {
        foreach (var declaredMember in type.GetTypeInfo().DeclaredMembers)
        {
            Type importType = null;
            var propertyInfo = declaredMember as PropertyInfo;

            if (propertyInfo != null)
            {
                if (propertyInfo.CanWrite)
                {
                    importType = propertyInfo.PropertyType;
                }
            }
            else if (declaredMember is FieldInfo fieldInfo)
            {
                importType = fieldInfo.FieldType;
            }

            if (importType == null)
            {
                continue;
            }

            var attr = declaredMember.GetAttributeWithDefined<ImportAttribute>();

            if (attr != null)
            {
                // Try to set the default value
                object localValue = null;
                if (attr.Type != null)
                {
                    injectionScope.TryLocate(attr.Type, out localValue);
                }


                yield return new MemberInjectionInfo()
                {
                    MemberInfo = declaredMember,
                    IsRequired = attr.IsRequired,
                    DefaultValue = localValue,//Given value, but doesn't work
                };
            }
        }
    }

    public IEnumerable<MethodInjectionInfo> GetMethods(Type type, IInjectionScope injectionScope, IActivationExpressionRequest request)
    {
        yield break;
    }
}

Currently the default way to do something like this is with keyed exports. I created a unit test showing the pattern, technically the key can be anything that goes in an attribute so you could use type as the key.

Classes
https://github.com/ipjohnson/Grace/blob/master/tests/Grace.Tests/Classes/Simple/KeyedMultipleService.cs

Test
https://github.com/ipjohnson/Grace/blob/master/tests/Grace.Tests/DependencyInjection/AttributeTests/KeyedAttributeTests.cs

That is great! According to the scheme you provided, it worked. Thank you very much for your quick answer. hoped that there will be more schemes to complete such operations in the future. 😁