464 lines
19 KiB
C#
464 lines
19 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
|
||
|
#region Using directives
|
||
|
|
||
|
using System;
|
||
|
using System.Diagnostics;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Text;
|
||
|
using System.Reflection;
|
||
|
using System.Runtime.Serialization;
|
||
|
using System.Workflow.ComponentModel;
|
||
|
using System.Runtime.Remoting;
|
||
|
using System.Runtime.Remoting.Messaging;
|
||
|
using System.Workflow.Runtime;
|
||
|
using System.Xml;
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
namespace System.Workflow.Activities
|
||
|
{
|
||
|
internal static class CorrelationResolver
|
||
|
{
|
||
|
static Dictionary<Type, CorrelationMethodResolver> cachedTypeResolver = new Dictionary<Type, CorrelationMethodResolver>();
|
||
|
static object mutex = new object();
|
||
|
|
||
|
internal static bool IsInitializingMember(Type interfaceType, string memberName, object[] methodArgs)
|
||
|
{
|
||
|
if (interfaceType == null)
|
||
|
throw new ArgumentNullException("interfaceType");
|
||
|
if (memberName == null)
|
||
|
throw new ArgumentNullException("memberName");
|
||
|
if (memberName.Length == 0)
|
||
|
throw new ArgumentException(SR.GetString(SR.Error_EventNameMissing));
|
||
|
|
||
|
ICorrelationProvider correlationProvider = CorrelationResolver.GetCorrelationProvider(interfaceType);
|
||
|
return correlationProvider.IsInitializingMember(interfaceType, memberName, methodArgs);
|
||
|
}
|
||
|
|
||
|
internal static ICollection<CorrelationProperty> ResolveCorrelationValues(Type interfaceType, string eventName, object[] eventArgs, bool provideInitializerTokens)
|
||
|
{
|
||
|
if (interfaceType == null)
|
||
|
throw new ArgumentNullException("interfaceType");
|
||
|
if (eventName == null)
|
||
|
throw new ArgumentNullException("eventName");
|
||
|
if (eventName.Length == 0)
|
||
|
throw new ArgumentException(SR.GetString(SR.Error_EventNameMissing));
|
||
|
|
||
|
ICorrelationProvider correlationProvider = CorrelationResolver.GetCorrelationProvider(interfaceType);
|
||
|
return correlationProvider.ResolveCorrelationPropertyValues(interfaceType, eventName, eventArgs, provideInitializerTokens);
|
||
|
}
|
||
|
|
||
|
internal static ICorrelationProvider GetCorrelationProvider(Type interfaceType)
|
||
|
{
|
||
|
CorrelationMethodResolver resolver = GetResolver(interfaceType);
|
||
|
return resolver.CorrelationProvider;
|
||
|
}
|
||
|
|
||
|
private static CorrelationMethodResolver GetResolver(Type interfaceType)
|
||
|
{
|
||
|
CorrelationMethodResolver resolver = null;
|
||
|
cachedTypeResolver.TryGetValue(interfaceType, out resolver);
|
||
|
if (resolver == null)
|
||
|
{
|
||
|
lock (mutex)
|
||
|
{
|
||
|
cachedTypeResolver.TryGetValue(interfaceType, out resolver);
|
||
|
if (resolver == null)
|
||
|
{
|
||
|
resolver = new CorrelationMethodResolver(interfaceType);
|
||
|
cachedTypeResolver.Add(interfaceType, resolver);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return resolver;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sealed class CorrelationMethodResolver
|
||
|
{
|
||
|
Type interfaceType;
|
||
|
|
||
|
// a correlation provider for each interface type
|
||
|
ICorrelationProvider correlationProvider;
|
||
|
object corrProviderSync = new object();
|
||
|
|
||
|
internal CorrelationMethodResolver(Type interfaceType)
|
||
|
{
|
||
|
this.interfaceType = interfaceType;
|
||
|
}
|
||
|
|
||
|
internal ICorrelationProvider CorrelationProvider
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (this.correlationProvider == null)
|
||
|
{
|
||
|
lock (this.corrProviderSync)
|
||
|
{
|
||
|
if (this.correlationProvider == null)
|
||
|
{
|
||
|
ICorrelationProvider provider = null;
|
||
|
object[] corrProviderAttribs = this.interfaceType.GetCustomAttributes(typeof(CorrelationProviderAttribute), true);
|
||
|
if (corrProviderAttribs.Length == 0)
|
||
|
{
|
||
|
corrProviderAttribs = this.interfaceType.GetCustomAttributes(typeof(ExternalDataExchangeAttribute), true);
|
||
|
object[] corrParameterAttribs = this.interfaceType.GetCustomAttributes(typeof(CorrelationParameterAttribute), true);
|
||
|
if (corrProviderAttribs.Length != 0 && corrParameterAttribs.Length != 0)
|
||
|
{
|
||
|
// no provider specified but it is a data exchange correlation service
|
||
|
// hence use our default correlation
|
||
|
provider = new DefaultCorrelationProvider(this.interfaceType);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// opaque interface with no correlation
|
||
|
provider = new NonCorrelatedProvider();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CorrelationProviderAttribute cpattrib = corrProviderAttribs[0] as CorrelationProviderAttribute;
|
||
|
Type providerType = cpattrib.CorrelationProviderType;
|
||
|
provider = Activator.CreateInstance(providerType) as ICorrelationProvider;
|
||
|
}
|
||
|
|
||
|
System.Threading.Thread.MemoryBarrier();
|
||
|
this.correlationProvider = provider;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this.correlationProvider;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sealed class CorrelationPropertyValue
|
||
|
{
|
||
|
string name;
|
||
|
string locationPath;
|
||
|
int signaturePosition;
|
||
|
|
||
|
internal CorrelationPropertyValue(string name, string locationPath, int signaturePosition)
|
||
|
{
|
||
|
this.name = name;
|
||
|
this.locationPath = locationPath;
|
||
|
this.signaturePosition = signaturePosition;
|
||
|
}
|
||
|
|
||
|
internal object GetValue(object[] args)
|
||
|
{
|
||
|
if (args.Length <= this.signaturePosition)
|
||
|
throw new ArgumentOutOfRangeException("args");
|
||
|
|
||
|
object arg = args[this.signaturePosition];
|
||
|
if (arg == null)
|
||
|
return arg;
|
||
|
Type type = arg.GetType();
|
||
|
object val = arg;
|
||
|
if (this.locationPath.Length != 0)
|
||
|
{
|
||
|
string[] split = locationPath.Split(new Char[] { '.' });
|
||
|
for (int i = 1; i < split.Length; i++)
|
||
|
{
|
||
|
string s = split[i];
|
||
|
if (null == arg)
|
||
|
break;
|
||
|
val = type.InvokeMember(s, BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty, null, arg, null, null);
|
||
|
|
||
|
MemberInfo[] mInfos = type.GetMember(s, BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty);
|
||
|
|
||
|
type = GetMemberType(mInfos[0]);
|
||
|
arg = val;
|
||
|
}
|
||
|
}
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
internal string Name
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private Type GetMemberType(MemberInfo mInfo)
|
||
|
{
|
||
|
Type type = null;
|
||
|
switch (mInfo.MemberType)
|
||
|
{
|
||
|
case MemberTypes.Field:
|
||
|
type = ((FieldInfo)mInfo).FieldType;
|
||
|
break;
|
||
|
|
||
|
case MemberTypes.Property:
|
||
|
type = ((PropertyInfo)mInfo).PropertyType;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Debug.Assert(false, "locationPath points to something other than a Field/Property");
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return type;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sealed class DefaultCorrelationProvider : ICorrelationProvider
|
||
|
{
|
||
|
Type interfaceType;
|
||
|
|
||
|
// map of method name to correlation properties
|
||
|
Dictionary<string, CorrelationPropertyValue[]> cachedCorrelationProperties;
|
||
|
object cachedCorrelationPropertiesSync = new object();
|
||
|
|
||
|
// cached initializers
|
||
|
// map operation to bool flag to indicate events
|
||
|
Dictionary<string, bool> initializerCorrelationPropertys = null;
|
||
|
object initializerCorrelationPropertysSync = new object();
|
||
|
|
||
|
internal DefaultCorrelationProvider(Type interfaceType)
|
||
|
{
|
||
|
this.cachedCorrelationProperties = new Dictionary<string, CorrelationPropertyValue[]>();
|
||
|
this.interfaceType = interfaceType;
|
||
|
}
|
||
|
|
||
|
ICollection<CorrelationProperty> ICorrelationProvider.ResolveCorrelationPropertyValues(Type interfaceType, string methodName, object[] methodArgs, bool provideInitializerTokens)
|
||
|
{
|
||
|
CorrelationPropertyValue[] correlationProperties = null;
|
||
|
|
||
|
if (methodArgs == null || provideInitializerTokens)
|
||
|
{
|
||
|
return null; // no initializer specific token to return
|
||
|
}
|
||
|
|
||
|
this.cachedCorrelationProperties.TryGetValue(methodName, out correlationProperties);
|
||
|
if (correlationProperties == null)
|
||
|
{
|
||
|
lock (this.cachedCorrelationPropertiesSync)
|
||
|
{
|
||
|
this.cachedCorrelationProperties.TryGetValue(methodName, out correlationProperties);
|
||
|
if (correlationProperties == null)
|
||
|
{
|
||
|
correlationProperties = GetCorrelationProperties(interfaceType, methodName);
|
||
|
this.cachedCorrelationProperties.Add(methodName, correlationProperties);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
List<CorrelationProperty> predicates = new List<CorrelationProperty>();
|
||
|
for (int i = 0; i < correlationProperties.Length; i++)
|
||
|
{
|
||
|
predicates.Add(new CorrelationProperty(correlationProperties[i].Name, correlationProperties[i].GetValue(methodArgs)));
|
||
|
}
|
||
|
|
||
|
return predicates;
|
||
|
}
|
||
|
|
||
|
private Dictionary<string, bool> InitializerCorrelationPropertys
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (this.initializerCorrelationPropertys == null)
|
||
|
{
|
||
|
lock (this.initializerCorrelationPropertysSync)
|
||
|
{
|
||
|
if (this.initializerCorrelationPropertys == null)
|
||
|
{
|
||
|
Dictionary<string, bool> members = new Dictionary<string, bool>();
|
||
|
// note this is separated out since we may need to distinguish between events & methods
|
||
|
foreach (EventInfo member in this.interfaceType.GetEvents())
|
||
|
{
|
||
|
if ((member.GetCustomAttributes(typeof(CorrelationInitializerAttribute), true)).Length > 0)
|
||
|
{
|
||
|
members.Add(member.Name, true);
|
||
|
}
|
||
|
}
|
||
|
foreach (MethodInfo member in this.interfaceType.GetMethods())
|
||
|
{
|
||
|
if ((member.GetCustomAttributes(typeof(CorrelationInitializerAttribute), true)).Length > 0)
|
||
|
{
|
||
|
members.Add(member.Name, false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.initializerCorrelationPropertys = members;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return this.initializerCorrelationPropertys;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ICorrelationProvider.IsInitializingMember(Type interfaceType, string memberName, object[] methodArgs)
|
||
|
{
|
||
|
return InitializerCorrelationPropertys.ContainsKey(memberName);
|
||
|
}
|
||
|
|
||
|
private CorrelationPropertyValue[] GetCorrelationProperties(Type interfaceType, string methodName)
|
||
|
{
|
||
|
CorrelationPropertyValue[] correlationProperties = null;
|
||
|
|
||
|
if (interfaceType.GetCustomAttributes(typeof(ExternalDataExchangeAttribute), true).Length == 0)
|
||
|
throw new InvalidOperationException(SR.GetString(SR.Error_ExternalDataExchangeException, interfaceType.AssemblyQualifiedName));
|
||
|
|
||
|
List<Object> correlationParamAttributes = new List<Object>();
|
||
|
correlationParamAttributes.AddRange(GetCorrelationParameterAttributes(interfaceType));
|
||
|
|
||
|
if (correlationParamAttributes.Count == 0)
|
||
|
throw new InvalidOperationException(SR.GetString(SR.Error_CorrelationParameterException, interfaceType.AssemblyQualifiedName));
|
||
|
|
||
|
correlationProperties = new CorrelationPropertyValue[correlationParamAttributes.Count];
|
||
|
|
||
|
Dictionary<String, CorrelationAliasAttribute> corrAliases = null;
|
||
|
MethodInfo methodInfo = null;
|
||
|
|
||
|
GetMethodInfo(interfaceType, methodName, out methodInfo, out corrAliases);
|
||
|
|
||
|
if (methodInfo == null)
|
||
|
{
|
||
|
throw new MissingMethodException(interfaceType.AssemblyQualifiedName, methodName);
|
||
|
}
|
||
|
|
||
|
ParameterInfo[] parameters = methodInfo.GetParameters();
|
||
|
|
||
|
int i = 0;
|
||
|
foreach (CorrelationParameterAttribute paramAttribute in correlationParamAttributes)
|
||
|
{
|
||
|
String location = paramAttribute.Name;
|
||
|
CorrelationAliasAttribute aliasAttribute = GetMatchingCorrelationAlias(paramAttribute, corrAliases, correlationParamAttributes.Count == 1);
|
||
|
|
||
|
if (aliasAttribute != null)
|
||
|
location = aliasAttribute.Path;
|
||
|
|
||
|
CorrelationPropertyValue value = GetCorrelationProperty(parameters, paramAttribute.Name, location);
|
||
|
if (value == null)
|
||
|
throw new InvalidOperationException(SR.GetString(SR.Error_CorrelationParameterException, interfaceType.AssemblyQualifiedName, paramAttribute.Name, methodName));
|
||
|
|
||
|
correlationProperties[i++] = value;
|
||
|
}
|
||
|
return correlationProperties;
|
||
|
}
|
||
|
|
||
|
private CorrelationAliasAttribute GetMatchingCorrelationAlias(CorrelationParameterAttribute paramAttribute, Dictionary<String, CorrelationAliasAttribute> correlationAliases, bool defaultParameter)
|
||
|
{
|
||
|
CorrelationAliasAttribute corrAlias = null;
|
||
|
|
||
|
if (correlationAliases == null) return null;
|
||
|
|
||
|
if (defaultParameter)
|
||
|
{
|
||
|
if (correlationAliases.TryGetValue("", out corrAlias))
|
||
|
{
|
||
|
return corrAlias;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
correlationAliases.TryGetValue(paramAttribute.Name, out corrAlias);
|
||
|
return corrAlias;
|
||
|
}
|
||
|
|
||
|
private CorrelationPropertyValue GetCorrelationProperty(ParameterInfo[] parameters, String propertyName, String location)
|
||
|
{
|
||
|
string[] split = location.Split(new Char[] { '.' });
|
||
|
|
||
|
if (split.Length == 1 && parameters.Length == 2)
|
||
|
{
|
||
|
if (typeof(ExternalDataEventArgs).IsAssignableFrom(parameters[1].ParameterType))
|
||
|
{
|
||
|
string aliasedLocation = "e." + location;
|
||
|
return GetCorrelationProperty(parameters, propertyName, "e", aliasedLocation);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
string parameterName = split[0];
|
||
|
|
||
|
return GetCorrelationProperty(parameters, propertyName, parameterName, location);
|
||
|
}
|
||
|
|
||
|
private void GetMethodInfo(Type interfaceType, string methodName, out MethodInfo methodInfo, out Dictionary<String, CorrelationAliasAttribute> correlationAliases)
|
||
|
{
|
||
|
correlationAliases = new Dictionary<String, CorrelationAliasAttribute>();
|
||
|
Object[] customAttrs = null;
|
||
|
methodInfo = null;
|
||
|
// check events
|
||
|
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||
|
EventInfo eventInfo = interfaceType.GetEvent(methodName, bindingFlags);
|
||
|
if (eventInfo != null)
|
||
|
{
|
||
|
customAttrs = eventInfo.GetCustomAttributes(typeof(CorrelationAliasAttribute), true);
|
||
|
if (customAttrs == null || customAttrs.Length == 0)
|
||
|
{
|
||
|
customAttrs = eventInfo.EventHandlerType.GetCustomAttributes(typeof(CorrelationAliasAttribute), true);
|
||
|
}
|
||
|
MethodInfo[] methInfo = eventInfo.EventHandlerType.GetMethods();
|
||
|
methodInfo = methInfo[0];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// check methods
|
||
|
methodInfo = interfaceType.GetMethod(methodName, bindingFlags);
|
||
|
if (methodInfo == null)
|
||
|
{
|
||
|
throw new MissingMethodException(interfaceType.AssemblyQualifiedName, methodName);
|
||
|
}
|
||
|
customAttrs = methodInfo.GetCustomAttributes(typeof(CorrelationAliasAttribute), true);
|
||
|
}
|
||
|
|
||
|
foreach (CorrelationAliasAttribute aliasAttribute in customAttrs)
|
||
|
{
|
||
|
if (customAttrs.Length > 1)
|
||
|
{
|
||
|
Debug.Assert(aliasAttribute.Name != null);
|
||
|
if (aliasAttribute.Name == null)
|
||
|
throw new ArgumentNullException("ParameterName");
|
||
|
}
|
||
|
correlationAliases.Add(aliasAttribute.Name == null ? "" : aliasAttribute.Name, aliasAttribute);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private CorrelationPropertyValue GetCorrelationProperty(ParameterInfo[] parameters, string propertyName, string parameterName, string location)
|
||
|
{
|
||
|
for (int j = 0; parameters != null && j < parameters.Length; j++)
|
||
|
{
|
||
|
ParameterInfo param = parameters[j];
|
||
|
if (param.Name == parameterName)
|
||
|
{
|
||
|
// parameter match
|
||
|
return new CorrelationPropertyValue(propertyName, location, param.Position);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private object[] GetCorrelationParameterAttributes(Type type)
|
||
|
{
|
||
|
return type.GetCustomAttributes(typeof(CorrelationParameterAttribute), true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sealed class NonCorrelatedProvider : ICorrelationProvider
|
||
|
{
|
||
|
internal NonCorrelatedProvider()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
ICollection<CorrelationProperty> ICorrelationProvider.ResolveCorrelationPropertyValues(Type interfaceType, string methodName, object[] methodArgs, bool provideInitializerTokens)
|
||
|
{
|
||
|
// non correlated
|
||
|
// no values to return
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
bool ICorrelationProvider.IsInitializingMember(Type interfaceType, string memberName, object[] methodArgs)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|