using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace Contracts
{
public delegate void OnSendOneWay<T>(T message);
public delegate V OnSendRequestResponse<T, V>(T message);
public delegate V OnSendNullRequestResponse<V>();
/// <summary>
/// Abstract base class for a WCF service implementing IWcfContract.
/// </summary>
/// <typeparam name="TTopic">TopicID enumeration type with an underlying type of int</typeparam>
/// <typeparam name="TMessage">MessageID enumeration type with an underlying type of int</typeparam>
public abstract class ServiceWrapperBase<TTopic, TMessage> : IWcfContractWithCallback, IWcfContract
where TTopic : struct, IConvertible
where TMessage : struct, IConvertible
{
// Dictionaries of serializers and deserializers.
// Note that we have to have two dictionaries here, because there might exist a serializer and a deserializer for a single topic/message pair.
Dictionary<Tuple<int, int>, Delegate> deserializers = new Dictionary<Tuple<int, int>, Delegate>();
Dictionary<Tuple<int, int>, Delegate> serializers = new Dictionary<Tuple<int, int>, Delegate>();
// Dictionary of topic/message > method delegate mapping
Dictionary<Tuple<int, int>, Delegate> handlers = new Dictionary<Tuple<int, int>, Delegate>();
bool supportsDuplex;
/// <summary>
/// Doing some validation of the types passed in to the class
/// </summary>
public ServiceWrapperBase(bool supportsDuplex)
{
this.supportsDuplex = supportsDuplex;
if (!typeof(TTopic).IsEnum || !typeof(TMessage).IsEnum)
{
throw new ArgumentException("TTopic and TMessage must be an enum type");
}
if (typeof(TTopic).GetEnumUnderlyingType() != typeof(int) || typeof(TMessage).GetEnumUnderlyingType() != typeof(int))
{
throw new ArgumentException("TTopic and TMessage must have an underlying type of 'int'");
}
}
/// <summary>
/// Register a handler for a one-way WCF call by passing in the topic id, message id, and delegate pointing to your handler method.
/// </summary>
/// <typeparam name="TSendMessage">Type of the message that is going to be handled</typeparam>
/// <param name="topicId">The topic ID of the message</param>
/// <param name="messageId">the message ID of the message</param>
/// <param name="handler">The OnSendOneWay delegate that will handle any incoming messages for this topic and message ID.</param>
public void RegisterHandler<TSendMessage>(TTopic topicId, TMessage messageId, OnSendOneWay<TSendMessage> handler)
where TSendMessage : class
{
RegisterDeserializer<TSendMessage>(topicId, messageId);
lock (handlers)
handlers.Add(new Tuple<int, int>(topicId.ToInt32(CultureInfo.CurrentCulture), messageId.ToInt32(CultureInfo.CurrentCulture)), handler);
}
/// <summary>
/// Register a handler for a two-way WCF call that returns TReceiveMessage by passing in the topic id, message id, and delegate pointing to your handler method.
/// </summary>
/// <typeparam name="TSendMessage">Type of the message that is going to be handled</typeparam>
/// <typeparam name="TReceiveMessage">Type of the return value that this handler will output</typeparam>
/// <param name="topicId">The topic ID of the message</param>
/// <param name="messageId">the message ID of the message</param>
/// <param name="handler">The OnSendRequestResponse delegate that will handle any incoming messages for this topic and message ID.</param>
public void RegisterHandler<TSendMessage, TReceiveMessage>(TTopic topicId, TMessage messageId, OnSendRequestResponse<TSendMessage, TReceiveMessage> handler)
where TSendMessage : class
where TReceiveMessage : class
{
RegisterDeserializer<TSendMessage>(topicId, messageId);
RegisterSerializer<TReceiveMessage>(topicId, messageId);
lock (handlers)
handlers.Add(new Tuple<int, int>(topicId.ToInt32(CultureInfo.CurrentCulture), messageId.ToInt32(CultureInfo.CurrentCulture)), handler);
}
/// <summary>
/// Register a handler for a two-way WCF call that returns null by passing in the topic id, message id, and delegate pointing to your handler method.
/// </summary>
/// <typeparam name="TReceiveMessage">Type of the return value that this handler will output</typeparam>
/// <param name="topicId">The topic ID of the message</param>
/// <param name="messageId">the message ID of the message</param>
/// <param name="handler">The OnSendRequestResponse delegate that will handle any incoming messages for this topic and message ID.</param>
public void RegisterHandler<TReceiveMessage>(TTopic topicId, TMessage messageId, OnSendNullRequestResponse<TReceiveMessage> handler)
where TReceiveMessage : class
{
RegisterSerializer<TReceiveMessage>(topicId, messageId);
lock (handlers)
handlers.Add(new Tuple<int, int>(topicId.ToInt32(CultureInfo.CurrentCulture), messageId.ToInt32(CultureInfo.CurrentCulture)), handler);
}
/// <summary>
/// Unregister a handler
/// </summary>
/// <param name="topicId">topic ID of the handler to unregister</param>
/// <param name="messageId">message ID of the handler to unregister</param>
public void UnregisterHandler(TTopic topicId, TMessage messageId)
{
Tuple<int, int> key = new Tuple<int, int>(topicId.ToInt32(CultureInfo.CurrentCulture), messageId.ToInt32(CultureInfo.CurrentCulture));
lock (handlers)
if (handlers.ContainsKey(key))
handlers.Remove(key);
if (serializers.ContainsKey(key))
serializers.Remove(key);
if (deserializers.ContainsKey(key))
deserializers.Remove(key);
}
/// <summary>
/// Register a deserializer associated with this topic and message ID
/// </summary>
private void RegisterDeserializer<T>(TTopic topicId, TMessage messageId)
{
Func<byte[], T> deserialize = SerializationHelper.Deserialize<T>;
deserializers.Add(new Tuple<int, int>(topicId.ToInt32(CultureInfo.CurrentCulture), messageId.ToInt32(CultureInfo.CurrentCulture)), deserialize);
}
/// <summary>
/// Register a serializer associated with this topic and message ID
/// </summary>
private void RegisterSerializer<T>(TTopic topicId, TMessage messageId) where T : class
{
Func<T, byte[]> serialize = SerializationHelper.Serialize;
serializers.Add(new Tuple<int, int>(topicId.ToInt32(CultureInfo.CurrentCulture), messageId.ToInt32(CultureInfo.CurrentCulture)), serialize);
}
/// <summary>
/// Call the handler registered for this topic and message id
/// </summary>
private byte[] CallHandler(int topicId, int messageId, byte[] message, bool isOneWay)
{
Tuple<int, int> key = new Tuple<int, int>(topicId, messageId);
lock (handlers)
if (!handlers.ContainsKey(key))
throw new FaultException("Method not supported");
if (isOneWay)
{
lock (handlers)
handlers[key].DynamicInvoke(deserializers[key].DynamicInvoke(message));
return null;
}
else
{
object serializedObject;
if (message == null)
{
lock (handlers)
serializedObject = serializers[key].DynamicInvoke(handlers[key].DynamicInvoke());
}
else
{
lock (handlers)
serializedObject = serializers[key].DynamicInvoke(handlers[key].DynamicInvoke(deserializers[key].DynamicInvoke(message))); ;
}
return serializedObject as byte[];
}
}
#region Wcf Methods
/// <summary>
/// One-way message interface exposed to WCF
/// </summary>
public void SendMessageOneWay(int topicId, int messageId, byte[] message)
{
CallHandler(topicId, messageId, message, true);
}
/// <summary>
/// Two-way message interface exposed to WCF
/// </summary>
public byte[] SendMessageRequestResponse(int topicId, int messageId, byte[] message)
{
return CallHandler(topicId, messageId, message, false);
}
public void Callback<TSendMessage>(TTopic topicId, TMessage messageId, TSendMessage message, OperationContext currentContext)
{
if(!supportsDuplex)
throw new InvalidOperationException("The service does not support Duplex actions!");
IWcfCallbackContract callback = currentContext.GetCallbackChannel<IWcfCallbackContract>();
callback.Callback(topicId.ToInt32(CultureInfo.CurrentCulture), messageId.ToInt32(CultureInfo.CurrentCulture), SerializationHelper.Serialize(message));
}
#endregion Wcf Methods
}
}