File: System\ServiceModel\MessageHeaderT.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime;
using System.ServiceModel.Channels;
using System.Threading;
 
namespace System.ServiceModel
{
    public class MessageHeader<T>
    {
        private bool _relay;
 
        public MessageHeader()
        {
        }
 
        public MessageHeader(T content)
            : this(content, false, "", false)
        {
        }
 
        public MessageHeader(T content, bool mustUnderstand, string actor, bool relay)
        {
            Content = content;
            MustUnderstand = mustUnderstand;
            Actor = actor;
            _relay = relay;
        }
 
        public string Actor { get; set; }
 
        public T Content { get; set; }
 
        public bool MustUnderstand { get; set; }
 
        public bool Relay
        {
            get { return _relay; }
            set { _relay = value; }
        }
 
        internal Type GetGenericArgument()
        {
            return typeof(T);
        }
 
        public MessageHeader GetUntypedHeader(string name, string ns)
        {
            return MessageHeader.CreateHeader(name, ns, Content, MustUnderstand, Actor, _relay);
        }
    }
 
    // problem: creating / getting content / settings content on a MessageHeader<T> given the type at runtime
    // require reflection.
    // solution: This class creates a cache of adapters that provide an untyped wrapper over a particular
    // MessageHeader<T> instantiation.
    // better solution: implement something like "IUntypedTypedHeader" that has a "object Content" property,
    // privately implement this on TypedHeader, and then just use that iface to operation on the header (actually
    // you'd still have the creation problem...).  the issue with that is you now have a new public interface
    internal abstract class TypedHeaderManager
    {
        private static Dictionary<Type, TypedHeaderManager> s_cache = new Dictionary<Type, TypedHeaderManager>();
        private static ReaderWriterLockSlim s_cacheLock = new ReaderWriterLockSlim();
        private static Type s_GenericAdapterType = typeof(GenericAdapter<>);
 
        internal static object Create(Type t, object content, bool mustUnderstand, bool relay, string actor)
        {
            return GetTypedHeaderManager(t).Create(content, mustUnderstand, relay, actor);
        }
 
        internal static object GetContent(Type t, object typedHeaderInstance, out bool mustUnderstand, out bool relay, out string actor)
        {
            return GetTypedHeaderManager(t).GetContent(typedHeaderInstance, out mustUnderstand, out relay, out actor);
        }
 
        internal static Type GetMessageHeaderType(Type contentType)
        {
            return GetTypedHeaderManager(contentType).GetMessageHeaderType();
        }
        internal static Type GetHeaderType(Type headerParameterType)
        {
            if (headerParameterType.IsGenericType() && headerParameterType.GetGenericTypeDefinition() == typeof(MessageHeader<>))
            {
                return headerParameterType.GetGenericArguments()[0];
            }
 
            return headerParameterType;
        }
 
        [SuppressMessage(FxCop.Category.Usage, "CA2301:EmbeddableTypesInContainersRule", MessageId = "cache", Justification = "No need to support type equivalence here.")]
        private static TypedHeaderManager GetTypedHeaderManager(Type t)
        {
            TypedHeaderManager result = null;
 
            bool readerLockHeld = false;
            bool writerLockHeld = false;
            try
            {
                try { }
                finally
                {
                    s_cacheLock.TryEnterUpgradeableReadLock(Timeout.Infinite);
                    readerLockHeld = true;
                }
                if (!s_cache.TryGetValue(t, out result))
                {
                    s_cacheLock.TryEnterWriteLock(Timeout.Infinite);
                    writerLockHeld = true;
                    if (!s_cache.TryGetValue(t, out result))
                    {
                        result = (TypedHeaderManager)Activator.CreateInstance(s_GenericAdapterType.MakeGenericType(t));
                        s_cache.Add(t, result);
                    }
                }
            }
            finally
            {
                if (writerLockHeld)
                {
                    s_cacheLock.ExitWriteLock();
                }
                if (readerLockHeld)
                {
                    s_cacheLock.ExitUpgradeableReadLock();
                }
            }
 
            return result;
        }
 
        protected abstract object Create(object content, bool mustUnderstand, bool relay, string actor);
        protected abstract object GetContent(object typedHeaderInstance, out bool mustUnderstand, out bool relay, out string actor);
        protected abstract Type GetMessageHeaderType();
 
        private class GenericAdapter<T> : TypedHeaderManager
        {
            protected override object Create(object content, bool mustUnderstand, bool relay, string actor)
            {
                MessageHeader<T> header = new MessageHeader<T>();
                header.Content = (T)content;
                header.MustUnderstand = mustUnderstand;
                header.Relay = relay;
                header.Actor = actor;
                return header;
            }
 
            protected override object GetContent(object typedHeaderInstance, out bool mustUnderstand, out bool relay, out string actor)
            {
                mustUnderstand = false;
                relay = false;
                actor = null;
                if (typedHeaderInstance == null)
                {
                    return null;
                }
 
                MessageHeader<T> header = typedHeaderInstance as MessageHeader<T>;
                if (header == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException("typedHeaderInstance"));
                }
 
                mustUnderstand = header.MustUnderstand;
                relay = header.Relay;
                actor = header.Actor;
                return header.Content;
            }
 
            protected override Type GetMessageHeaderType()
            {
                return typeof(MessageHeader<T>);
            }
        }
    }
}