A place for spare thoughts

12/09/2012

Property Injection for infrastructural dependencies in practice, part 2

Filed under: c# — Ivan Danilov @ 21:41

In the first part I introduced RequiredPropertyAttribute and integrated it with Windsor. And the problem of preventing manual instantiation of such classes (because manually nobody is forcing us to treat such properties as required in any way) remained open.

So, the best thing possible from my point of view would be compiler emitting warning each time we create such classes. Then you will have a clear sign you’re doing something not-intended. Or, if you have “Treat compiler warnings as errors” – it will not allow you to even compile such code.

I believe with Roslyn it is not so hard to write custom inspection for this and warn user whenever required. But for now it is not that easy unfortunately. At least I haven’t found better way than described below (if you know one – let me know, please).

The closest thing (actually, surprisingly close to our needs) is ObsoleteAttribute on the class constructor. It has several advantages:

  • compiler emits warning only if it is used, so we don’t need to analyze code ourselves;
  • it is totally ignored by IoCC and other reflection-based approaches;
  • we may customize warning text to let user know that instantiating of a class should be done via IoCC rather then manually;
  • compiler enforces “viral propagating” of the attribute.

Let me elaborate a bit on the last point. Suppose you have this class:

    public class A
    {
        [Obsolete("You should not call this contructor manually. It should be called only by IoC container to avoid missing dependencies")]
        public A() {}
    }

And someone writes that one:

    public class B : A {}

Oops. Immediate warning: “‘A.A()’ is obsolete”. In order to have such class without warning you have to write that code:

    public class B : A
    {
        [Obsolete]
        public B() {}
    }

And that is a good thing, obviously.

Now, some disadvantages clearly present as well. First off, it is a hack and using Obsolete in not-intended way. Also you have no means to avoid setting this long string line if you want clear warning message (ObsoleteAttribute class is sealed unfortunately). And finally, if you’re developing some library and making it backward-compatible – there’re good chances you need Obsolete for your own legitimate use. Thus said, if you want to incorporate this approach – you will be marking as Obsolete some internal implementations which probably will not be seen by user of your library anyway.

And obviously, this approach doesn’t defend you against reflection. But reflection is absolutely another story where you have much less guarantees by default, so I think it is ok.

Though I’m not absolutely content with this solution – it works and I can’t think out something better right now. And I can move my app to property injection more or less sure that required properties will be really required, and these requirements would be checked as early as possible.

Advertisements

Property Injection for infrastructural dependencies in practice, part 1

Filed under: c# — Ivan Danilov @ 21:41

Inspired by this post I tried this approach myself. If you haven’t read it yet – it’s time to spend several minutes there.

The main reason I was reluctant to use “infrastructure dependencies as properties, application-level dependencies as ctor arguments” is that it is very easy to miss the required property and find oneself one day in debugging because of some silly configuration error or forgetting to set a property. I strive to use static checking as much as possible and detect errors in compile-time or build-time (on build server where some additional checks are taking place).

On Inversion of Control Container (IoCC) side it can be solved – as Krzysztof suggested – via configuring so that it treats them as required. Here I will show things using Castle Windsor 3.0, but the approach overall is not specific to concrete container.

The default way in Windsor to make property required is at configuration time shown here. Personally I don’t like that very much, because in this way you need to find registration and look through it in order to understand if some property will be filled always or not. It is much cleaner and easier to read if it is immediately obvious right in the property declaration place, thus making custom attributes natural choice. As far as I’m aware Windsor don’t have built-in attribute for that, so let’s make our own.

It is easy if you know where to look. I have to admit I spent about an hour to find the place and another 15 minutes to write implementation and tests for it.

Here you go:

    [AttributeUsage(AttributeTargets.Property)]
    public sealed class RequiredPropertyAttribute : Attribute
    {
    }

    public class RespectRequiredPropertiesContributor : IContributeComponentModelConstruction
    {
        public void ProcessModel(IKernel kernel, ComponentModel model)
        {
            var markedProperties = model.Properties
                .Where(p => p.Property
                                .GetCustomAttributes(typeof(RequiredPropertyAttribute), true)
                                .FirstOrDefault() != null);
            foreach (var p in markedProperties)
            {
                p.Dependency.IsOptional = false;
            }
        }
    }

And here are the tests:

    public class Dependency
    {
    }

    public class ClassWithoutRequiredProperties
    {
        public string StringProperty { get; set; }
    }

    public class ClassWithRequiredProperty
    {
        [RequiredProperty]
        public Dependency Dependency { get; set; }
    }

    public class ClassWithInheritedRequiredProperty : ClassWithRequiredProperty
    {
    }

    [TestFixture]
    public class WindsorRespectRequirementPropertiesTests
    {
        private IWindsorContainer _windsor;

        [SetUp]
        public void Setup()
        {
            _windsor = new WindsorContainer();
            _windsor.Kernel.ComponentModelBuilder.AddContributor(new RespectRequiredPropertiesContributor());
        }

        [Test]
        public void NoPropertiesAreRequired_ComponentCreated()
        {
            _windsor.Register(Component.For<ClassWithoutRequiredProperties>());
            var obj = _windsor.Resolve<ClassWithoutRequiredProperties>();
            Assert.NotNull(obj);
            Assert.IsNull(obj.StringProperty);
        }

        [Test]
        public void SomePropertiesAreRequired_DependencyMissing_Throws()
        {
            _windsor.Register(Component.For<ClassWithRequiredProperty>());
            Assert.Throws<HandlerException>(() => _windsor.Resolve<ClassWithRequiredProperty>());
        }

        [Test]
        public void SomePropertiesAreRequired_DependencyExists_ResolveSuccessfull()
        {
            _windsor.Register(Component.For<ClassWithRequiredProperty>());
            _windsor.Register(Component.For<Dependency>());
            var obj = _windsor.Resolve<ClassWithRequiredProperty>();
            Assert.IsNotNull(obj);
            Assert.IsNotNull(obj.Dependency);
        }

        [Test]
        public void SomePropertiesAreRequiredInBaseClass_DependencyMissing_Throws()
        {
            _windsor.Register(Component.For<ClassWithInheritedRequiredProperty>());
            Assert.Throws<HandlerException>(() => _windsor.Resolve<ClassWithInheritedRequiredProperty>());
        }

        [Test]
        public void SomePropertiesAreRequiredInBaseClass_DependencyExists_ResolveSuccessfull()
        {
            _windsor.Register(Component.For<ClassWithInheritedRequiredProperty>());
            _windsor.Register(Component.For<Dependency>());
            var obj = _windsor.Resolve<ClassWithInheritedRequiredProperty>();
            Assert.IsNotNull(obj);
            Assert.IsNotNull(obj.Dependency);
        }
    }

Now, you may use this attribute and Windsor will treat both constructor dependencies and marked property dependencies equally required.

But still, there’s one problem. What if someone will create some class by manually calling the constructor? Well, maybe he even will be attentive enough and checks which properties are required and sets them properly. But still such code would be defenseless against change in the class being created. What happens if later another (not so attentive) developer adds some required property? Clearly, he needs to check every site where class is created manually and satisfy that new dependency. In other words, allowing to create such classes manually is a way to make IoCC’s work by hand. Clearly not a good thing – if we have IoCC – we should use it, otherwise why it clutters the code?

But how one can prevent creating some classes in code, but still allow IoCC to create them? I will describe one way in part 2 shortly. It is not perfect, but it works and makes unintended error much less likely.

Blog at WordPress.com.