简体   繁体   English

在运行时获取通用属性的值

[英]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. 我要记录的属性之一是Rabbit MQ的消息ID。

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). 在Visual Studio中,一旦我将其投射,就会弹出Message对象(不投射就没有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: 在这里,您可以在Visual Studio中看到消息,在其中我可以得到消息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. 如果必须向下转换,它也可以工作,即,即使T是特定类型的消息,您仍然可以将其强制转换为公开消息的接口或基类。

    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 DotNetFiddle上的完整工作示例

I went with the following in the end, works a treat, I didn't think of using the Type directly like that immediately. 最后,我继续进行以下工作,请客,我没想到立即像这样直接使用Type。 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;
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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