Commit 21709f51 authored by O'Reilly Media, Inc.'s avatar O'Reilly Media, Inc.
Browse files

Initial commit

parents
Loading
Loading
Loading
Loading
+82 −0
Changes for PWSTE-Sources/ServiceModelEx (.NET 3.5)/AsyncOneWayClientBase.cs: 82 added lines, 0 removed lines.
Original line number Diff line number Diff line
// © 2011 IDesign Inc. 
//Questions? Comments? go to 
//http://www.idesign.net

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.ServiceModel;
using System.Threading;

namespace ServiceModelEx
{
   public class AsyncOneWayClientBase<T> : ClientBase<T> where T : class
   {
      List<WaitHandle> m_PendingOperations = new List<WaitHandle>();

      static AsyncOneWayClientBase()
      {
         Type type = typeof(T);
         Debug.Assert(type.IsInterface);

         MethodInfo[] methods = type.GetMethods();

         foreach(MethodInfo method in methods)
         {
            object[] attributes = method.GetCustomAttributes(typeof(OperationContractAttribute),true);

            if(attributes.Length == 0)
            {
               Debug.Assert(method.Name.StartsWith("End"));
               Debug.Assert(method.ReturnType == typeof(void));

               ParameterInfo[] parameters = method.GetParameters();
               Debug.Assert(parameters[parameters.Length-1].ParameterType == typeof(IAsyncResult));

               continue;
            }
            OperationContractAttribute operationContract = attributes[0] as OperationContractAttribute;


            if(operationContract.IsOneWay == false)
            {
               throw new InvalidOperationException("All operations on contract " + type + " must be one-way, but operation " + method.Name + " is not configured for one-way");
            }
         }
      }
      protected AsyncCallback GetCompletion()
      {
         ManualResetEvent handle = new ManualResetEvent(false);
         lock(m_PendingOperations)
         {
            m_PendingOperations.Add(handle);
         }
         return _=>
                {
                   handle.Set();                   
                   lock(m_PendingOperations)
                   {

                      m_PendingOperations.Remove(handle);
                   }
                };
      }
      public new void Close()
      {
         lock(m_PendingOperations)
         {
            WaitHandle[] operations = m_PendingOperations.ToArray();
            if(operations.Length > 0)
            {
               WaitHandle.WaitAll(operations);
            }
         }
         base.Close();
      }
      public void Dispose()
      {
         Close();
      }
   }
}
 No newline at end of file
+166 −0
Changes for PWSTE-Sources/ServiceModelEx (.NET 3.5)/Bindings/BindingRequirementAttribute.cs: 166 added lines, 0 removed lines.
Original line number Diff line number Diff line
// © 2011 IDesign Inc. 
//Questions? Comments? go to 
//http://www.idesign.net

using System;
using System.ServiceModel;
using System.Diagnostics;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Collections.ObjectModel;
using System.ServiceModel.Dispatcher;

namespace ServiceModelEx
{
   [AttributeUsage(AttributeTargets.Class)]
   public class BindingRequirementAttribute : Attribute,IServiceBehavior,IEndpointBehavior
   {
      public bool ReliabilityRequired
      {
         get;set;
      }

      public bool WCFOnly
      {
         get;set;
      }

      public bool TransactionFlowEnabled
      {
         get;set;
      }

      static void ValidateWCF(ServiceEndpoint endpoint)
      {
         if(endpoint.Binding is NetTcpBinding || 
            endpoint.Binding is NetNamedPipeBinding ||
            endpoint.Binding is NetMsmqBinding)
         {
            return;
         }
         throw new InvalidOperationException("BindingRequirementAttribute requires WCF-to-WCF binding only, but binding for the endpoint with contract " + endpoint.Contract.ContractType + " is not.");
      }
      static void ValidateReliability(ServiceEndpoint endpoint)
      {
         if(endpoint.Binding is NetNamedPipeBinding)//Inherently reliable
         {
            return;
         }
         if(endpoint.Binding is WSDualHttpBinding)//Always reliable
         {
            return;
         }             
         if(endpoint.Binding is NetTcpBinding)
         {
            NetTcpBinding tcpBinding = endpoint.Binding as NetTcpBinding;
            if(tcpBinding.ReliableSession.Enabled)
            {
               return;
            }
         }
         if(endpoint.Binding is WSHttpBindingBase)
         {
            WSHttpBindingBase wsBinding = endpoint.Binding as WSHttpBindingBase;
            if(wsBinding.ReliableSession.Enabled)
            {
               return;
            }
         }
         throw new InvalidOperationException("BindingRequirementAttribute requires reliability enabled, but binding for the endpoint with contract " + endpoint.Contract.ContractType + " has does not support reliability or has it disabled");
      }
      static void ValidateTransactionFlow(ServiceEndpoint endpoint)
      {
         Exception exception = new InvalidOperationException("BindingRequirementAttribute requires transaction flow enabled, but binding for the endpoint with contract " + endpoint.Contract.ContractType + " has it disabled");

         foreach(OperationDescription operation in endpoint.Contract.Operations)
         {
            TransactionFlowAttribute attribute = operation.Behaviors.Find<TransactionFlowAttribute>();
            if(attribute != null)
            {
               if(attribute.Transactions == TransactionFlowOption.Allowed)
               {
                  if(endpoint.Binding is NetTcpBinding)
                  {
                     NetTcpBinding tcpBinding = endpoint.Binding as NetTcpBinding;
                     if(tcpBinding.TransactionFlow == false)
                     {
                        throw exception;
                     }
                     continue;
                  }
                  if(endpoint.Binding is NetNamedPipeBinding)
                  {
                     NetNamedPipeBinding ipcBinding = endpoint.Binding as NetNamedPipeBinding;
                     if(ipcBinding.TransactionFlow == false)
                     {
                        throw exception;
                     }
                     continue;
                  }
                  if(endpoint.Binding is WSHttpBindingBase)
                  {
                     WSHttpBindingBase wsBinding = endpoint.Binding as WSHttpBindingBase;
                     if(wsBinding.TransactionFlow == false)
                     {
                        throw exception;
                     }
                     continue;
                  }
                  if(endpoint.Binding is WSDualHttpBinding)
                  {
                     WSDualHttpBinding wsDualBinding = endpoint.Binding as WSDualHttpBinding;
                     if(wsDualBinding.TransactionFlow == false)
                     {
                        throw exception;
                     }
                     continue;
                  }
                  throw new InvalidOperationException("BindingRequirementAttribute requires transaction flow enabled, but binding for the endpoint with contract " + endpoint.Contract.ContractType + " does not support transaction flow");
               }
            }
         }
      }
      void IServiceBehavior.Validate(ServiceDescription description,ServiceHostBase host) 
      {
         IEndpointBehavior endpointBehavior = this;
         foreach(ServiceEndpoint endpoint in description.Endpoints)
         {
            endpointBehavior.Validate(endpoint);
         }
      }
      void IServiceBehavior.AddBindingParameters(ServiceDescription description,ServiceHostBase host,Collection<ServiceEndpoint> endpoints,BindingParameterCollection parameters)
      {}
      void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description,ServiceHostBase host)
      {}

      void IEndpointBehavior.AddBindingParameters(ServiceEndpoint serviceEndpoint,BindingParameterCollection bindingParameters)
      {}

      void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint serviceEndpoint,ClientRuntime behavior)
      {}

      void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint,EndpointDispatcher endpointDispatcher)
      {}

      void IEndpointBehavior.Validate(ServiceEndpoint endpoint)
      {
         if(WCFOnly)
         {
            ValidateWCF(endpoint);
         }
         if(TransactionFlowEnabled)
         {
            ValidateTransactionFlow(endpoint);
         }
         if(ReliabilityRequired)
         {
            ValidateReliability(endpoint);
         }
      }
   }
} 




+115 −0
Changes for PWSTE-Sources/ServiceModelEx (.NET 3.5)/Bindings/NetNamedPipeContextBinding.cs: 115 added lines, 0 removed lines.
Original line number Diff line number Diff line
// © 2011 IDesign Inc. 
//Questions? Comments? go to 
//http://www.idesign.net

using System;
using System.Configuration;
using System.Diagnostics;
using System.Net.Security;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;

namespace ServiceModelEx
{
   public class NetNamedPipeContextBinding : NetNamedPipeBinding
   {
      internal const string SectionName = "netNamedPipeContextBinding";

      public ProtectionLevel ContextProtectionLevel
      {get;set;}

      public NetNamedPipeContextBinding()
      {
         ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
      }
      public NetNamedPipeContextBinding(NetNamedPipeSecurityMode securityMode) : base(securityMode)
      {
         ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
      }
      public NetNamedPipeContextBinding(string configurationName)
      {
         ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
         ApplyConfiguration(configurationName);
      }
      //The heart of the custom binding 
      public override BindingElementCollection CreateBindingElements()
      {
         BindingElement element = new ContextBindingElement(ContextProtectionLevel,ContextExchangeMechanism.ContextSoapHeader);

         BindingElementCollection elements = base.CreateBindingElements();
         elements.Insert(0,element);

         return elements;
      }
      void ApplyConfiguration(string configurationName)
      {
         Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
         ServiceModelSectionGroup sectionGroup = ServiceModelSectionGroup.GetSectionGroup(config);

         BindingsSection bindings = sectionGroup.Bindings;

         NetNamedPipeContextBindingCollectionElement section = (NetNamedPipeContextBindingCollectionElement)bindings[SectionName];
         NetNamedPipeContextBindingElement element = section.Bindings[configurationName];
         if(element == null)
         {
            throw new ConfigurationErrorsException("There is no binding named " + configurationName + " at " + section.BindingName);
         }
         else
         {
            element.ApplyConfiguration(this);
         }
      }
   }
   public class NetNamedPipeContextBindingElement : NetNamedPipeBindingElement
   {
      const string ContextProtectionLevelName = "contextProtectionLevel";


      public NetNamedPipeContextBindingElement()
      {
         Initialize();
      }
      public NetNamedPipeContextBindingElement(string name) : base(name)
      {
         Initialize();
      }

      void Initialize()
      {
         ConfigurationProperty property = new ConfigurationProperty(ContextProtectionLevelName,typeof(ProtectionLevel),ProtectionLevel.EncryptAndSign);
         Properties.Add(property);

         ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
      }
      protected override void OnApplyConfiguration(Binding binding)
      {
         base.OnApplyConfiguration(binding);
         NetNamedPipeContextBinding netNamedPipeContextBinding = binding as NetNamedPipeContextBinding;
         Debug.Assert(netNamedPipeContextBinding != null);

         netNamedPipeContextBinding.ContextProtectionLevel = ContextProtectionLevel;
      }
      protected override Type BindingElementType
      {
         get
         {
            return typeof(NetNamedPipeContextBinding);
         }
      }

      public ProtectionLevel ContextProtectionLevel
      {
         get
         {
            return (ProtectionLevel)base[ContextProtectionLevelName];
         }
         set
         {
            base[ContextProtectionLevelName] = value;
         }
      }
   }
   public class NetNamedPipeContextBindingCollectionElement : StandardBindingCollectionElement<NetNamedPipeContextBinding,NetNamedPipeContextBindingElement>
   {}
}
+103 −0
Changes for PWSTE-Sources/ServiceModelEx (.NET 3.5)/Context/ContextClientBase.cs: 103 added lines, 0 removed lines.
Original line number Diff line number Diff line
// © 2011 IDesign Inc. 
//Questions? Comments? go to 
//http://www.idesign.net

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;


namespace ServiceModelEx
{
   public abstract class ContextClientBase<T> : ClientBase<T> where T : class
   {
      public Guid InstanceId
      {
         get
         {
            return ContextManager.GetInstanceId(InnerChannel);
         }
      }

      public ContextClientBase()
      {}

      public ContextClientBase(string endpointName) : base(endpointName)
      {}

      public ContextClientBase(Binding binding,EndpointAddress remoteAddress) : base(binding,remoteAddress)
      {}

      public ContextClientBase(Guid instanceId) : this(ContextManager.InstanceIdKey,instanceId.ToString())
      {}

      public ContextClientBase(Guid instanceId,string endpointName) : this(ContextManager.InstanceIdKey,instanceId.ToString(),endpointName)
      {}

      public ContextClientBase(Guid instanceId,Binding binding,EndpointAddress remoteAddress) : this(ContextManager.InstanceIdKey,instanceId.ToString(),binding,remoteAddress)
      {}

      public ContextClientBase(string key,string value) : this(ContextManager.CreateContext(key,value))
      {}

      public ContextClientBase(string key,string value,string endpointName) : this(ContextManager.CreateContext(key,value),endpointName)
      {}

      public ContextClientBase(string key,string value,Binding binding,EndpointAddress remoteAddress) : this(ContextManager.CreateContext(key,value),binding,remoteAddress)
      {}
      
      public ContextClientBase(IDictionary<string,string> context)
      {
         SetContext(context);
      }

      public ContextClientBase(IDictionary<string,string> context,string endpointName) : base(endpointName)
      {
         SetContext(context);
      }

      public ContextClientBase(IDictionary<string,string> context,Binding binding,EndpointAddress remoteAddress) : base(binding,remoteAddress)
      {
         SetContext(context);
      }

      public T DuplicateProxy()
      {
         T channel = ChannelFactory<T>.CreateChannel(Endpoint.Binding,Endpoint.Address);
         IClientChannel innerChannel = channel as IClientChannel;
         
         Guid instanceId = ContextManager.GetInstanceId(InnerChannel);
         ContextManager.SetInstanceId(innerChannel,instanceId);
         return channel;
      }
      void SetContext(IDictionary<string,string> context)
      {
         VerifyContextBinding();

         //Special case is context with only instance ID that is the empty guid
         if(context.Count == 1)
         {
            if(context.ContainsKey(ContextManager.InstanceIdKey))
            {
               if(context[ContextManager.InstanceIdKey] == Guid.Empty.ToString())
               {
                  return;
               }
            }
         }
         ContextManager.SetContext(InnerChannel,context);
      }

      void VerifyContextBinding()
      {
         BindingElementCollection elements = Endpoint.Binding.CreateBindingElements();
         
         if(elements.Contains(typeof(ContextBindingElement)))
         {
            return;
         }
         throw new InvalidOperationException("Can only use a context binding with " + GetType());
      }
   }
}
 No newline at end of file
+141 −0
Changes for PWSTE-Sources/ServiceModelEx (.NET 3.5)/Context/ContextManager.cs: 141 added lines, 0 removed lines.
Original line number Diff line number Diff line
//  2011 IDesign Inc. 
//Questions? Comments? go to 
//http://www.idesign.net

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace ServiceModelEx
{
   public static class ContextManager
   {
      public const string InstanceIdKey = "instanceId";

      public static Guid InstanceId
      {
         get
         {
            string id = GetContext(InstanceIdKey) ?? Guid.Empty.ToString();
            return new Guid(id); 
         }
      }

      public static IDictionary<string,string> CreateContext(string key,string value)
      {
         IDictionary<string,string> context = new Dictionary<string,string>();
         context[key] = value;
         return context;
      }

      //Called by the service to read incoming context over context bindings
      public static string GetContext(string key)
      {
         if(OperationContext.Current == null)
         {
            return null;
         }
         if(OperationContext.Current.IncomingMessageProperties.ContainsKey(ContextMessageProperty.Name))
         {
            ContextMessageProperty contextProperty = OperationContext.Current.IncomingMessageProperties[ContextMessageProperty.Name] as ContextMessageProperty;
            if(contextProperty.Context.ContainsKey(key) == false)
            {
               return null;
            }
            return contextProperty.Context[key]; 
         }
         else
         {
            return null;
         }
      }
      
      
      //Called by the client to write context over context bindings
      public static void SetContext(IClientChannel innerChannel,string key,string value)
      {
         SetContext(innerChannel,UpdateContext(innerChannel,key,value));
      }
      
      //Called by the client to overwrite context with context bindings
      public static void SetContext(IClientChannel innerChannel,IDictionary<string,string> context)
      {
         Debug.Assert((innerChannel as ICommunicationObject).State != CommunicationState.Opened);

         IContextManager contextManager = innerChannel.GetProperty<IContextManager>();
         contextManager.SetContext(context);
      }

      //Creates new context containing the new value and the old ones from the proxy
      public static IDictionary<string,string> UpdateContext(IClientChannel innerChannel,string key,string value)
      {
         IContextManager contextManager = innerChannel.GetProperty<IContextManager>();

         IDictionary<string,string> context = new Dictionary<string,string>(contextManager.GetContext());
         context[key] = value;
         return context;
      }

      public static Guid GetInstanceId(IClientChannel innerChannel)
      {
         try
         {
            string instanceId = innerChannel.GetProperty<IContextManager>().GetContext()[InstanceIdKey];
            return new Guid(instanceId);
         }
         catch(KeyNotFoundException)
         {
            return Guid.Empty;
         }
      }

      public static void SetInstanceId(IClientChannel innerChannel,Guid instanceId)
      {
         SetContext(innerChannel,InstanceIdKey,instanceId.ToString());
      }

      //Proxy extensions
      public static void SetContext<T>(this ClientBase<T> proxy,string key,string value) where T : class
      {
         SetContext(proxy.InnerChannel,key,value);
      }

      public static void SetContext<T>(this ClientBase<T> proxy,IDictionary<string,string> context) where T : class
      {
         SetContext(proxy.InnerChannel,context);
      }

      public static IDictionary<string,string> UpdateContext<T>(this ClientBase<T> proxy,string key,string value) where T : class
      {
         return UpdateContext(proxy.InnerChannel,key,value);
      }      
      public static void SaveInstanceId(Guid instanceId,string fileName)
      {
         using(Stream stream = new FileStream(fileName,FileMode.OpenOrCreate,FileAccess.Write))
         {
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream,instanceId);
         }
      }
      public static Guid LoadInstanceId(string fileName)
      {
         try
         {
            using(Stream stream = new FileStream(fileName,FileMode.Open,FileAccess.Read))
            {
               IFormatter formatter = new BinaryFormatter();
               return (Guid)formatter.Deserialize(stream);
            }
         }
         catch
         {
            return Guid.Empty;
         }
      }
   }
}
Loading