简体   繁体   中英

Getting the value of a generic property at run time

I am using proxies and intercepts for logging purposes. One of the attributes I want to log is message ID from rabbit MQ.

We are using the following object:

namespace MassTransit
{
    public interface ConsumeContext<out T> : ConsumeContext, MessageContext, PipeContext, IPublishEndpoint, IPublishObserverConnector, ISendEndpointProvider where T : class
    {
        T Message { get; }

        /// <summary>Notify that the message has been consumed</summary>
        /// <param name="duration"></param>
        /// <param name="consumerType">The consumer type</param>
        Task NotifyConsumed(TimeSpan duration, string consumerType);

        /// <summary>
        /// Notify that a fault occurred during message consumption
        /// </summary>
        /// <param name="duration"></param>
        /// <param name="consumerType"></param>
        /// <param name="exception"></param>
        Task NotifyFaulted(TimeSpan duration, string consumerType, Exception exception);
    }
}

It is the generic Message that I need to get hold of within the intercept.

I can successfully cast it to an object say:

ConsumeContext<AuthenticationDataRequest>

And within visual studio once I've cast it the Message object pops up (without casting there is no MessageObject).

To cast I am using the following generic method:

public Guid? RunMessageRetrieve(dynamic obj, Type castTo)
{
    MethodInfo castMethod = GetType().GetMethod("GetMessageIdFromContext").MakeGenericMethod(castTo);
    return castMethod.Invoke(null, new object[] { obj }) as Guid?;
}

public static Guid? GetMessageIdFromContext<T>(dynamic context) where T : class
{
    Guid? messageId = null; 

    try
    {
        var contextCasted = (T)context;
        Type contextType = contextCasted.GetType();
        var message = contextCasted.GetType().GetProperty("Message");
        if (message != null)
        {
            messageId = message.GetType().GetProperty("MessageId").GetValue(message) as Guid?;
        }
    }
    catch (InvalidCastException castException)
    {
        Console.WriteLine("Could not retrieve message Id from context message as the cast failed");
    }
    catch (NullException nullException)
    {
        Console.WriteLine("Could not retrieve message Id from context as the message Id did not exist");
    }

    return messageId;
}

Here you can see in visual studio the message, and within that I can get the message ID:

在此处输入图片说明

However I have tried to get the actual message property out using reflection because of course I don't know the type at compile time and I just can't seem to work it out. The following is null because it's of course a generic type:

var message = contextCasted.GetType().GetProperty("Message");

This has to be doable because when the actual method is invoked after the intercepts it has the proper object with the message.

Just cast to the interface that has the property that you want, and use it. Reflection is not needed.

public static Guid? GetMessageId<T>(ConsumeContext<T> input) where T : class
{
    var casted = input as ConsumeContext<IMessage>;
    if (casted == null) return null;
    return casted.Message.MessageId;
}

Example program:

public class Program
{
    public static Guid? GetMessageId<T>(ConsumeContext<T> input) where T : class
    {
        var casted = input as ConsumeContext<IMessage>;
        if (casted == null) return null;
        return casted.Message.MessageId;
    }
    public static void Main()
    {
        var exampleObject = new MyClass<Message>();
        exampleObject.Message = new Message { MessageId = new Guid("00000001-0002-0003-0004-000000000005") };

        var messageId = GetMessageId(exampleObject);
        Console.WriteLine(messageId.Value);
    }
}

Output:

00000001-0002-0003-0004-000000000005

It also works if you have to downcast, ie even if T is a specific type of message, you can still cast it to the interface or base class that exposes the message.

    public static void Main()
    {
        var exampleObject = new MyClass<SpecificMessage>();
        exampleObject.Message = new SpecificMessage { MessageId = new Guid("00000001-0002-0003-0004-000000000005") };

        var messageId = GetMessageId(exampleObject);
        Console.WriteLine(messageId.Value);
    }

Full working example on DotNetFiddle

I went with the following in the end, works a treat, I didn't think of using the Type directly like that immediately. It was the dynamic parameter that was messing things up:

public static Guid? GetMessageIdFromContext(object context, Type contextType) 
    {
        Guid? messageId = null;

        try
        {
            var contextProp = contextType.GetProperty("Message");
            if (contextProp != null)
            {
                var message = contextProp.GetValue(context);
                if (message != null)
                {
                    messageId = message.GetType().GetProperty("UniqueId").GetValue(message) as Guid?;
                }

            }
        }
        catch (NullException nullException)
        {
            Console.WriteLine("Could not retrieve message Id from context as the message Id did not exist");
        }

        return messageId;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM