Setting dependancies prior to aspect enhanced classes constructor.

daniel.comerford's Avatar

daniel.comerford

05 Sep, 2018 09:57 AM

Hi,

I'm currently using one aspect to try and introduce an interface which contains some dependencies required for other aspects. The aim here is to automate the dependency injection by wrapping object creation. I have a current approach using FormatterServices.GetUninitializedObject(Type) and then manually initializing the aspect using reflection. After this I call the constructor (again through reflection). The issue I'm having is that any dependencies set before the constructor call are over written during. Looking at the decompiled assembly in ILSpy I'm guessing that the call to <>z__InitializeAspects(AspectInitializationReason.Constructor) re-initializes the aspect (with another deserialized copy?). Is there any way to avoid the re-initialization at all? Or is there another way to accomplish what I'm trying to achieve. Using PostSharp 6.0.25.0. A code example showing an example of the implementation is below:

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.Reflection;
using System.Diagnostics;

using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace PostSharpTest
{
    public interface IDependant
    {
        int DependantProperty { get; set; }
    }

    [IntroduceInterface(typeof(IDependant))]
    [PSerializable]
    public class Dependant : InstanceLevelAspect, IDependant
    {
        public int DependantProperty { get; set; }
    }

    public static class DependantObjectExtentsions
    {
        public static T Construct<T>(this object parentObject, params object[] constructorParameters)
        {
            IDependant parent = (IDependant)parentObject;

            Type childType = typeof(T);

            IDependant child = (IDependant)FormatterServices.GetUninitializedObject(childType);

            childType.GetMethod
            (
                "InitializeAspects",
                BindingFlags.NonPublic | BindingFlags.Instance
            )
            .Invoke(child, new object[0] { });

            child.DependantProperty = parent.DependantProperty;

            Console.WriteLine("Before construction (Expected a value of 1): " + child.DependantProperty.ToString());

            childType.GetConstructor
            (
                constructorParameters.Select(x => x.GetType()).ToArray()
            )
            .Invoke(child, constructorParameters);

            return (T)child;
        }
    }

    [Dependant]
    public class DependantChild
    {
    }

    [Dependant]
    public class DependantParent
    {
        public DependantChild CreateChild()
        {
            return this.Construct<DependantChild>();
        }
    }

    public static class Program
    {
        public static void Main(string[] args)
        {
            DependantParent parent = new DependantParent();

            ((IDependant)parent).DependantProperty = 1;

            DependantChild child = parent.CreateChild();

            Console.WriteLine("After construction (Expected a value of 1): " + ((IDependant)child).DependantProperty.ToString());

            Console.ReadLine();
        }

    }
}

Regards,

Dan

  1. Support Staff 1 Posted by PostSharp Techn... on 05 Sep, 2018 02:59 PM

    PostSharp Technologies's Avatar

    Hello,

    I don't think it's possible to block <>z__InitializeAspects. However you can use a stack of thread-static variables to pass the context:

    [IntroduceInterface(typeof(IDependant))]
    [PSerializable]
    public class Dependant : InstanceLevelAspect, IDependant
    {
        [ThreadStatic] private static Stack<IDependant> context;
        public static Stack<IDependant> Context => context ?? (context = new Stack<IDependant>());
    
        public int DependantProperty { get; set; }
    
        [InitializeAspectInstanceAdvice]
        public void InitializeInstance(AspectInitializationReason reason)
        {
            if (reason == AspectInitializationReason.Constructor)
            {
                if (Context.TryPeek(out var parent))
                {
                    this.DependantProperty = parent.DependantProperty;
                }
            }
        }
    }
    
    public static class DependantObjectExtentsions
    {
        public static T Construct<T>(this object parentObject, params object[] constructorParameters)
        {
            try
            {
                Dependant.Context.Push((IDependant)parentObject);
    
                return (T)Activator.CreateInstance(typeof(T), constructorParameters);
            }
            finally
            {
                Dependant.Context.Pop();
            }
        }
    }
    

    Please let me know if this does not solve your problem or if I did not understand it correctly.

    Best regards,
    Daniel

  2. 2 Posted by daniel.comerfor... on 06 Sep, 2018 01:10 PM

    daniel.comerford's Avatar

    Hi Daniel,

    Much appreciated, this is a much tidier solution.

    Regards,

    Dan

  3. daniel.comerford closed this discussion on 06 Sep, 2018 01:10 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac