Search code examples
c#dependency-injectionninjectcontextual-binding

How can I retrieve the attribute and the properties when using Ninject contextual binding?


I have a constructor

[ReadFromFile(@"C:\SampleData\login.json")]
public AccountController(IReadRepository<LoginMockDataModel> repository, string filePath) : base(repository)
{
}

The attribute contains a property "FilePath".

public string FilePath {get;set;}

I would like to retrieve the value of "FilePath" which would be "C:\SampleData\login.json" in the above case.

Is it possible to retrieve the value using Ninject's IContext?

The idea is to retrieve the property's value and then use it in the binding as follows:

// FileReadRepo contains a constructor with the argument "filePath"
// which will need a string passed to it which I am trying to retrieve
// from the Attribute above
Bind(typeof(IReadRepository<>)).To(typeof(FileReadRepo<>))
.WhenMemberHas<ReadFromFileAttribute>()
.WithConstructorArgument("filePath", CheckAttributePath);

where CheckAttributePath would be the delegate:

private object CheckAttributePath(IContext arg)
{
    throw new NotImplementedException();
}

I'm not sure how to obtain the attribute's value.


Solution

  • Accessing the constructor of the AccountController is done through IContext.Request.Target.Member. So this works:

    private static object CheckAttributePath(IContext context)
    {
        var attributes = context.Request.Target.Member
            .GetCustomAttributes(typeof(ReadFromFileAttribute), false);
        return ((ReadFromFileAttribute)attributes[0]).Path;
    }
    

    Complete test code (employs xunit and FluentAssertions):

    using System;
    using Ninject;
    using Ninject.Activation;
    using Xunit;
    using FluentAssertions;
    
    public interface IReadRepository<T>
    {
        string FilePath { get; }
    }
    
    public class FileReadRepo<T> : IReadRepository<T>
    {
        private readonly string filePath;
    
        public FileReadRepo(string filePath)
        {
            this.filePath = filePath;
        }
    
        public string FilePath { get { return this.filePath; } }
    }
    
    [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
    public class ReadFromFileAttribute : Attribute
    {
        public readonly string Path;
    
        public ReadFromFileAttribute(string path)
        {
            this.Path = path;
        }
    }
    
    public class AccountController
    {
        public readonly IReadRepository<string> Repository;
    
        [ReadFromFile(IntegrationTest.SampleFilePath)]
        public AccountController(IReadRepository<string> repository)
        {
            this.Repository = repository;
        }
    }
    
    public class IntegrationTest
    {
        public const string SampleFilePath = @"C:\SampleData\login.json";
    
        [Fact]
        public void Test()
        {
            var kernel = new StandardKernel();
    
            kernel.Bind(typeof(IReadRepository<>)).To(typeof(FileReadRepo<>))
                .WhenMemberHas<ReadFromFileAttribute>()
                .WithConstructorArgument("filePath", CheckAttributePath);
    
            kernel.Get<AccountController>().Repository.FilePath.Should().Be(SampleFilePath);
        }
    
        private static object CheckAttributePath(IContext context)
        {
            var attributes = context.Request.Target.Member.GetCustomAttributes(
                typeof(ReadFromFileAttribute), false);
            return ((ReadFromFileAttribute)attributes[0]).Path;
        }
    }