Skip to content

Support for required properties #1355

@TonyValenti

Description

@TonyValenti

Problem Statement

Traditionally, Autofac creates objects by calling one of their constructors. The constructor accepts dependencies and then assigns/initializes values.

Starting in C#11, dependencies can also be expressed using the required keyword. When a property is required, if the constructor that was called does not have the [SetsRequiredMembers] attribute, the property must have a value set immediately following construction.

Currently AutoFac does not support this core enhancement to object construction.

Desired Solution

When Autofac constructs an object, the constructor should be examined for [SetsRequiredMembers]. If it is not found, all writable properties containing the RequiredMemberAttribute attribute should be injected.

    public class Foo1 { 
        //This should be injected
        public required Dependency? Dep { protected get; init; }
    }

    public class Foo2 {
        //This should not
        public required Dependency? Dep { protected get; init; }

        [SetsRequiredMembers]
        public Foo2() {
            this.Dep = null;
        }

    }

Additional Context

Because C#11 introduces this core-level enhancement to object construction, the above rules should always be enabled.

The following code will be helpful in implementing this enhancement:

    public class PS : IPropertySelector {
        public static IPropertySelector Instance { get; }

        private static string RequiredAttributeName { get; }
        private static string SetsRequiredMembersAttributeName { get; }

        static PS() {
            Instance = new PS();

            //The type names are stored as strings so that this can also be used with legacy .NET platforms
            RequiredAttributeName = "System.Runtime.CompilerServices.RequiredMemberAttribute";
            SetsRequiredMembersAttributeName = "System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute";
        }

        public bool NeedsPropertyInjection(ConstructorInfo constructorInfo) {
            var SetsRequiredMembers = constructorInfo.GetCustomAttributesData().Any(x => string.Equals(x.AttributeType.FullName, SetsRequiredMembersAttributeName, StringComparison.InvariantCultureIgnoreCase));

            var ret = !SetsRequiredMembers;

            return ret;

        }

        public bool InjectProperty(PropertyInfo propertyInfo, object instance) {
            var ret = propertyInfo.CanWrite
                && propertyInfo.GetCustomAttributesData().Any(x => string.Equals(x.AttributeType.FullName, RequiredAttributeName, StringComparison.InvariantCultureIgnoreCase))
                ;

            return ret;

        }
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions