#region Imports using System; using System.Reflection; using System.Collections; using System.ComponentModel; using System.Collections.Generic; using System.Runtime.Serialization; using System.ComponentModel.Design; using System.Collections.Specialized; using System.Workflow.ComponentModel; using System.Workflow.ComponentModel.Design; using System.Workflow.ComponentModel.Compiler; using System.ComponentModel.Design.Serialization; using System.CodeDom; using System.Globalization; using System.Workflow.Runtime; using System.Workflow.Activities.Common; #endregion namespace System.Workflow.Activities { internal static class CorrelationSetsValidator { internal static ValidationErrorCollection Validate(ValidationManager manager, Object obj) { ValidationErrorCollection validationErrors = new ValidationErrorCollection(); Activity activity = obj as Activity; if (!(activity is CallExternalMethodActivity) && !(activity is HandleExternalEventActivity)) throw new ArgumentException(SR.GetString(SR.Error_UnexpectedArgumentType, typeof(Activity).FullName), "obj"); Type interfaceType = (activity is CallExternalMethodActivity) ? ((CallExternalMethodActivity)activity).InterfaceType : ((HandleExternalEventActivity)activity).InterfaceType; if (interfaceType == null) return validationErrors; if (interfaceType.ContainsGenericParameters) { ValidationError error = new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_GenericMethodsNotSupported), interfaceType.FullName), ErrorNumbers.Error_GenericMethodsNotSupported); error.PropertyName = "InterfaceType"; validationErrors.Add(error); return validationErrors; } object[] attributes = interfaceType.GetCustomAttributes(typeof(ExternalDataExchangeAttribute), false); if (attributes.Length == 0) { ValidationError error = new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_ExternalDataExchangeException), interfaceType.FullName), ErrorNumbers.Error_TypeNotExist); error.PropertyName = "InterfaceType"; validationErrors.Add(error); return validationErrors; } if (activity.Site == null) { ValidationErrorCollection interfaceErrors = ValidateHostInterface(manager, interfaceType, activity); if (interfaceErrors.Count != 0) { validationErrors.AddRange(interfaceErrors); return validationErrors; } } MemberInfo targetMember = null; if (activity is CallExternalMethodActivity) { if (((CallExternalMethodActivity)activity).MethodName == null || ((CallExternalMethodActivity)activity).MethodName.Length == 0) return validationErrors; MethodInfo methodInfo = interfaceType.GetMethod(((CallExternalMethodActivity)activity).MethodName, BindingFlags.Instance | BindingFlags.Public); if (methodInfo == null || methodInfo.IsSpecialName) { validationErrors.Add(new ValidationError(SR.GetString(SR.Error_MissingMethodName, activity.Name, ((CallExternalMethodActivity)activity).MethodName), ErrorNumbers.Error_MissingMethodName)); return validationErrors; } if (methodInfo.ContainsGenericParameters) { ValidationError error = new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_GenericMethodsNotSupported), methodInfo.Name), ErrorNumbers.Error_GenericMethodsNotSupported); error.PropertyName = "MethodName"; validationErrors.Add(error); return validationErrors; } targetMember = methodInfo; } else { if (((HandleExternalEventActivity)activity).EventName == null || ((HandleExternalEventActivity)activity).EventName.Length == 0) return validationErrors; EventInfo eventInfo = interfaceType.GetEvent(((HandleExternalEventActivity)activity).EventName, BindingFlags.Instance | BindingFlags.Public); if (eventInfo == null) { validationErrors.Add(new ValidationError(SR.GetString(SR.Error_MissingEventName, activity.Name, ((HandleExternalEventActivity)activity).EventName), ErrorNumbers.Error_MissingMethodName)); return validationErrors; } targetMember = eventInfo; } attributes = interfaceType.GetCustomAttributes(typeof(CorrelationProviderAttribute), false); if (attributes.Length != 0) return validationErrors; CorrelationToken correlator = activity.GetValue((activity is CallExternalMethodActivity) ? CallExternalMethodActivity.CorrelationTokenProperty : HandleExternalEventActivity.CorrelationTokenProperty) as CorrelationToken; object[] correlationParameterAttributes = interfaceType.GetCustomAttributes(typeof(CorrelationParameterAttribute), false); if (correlationParameterAttributes.Length == 0) { if (correlator != null) validationErrors.Add(new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_CorrelationTokenSpecifiedForUncorrelatedInterface), activity.QualifiedName, interfaceType), ErrorNumbers.Error_InvalidIdentifier, false, "CorrelationToken")); return validationErrors; } // Someone derived from the activity and compiled, don't generate errors (P || C) validation. if (activity.Parent == null) return validationErrors; if (correlator == null || String.IsNullOrEmpty(correlator.Name)) { validationErrors.Add(new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_MissingCorrelationTokenProperty), activity.QualifiedName), ErrorNumbers.Error_ParameterPropertyNotSet, false, "CorrelationToken")); return validationErrors; } if (String.IsNullOrEmpty(correlator.OwnerActivityName)) { validationErrors.Add(new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_MissingCorrelationTokenOwnerNameProperty), activity.QualifiedName), ErrorNumbers.Error_ParameterPropertyNotSet, false, "CorrelationToken")); return validationErrors; } string qualifiedCorrelationToken = null; Activity sourceActivity = activity.GetActivityByName(correlator.OwnerActivityName); if (sourceActivity == null) sourceActivity = Helpers.ParseActivityForBind(activity, correlator.OwnerActivityName); if (sourceActivity != null) qualifiedCorrelationToken = sourceActivity.QualifiedName; Activity replicatorParent = null; CompositeActivity parent = activity.Parent; Activity rootActivity = parent; bool ownerIsParent = false; while (parent != null) { // We hardcode Replicator here, not MultiInstance | Concurrent. if (parent is ReplicatorActivity && replicatorParent == null) replicatorParent = parent; if (qualifiedCorrelationToken == parent.QualifiedName) ownerIsParent = true; rootActivity = parent; parent = parent.Parent; } if (!ownerIsParent) { ValidationError error = new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_OwnerActivityIsNotParent), activity.QualifiedName), ErrorNumbers.Error_ParameterPropertyNotSet); error.PropertyName = "CorrelationToken"; validationErrors.Add(error); } bool initializer = false; attributes = targetMember.GetCustomAttributes(typeof(CorrelationInitializerAttribute), false) as object[]; if (attributes.Length > 0) initializer = true; if (initializer) { if (replicatorParent != null && activity is HandleExternalEventActivity) { ValidationError error = new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_InitializerInReplicator), replicatorParent.QualifiedName), ErrorNumbers.Error_InitializerInReplicator, false); error.PropertyName = "CorrelationToken"; validationErrors.Add(error); } } if (!string.IsNullOrEmpty(qualifiedCorrelationToken)) { if (replicatorParent != null) { bool isValid = false; Walker walker = new Walker(); walker.FoundActivity += delegate(Walker w, WalkerEventArgs args) { if (!args.CurrentActivity.Enabled) return; if (args.CurrentActivity.QualifiedName == qualifiedCorrelationToken) { isValid = true; args.Action = WalkerAction.Abort; return; } }; walker.Walk(replicatorParent); if (!isValid) { ValidationError error = new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_CorrelationTokenInReplicator, correlator.Name, replicatorParent.QualifiedName)), ErrorNumbers.Error_CorrelationTokenInReplicator, true); error.PropertyName = "CorrelationToken"; validationErrors.Add(error); } } //if (rootActivity is RootActivity) { if (!initializer) { bool isValid = false; bool ownerNameValidated = false; bool initFollowerInTxnlScope = false; Walker walker = new Walker(); walker.FoundActivity += delegate(Walker w, WalkerEventArgs args) { Activity currentActivity = args.CurrentActivity; if (!currentActivity.Enabled) return; if (!(currentActivity is CallExternalMethodActivity) && !(currentActivity is HandleExternalEventActivity)) return; CorrelationToken existingCorrelationTokenValue = currentActivity.GetValue((currentActivity is CallExternalMethodActivity) ? CallExternalMethodActivity.CorrelationTokenProperty : HandleExternalEventActivity.CorrelationTokenProperty) as CorrelationToken; if (existingCorrelationTokenValue == null) return; if (currentActivity is CallExternalMethodActivity && !interfaceType.Equals(((CallExternalMethodActivity)currentActivity).InterfaceType)) return; else if (currentActivity is HandleExternalEventActivity && !interfaceType.Equals(((HandleExternalEventActivity)currentActivity).InterfaceType)) return; MemberInfo existingTargetMember = null; if (currentActivity is CallExternalMethodActivity) { if (((CallExternalMethodActivity)currentActivity).MethodName == null || ((CallExternalMethodActivity)currentActivity).MethodName.Length == 0) return; MethodInfo methodInfo = interfaceType.GetMethod(((CallExternalMethodActivity)currentActivity).MethodName, BindingFlags.Instance | BindingFlags.Public); if (methodInfo == null || methodInfo.IsSpecialName) return; existingTargetMember = methodInfo; } else { if (((HandleExternalEventActivity)currentActivity).EventName == null || ((HandleExternalEventActivity)currentActivity).EventName.Length == 0) return; EventInfo eventInfo = interfaceType.GetEvent(((HandleExternalEventActivity)currentActivity).EventName, BindingFlags.Instance | BindingFlags.Public); if (eventInfo == null) return; existingTargetMember = eventInfo; } attributes = existingTargetMember.GetCustomAttributes(typeof(CorrelationInitializerAttribute), false) as object[]; if (attributes.Length == 0) return; if (activity is HandleExternalEventActivity) { Activity txnlParent = GetTransactionalScopeParent(currentActivity); if (txnlParent != null && IsFollowerInTxnlScope(txnlParent, activity)) initFollowerInTxnlScope = true; } string existingQualifiedCorrelationToken = null; sourceActivity = activity.GetActivityByName(existingCorrelationTokenValue.OwnerActivityName); if (sourceActivity == null) sourceActivity = Helpers.ParseActivityForBind(activity, existingCorrelationTokenValue.OwnerActivityName); if (sourceActivity != null) existingQualifiedCorrelationToken = sourceActivity.QualifiedName; if ((correlator.Name == existingCorrelationTokenValue.Name) && IsOwnerActivitySame(correlator.OwnerActivityName, existingCorrelationTokenValue.OwnerActivityName, activity, currentActivity)) { isValid = true; ownerNameValidated = true; args.Action = WalkerAction.Abort; return; } }; walker.Walk(rootActivity); if (!isValid) { ValidationError error = new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_UninitializedCorrelation)), ErrorNumbers.Error_UninitializedCorrelation, true); error.PropertyName = "CorrelationToken"; validationErrors.Add(error); if (ownerNameValidated) { error = new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_MisMatchCorrelationTokenOwnerNameProperty), correlator.Name), ErrorNumbers.Error_UninitializedCorrelation, false); error.PropertyName = "CorrelationToken"; validationErrors.Add(error); } } if (initFollowerInTxnlScope) { ValidationError error = new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_InitializerFollowerInTxnlScope)), ErrorNumbers.Error_InitializerFollowerInTxnlScope, false); error.PropertyName = "CorrelationToken"; validationErrors.Add(error); } } } } return validationErrors; } private static Activity GetTransactionalScopeParent(Activity activity) { Activity parent = activity; while (parent != null) { if (parent is CompensatableTransactionScopeActivity || parent is TransactionScopeActivity) { return parent; } parent = parent.Parent; } return parent; } private static bool IsFollowerInTxnlScope(Activity parent, Activity activity) { Activity currentParent = activity; while (currentParent != null) { if (currentParent == parent) { return true; } currentParent = currentParent.Parent; } return false; } private static bool IsOwnerActivitySame(string ownerActivityName, string existingOwnerActivityName, Activity currentActivity, Activity existingActivity) { if (ownerActivityName.Equals(existingOwnerActivityName)) return true; Activity owner = currentActivity.GetActivityByName(ownerActivityName); if (owner == null) owner = Helpers.ParseActivityForBind(currentActivity, ownerActivityName); Activity existingowner = currentActivity.GetActivityByName(existingOwnerActivityName); if (existingowner == null) existingowner = Helpers.ParseActivityForBind(existingActivity, existingOwnerActivityName); if (owner != null && existingowner != null && owner.QualifiedName.Equals(existingowner.QualifiedName)) return true; return false; } private static ValidationErrorCollection ValidateHostInterface(IServiceProvider serviceProvider, Type interfaceType, Activity activity) { Dictionary typesValidated = serviceProvider.GetService(typeof(Dictionary)) as Dictionary; if (typesValidated == null) { typesValidated = new Dictionary(); IServiceContainer serviceContainer = serviceProvider.GetService(typeof(IServiceContainer)) as IServiceContainer; if (serviceContainer != null) serviceContainer.AddService(typeof(Dictionary), typesValidated); } if (typesValidated.ContainsKey(interfaceType)) return new ValidationErrorCollection(); typesValidated.Add(interfaceType, new ValidationErrorCollection()); object[] attributes = interfaceType.GetCustomAttributes(typeof(CorrelationProviderAttribute), false); if (attributes.Length == 0) { object[] dsAttribs = interfaceType.GetCustomAttributes(typeof(ExternalDataExchangeAttribute), false); object[] corrParamAttribs = interfaceType.GetCustomAttributes(typeof(CorrelationParameterAttribute), false); if (dsAttribs.Length != 0 && corrParamAttribs.Length != 0) { typesValidated[interfaceType].AddRange(ValidateHostInterfaceMembers(interfaceType, activity)); typesValidated[interfaceType].AddRange(ValidateHostInterfaceAttributes(interfaceType)); } else { typesValidated[interfaceType].AddRange(ValidateInvalidHostInterfaceAttributes(interfaceType)); } } return typesValidated[interfaceType]; } private static ValidationErrorCollection ValidateHostInterfaceMembers(Type interfaceType, Activity activity) { ValidationErrorCollection validationErrors = new ValidationErrorCollection(); foreach (MemberInfo memberInfo in interfaceType.GetMembers()) { if (!(memberInfo is MethodInfo) && !(memberInfo is EventInfo)) continue; if ((memberInfo is MethodInfo) && ((MethodInfo)memberInfo).IsSpecialName) continue; MethodInfo methodInfo = null; Type delegateType = null; if (memberInfo is EventInfo) { EventInfo eventInfo = (EventInfo)memberInfo; delegateType = eventInfo.EventHandlerType; if (delegateType == null) delegateType = TypeProvider.GetEventHandlerType(eventInfo); if (delegateType == null) throw new InvalidOperationException(); methodInfo = delegateType.GetMethod("Invoke"); } else methodInfo = (MethodInfo)memberInfo; if (methodInfo.IsGenericMethod) { ValidationError error = new ValidationError( string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_GenericMethodsNotSupported), (memberInfo is EventInfo) ? delegateType.Name : methodInfo.Name), ErrorNumbers.Error_GenericMethodsNotSupported); if (memberInfo is EventInfo) error.UserData.Add(typeof(EventInfo), ((EventInfo)memberInfo).Name); else error.UserData.Add(typeof(MethodInfo), methodInfo.Name); validationErrors.Add(error); } if (methodInfo.ReturnType != typeof(void) && (memberInfo is EventInfo)) { ValidationError error = new ValidationError( string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_ReturnTypeNotVoid), (memberInfo is EventInfo) ? delegateType.Name : methodInfo.Name), ErrorNumbers.Error_ReturnTypeNotVoid); if (memberInfo is EventInfo) error.UserData.Add(typeof(EventInfo), ((EventInfo)memberInfo).Name); else error.UserData.Add(typeof(MethodInfo), methodInfo.Name); validationErrors.Add(error); } foreach (ParameterInfo param in methodInfo.GetParameters()) { if (param.IsOut || param.IsRetval) { ValidationError error = new ValidationError( string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_OutRefParameterNotSupported), (memberInfo is EventInfo) ? delegateType.Name : methodInfo.Name, param.Name), ErrorNumbers.Error_OutRefParameterNotSupported); if (memberInfo is EventInfo) error.UserData.Add(typeof(EventInfo), ((EventInfo)memberInfo).Name); else error.UserData.Add(typeof(MethodInfo), methodInfo.Name); error.UserData.Add(typeof(ParameterInfo), param.Name); validationErrors.Add(error); } } } return validationErrors; } private static ValidationErrorCollection ValidateHostInterfaceAttributes(Type interfaceType) { if (interfaceType == null) throw new ArgumentNullException("interfaceType"); ValidationErrorCollection validationErrors = new ValidationErrorCollection(); ArrayList parameterAttrs = new ArrayList(); foreach (object interfaceAttribute in interfaceType.GetCustomAttributes(typeof(CorrelationParameterAttribute), false)) { CorrelationParameterAttribute parameterAttribute = Helpers.GetAttributeFromObject(interfaceAttribute); if (String.IsNullOrEmpty(parameterAttribute.Name) || parameterAttribute.Name.Trim().Length == 0) { ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_CorrelationAttributeInvalid, typeof(CorrelationParameterAttribute).Name, "Name", interfaceType.Name), ErrorNumbers.Error_CorrelationAttributeInvalid); error.UserData.Add(typeof(CorrelationParameterAttribute), interfaceType.Name); validationErrors.Add(error); continue; } if (parameterAttrs.Contains(parameterAttribute.Name)) { ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_DuplicateCorrelationAttribute, typeof(CorrelationParameterAttribute).Name, parameterAttribute.Name, interfaceType.Name), ErrorNumbers.Error_DuplicateCorrelationAttribute); error.UserData.Add(typeof(CorrelationParameterAttribute), interfaceType.Name); validationErrors.Add(error); continue; } parameterAttrs.Add(parameterAttribute.Name); } Hashtable paramTypes = new Hashtable(); Hashtable memberInfoCorrelationAliasAttrs = new Hashtable(); // MemberInfo -> CorrelationAliases Hashtable delegateTypeCorrelationAliasAttrs = new Hashtable(); // Delegate type -> CorrelationAliases int initializerCount = 0; foreach (MemberInfo memberInfo in interfaceType.GetMembers()) { if (memberInfo is MethodInfo && !((MethodInfo)memberInfo).IsSpecialName) { Hashtable correlationAliasAttrs = new Hashtable(); memberInfoCorrelationAliasAttrs.Add(memberInfo, correlationAliasAttrs); FillCorrelationAliasAttrs(memberInfo, correlationAliasAttrs, validationErrors); int aliasLength = memberInfo.GetCustomAttributes(typeof(CorrelationInitializerAttribute), false).Length; initializerCount += aliasLength; if (aliasLength > 0) { foreach (string paramName in parameterAttrs) { string paramPath = paramName; if (correlationAliasAttrs.Contains(paramName)) paramPath = ((CorrelationAliasAttribute)correlationAliasAttrs[paramName]).Path; Type paramType = FetchParameterType(memberInfo, paramPath); if (paramType != null) { if (!paramTypes.ContainsKey(paramName)) paramTypes[paramName] = paramType; } } } } else if (memberInfo is EventInfo) { int aliasLength = memberInfo.GetCustomAttributes(typeof(CorrelationInitializerAttribute), false).Length; initializerCount += aliasLength; // Add event info it's delegate's CorrelationAliasAttributes // to memberInfoCorrelationAliasAttrs against the EventInfo. Hashtable correlationAliasAttrs = new Hashtable(); memberInfoCorrelationAliasAttrs.Add(memberInfo, correlationAliasAttrs); FillCorrelationAliasAttrs(memberInfo, correlationAliasAttrs, validationErrors); Type delegateType = Helpers.GetDelegateFromEvent((EventInfo)memberInfo); MethodInfo delegateMethod = delegateType.GetMethod("Invoke"); FillCorrelationAliasAttrs(delegateType, correlationAliasAttrs, validationErrors); // Add event delegate's CorrelationAliasAttributes // to delegateTypeCorrelationAliasAttrs against the deletgate type. Hashtable delegateCorrelationAliasAttrs = new Hashtable(); FillCorrelationAliasAttrs(delegateType, delegateCorrelationAliasAttrs, validationErrors); if (delegateTypeCorrelationAliasAttrs[delegateType] == null) delegateTypeCorrelationAliasAttrs.Add(delegateType, delegateCorrelationAliasAttrs); if (aliasLength > 0) { foreach (string paramName in parameterAttrs) { string paramPath = paramName; if (correlationAliasAttrs.Contains(paramName)) paramPath = ((CorrelationAliasAttribute)correlationAliasAttrs[paramName]).Path; Type paramType = FetchParameterType(memberInfo, paramPath); if (paramType != null) { if (!paramTypes.ContainsKey(paramName)) paramTypes[paramName] = paramType; } } } } } // validate : correlaion alias has corresponding parameter foreach (DictionaryEntry memberEntry in memberInfoCorrelationAliasAttrs) { MemberInfo memberInfo = memberEntry.Key as MemberInfo; Hashtable correlationAliasAttrs = (Hashtable)memberEntry.Value; foreach (string paramName in correlationAliasAttrs.Keys) { if (!parameterAttrs.Contains(paramName)) { // Ignore the error if the alias attribute is from the event delegate. if (memberInfo is EventInfo && ((Hashtable)delegateTypeCorrelationAliasAttrs[Helpers.GetDelegateFromEvent((EventInfo)memberInfo)])[paramName] != null) continue; ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_CorrelationParameterNotFound, typeof(CorrelationAliasAttribute).Name, paramName, memberInfo.Name, typeof(CorrelationParameterAttribute).Name, interfaceType.Name), ErrorNumbers.Error_CorrelationParameterNotFound); error.UserData.Add(typeof(CorrelationAliasAttribute), memberInfo.Name); validationErrors.Add(error); } } } // validate : correlation parameters has valid entries in all members foreach (string paramName in parameterAttrs) { foreach (DictionaryEntry memberEntry in memberInfoCorrelationAliasAttrs) { string paramPath = paramName; MemberInfo memberInfo = (MemberInfo)memberEntry.Key; Hashtable correlationAliasAttrs = (Hashtable)memberEntry.Value; if (correlationAliasAttrs.Contains(paramName)) paramPath = ((CorrelationAliasAttribute)correlationAliasAttrs[paramName]).Path; Type paramType = FetchParameterType((MemberInfo)memberEntry.Key, paramPath); if (paramType == null) { // Ignore the error if the alias attribute is from the event delegate. if (memberInfo is EventInfo && ((Hashtable)delegateTypeCorrelationAliasAttrs[Helpers.GetDelegateFromEvent((EventInfo)memberInfo)])[paramName] != null) continue; //error path not resolved ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_CorrelationInvalid, (memberInfo.DeclaringType == interfaceType) ? memberInfo.Name : memberInfo.DeclaringType.Name, paramName), ErrorNumbers.Error_CorrelationInvalid); error.UserData.Add(typeof(CorrelationParameterAttribute), (memberInfo.DeclaringType == interfaceType) ? memberInfo.Name : memberInfo.DeclaringType.Name); validationErrors.Add(error); } else if (paramTypes.ContainsKey(paramName) && (Type)paramTypes[paramName] != paramType) { // error parameter type mismatch ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_CorrelationTypeNotConsistent, paramPath, typeof(CorrelationAliasAttribute).Name, (memberInfo.DeclaringType == interfaceType) ? memberInfo.Name : memberInfo.DeclaringType.Name, paramType.Name, ((Type)paramTypes[paramName]).Name, paramName, interfaceType.Name), ErrorNumbers.Error_CorrelationTypeNotConsistent); error.UserData.Add(typeof(CorrelationAliasAttribute), (memberInfo.DeclaringType == interfaceType) ? memberInfo.Name : memberInfo.DeclaringType.Name); validationErrors.Add(error); } } } if (initializerCount == 0) { ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_CorrelationInitializerNotDefinied, interfaceType.Name), ErrorNumbers.Error_CorrelationInitializerNotDefinied); error.UserData.Add(typeof(CorrelationInitializerAttribute), interfaceType.Name); validationErrors.Add(error); } return validationErrors; } private static ValidationErrorCollection ValidateInvalidHostInterfaceAttributes(Type interfaceType) { if (interfaceType == null) throw new ArgumentNullException("interfaceType"); ValidationErrorCollection validationErrors = new ValidationErrorCollection(); bool corrAttrsFound = false; foreach (MemberInfo memberInfo in interfaceType.GetMembers()) { if (memberInfo.GetCustomAttributes(typeof(CorrelationInitializerAttribute), false).Length != 0 || memberInfo.GetCustomAttributes(typeof(CorrelationAliasAttribute), false).Length != 0) { corrAttrsFound = true; break; } } if (corrAttrsFound) { ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_MissingCorrelationParameterAttribute, interfaceType.Name), ErrorNumbers.Error_MissingCorrelationParameterAttribute); error.UserData.Add(typeof(CorrelationParameterAttribute), interfaceType.Name); validationErrors.Add(error); } return validationErrors; } private static Type FetchParameterType(MemberInfo memberInfo, string paramPath) { MethodInfo method = null; if (memberInfo is EventInfo) { Type delegateType = Helpers.GetDelegateFromEvent((EventInfo)memberInfo); method = delegateType.GetMethod("Invoke"); } else method = (MethodInfo)memberInfo; return GetCorrelationParameterType(paramPath, method.GetParameters()); } private static void FillCorrelationAliasAttrs(MemberInfo memberInfo, Hashtable correlationAliasAttrs, ValidationErrorCollection validationErrors) { foreach (object memberAttribute in memberInfo.GetCustomAttributes(typeof(CorrelationAliasAttribute), false)) { CorrelationAliasAttribute aliasAttribute = Helpers.GetAttributeFromObject(memberAttribute); // fill validation errors, name, path, duplicate check if (String.IsNullOrEmpty(aliasAttribute.Name) || aliasAttribute.Name.Trim().Length == 0) { ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_CorrelationAttributeInvalid, typeof(CorrelationAliasAttribute).Name, "Name", memberInfo.Name), ErrorNumbers.Error_CorrelationAttributeInvalid); error.UserData.Add(typeof(CorrelationAliasAttribute), memberInfo.Name); validationErrors.Add(error); continue; } if (String.IsNullOrEmpty(aliasAttribute.Path) || aliasAttribute.Path.Trim().Length == 0) { ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_CorrelationAttributeInvalid, typeof(CorrelationAliasAttribute).Name, "Path", memberInfo.Name), ErrorNumbers.Error_CorrelationAttributeInvalid); error.UserData.Add(typeof(CorrelationAliasAttribute), memberInfo.Name); validationErrors.Add(error); continue; } if (correlationAliasAttrs.Contains(aliasAttribute.Name)) { ValidationError error = new ValidationError(SR.GetString(CultureInfo.CurrentCulture, SR.Error_DuplicateCorrelationAttribute, typeof(CorrelationAliasAttribute).Name, aliasAttribute.Name, memberInfo.Name), ErrorNumbers.Error_DuplicateCorrelationAttribute); error.UserData.Add(typeof(CorrelationAliasAttribute), memberInfo.Name); validationErrors.Add(error); continue; } correlationAliasAttrs.Add(aliasAttribute.Name, aliasAttribute); } return; } private static Type GetCorrelationParameterType(string parameterPropertyName, object parametersCollection) { string[] parsedPropertyName = parameterPropertyName.Split('.'); Type correlationParameterType = null; int index = 0; if (parsedPropertyName.Length == 1) { Type evntHandlerType = null; if (parametersCollection is CodeParameterDeclarationExpressionCollection) { foreach (CodeParameterDeclarationExpression parameterDeclaration in (CodeParameterDeclarationExpressionCollection)parametersCollection) { if (String.Compare("e", parameterDeclaration.Name, StringComparison.Ordinal) == 0) evntHandlerType = parameterDeclaration.UserData[typeof(Type)] as Type; } } else if (parametersCollection is ParameterInfo[]) { foreach (ParameterInfo parameterInfo in (ParameterInfo[])parametersCollection) { if (String.Compare("e", parameterInfo.Name, StringComparison.Ordinal) == 0) evntHandlerType = parameterInfo.ParameterType; } } if (evntHandlerType != null) { string paramName = parsedPropertyName[0]; parsedPropertyName = new string[] { "e", paramName }; } } if (parametersCollection is CodeParameterDeclarationExpressionCollection) { foreach (CodeParameterDeclarationExpression parameterDeclaration in (CodeParameterDeclarationExpressionCollection)parametersCollection) { if (String.Compare(parsedPropertyName[0], parameterDeclaration.Name, StringComparison.Ordinal) == 0) correlationParameterType = parameterDeclaration.UserData[typeof(Type)] as Type; } } else if (parametersCollection is ParameterInfo[]) { foreach (ParameterInfo parameterInfo in (ParameterInfo[])parametersCollection) { if (String.Compare(parsedPropertyName[0], parameterInfo.Name, StringComparison.Ordinal) == 0) correlationParameterType = parameterInfo.ParameterType; } } else return null; if (parsedPropertyName.Length == 1) return correlationParameterType; //Search each part of the parsed name in it's predecessor's public properties/fields for (index = 1; index < parsedPropertyName.Length && correlationParameterType != null; index++) { Type tempParameterType = null; //Search though the public properties for a matching name PropertyInfo[] publicProperties = correlationParameterType.GetProperties(); foreach (PropertyInfo propertyInfo in publicProperties) { tempParameterType = null; if (String.Compare(propertyInfo.Name, parsedPropertyName[index], StringComparison.Ordinal) == 0) { tempParameterType = propertyInfo.PropertyType; break; } } if (tempParameterType != null) { correlationParameterType = tempParameterType; continue; } //Search though the public fields for a matching name FieldInfo[] publicFields = correlationParameterType.GetFields(); foreach (FieldInfo fieldInfo in publicFields) { tempParameterType = null; if (String.Compare(fieldInfo.Name, parsedPropertyName[index], StringComparison.Ordinal) == 0) { tempParameterType = fieldInfo.FieldType; break; } } if (tempParameterType != null) { correlationParameterType = tempParameterType; continue; } // If a matching public field or property was not found, return null. if (tempParameterType == null) return null; } if (index == parsedPropertyName.Length) return correlationParameterType; return null; } } internal static class ParameterBindingValidator { internal static ValidationErrorCollection Validate(ValidationManager manager, Object obj) { ValidationErrorCollection validationErrors = new ValidationErrorCollection(); Activity activity = obj as Activity; if (!(activity is CallExternalMethodActivity) && !(activity is HandleExternalEventActivity)) throw new ArgumentException(SR.GetString(SR.Error_UnexpectedArgumentType, typeof(Activity).FullName), "obj"); Type interfaceType = (activity is CallExternalMethodActivity) ? ((CallExternalMethodActivity)activity).InterfaceType : ((HandleExternalEventActivity)activity).InterfaceType; if (interfaceType == null) return validationErrors; string operation = (activity is CallExternalMethodActivity) ? ((CallExternalMethodActivity)activity).MethodName : ((HandleExternalEventActivity)activity).EventName; if (String.IsNullOrEmpty(operation)) return validationErrors; WorkflowParameterBindingCollection parameterBinding = (activity is CallExternalMethodActivity) ? ((CallExternalMethodActivity)activity).ParameterBindings : ((HandleExternalEventActivity)activity).ParameterBindings; MethodInfo mInfo = interfaceType.GetMethod(operation); if (mInfo == null) { if (activity is CallExternalMethodActivity) { return validationErrors; } } bool isEvent = false; if (mInfo == null) { //This is a work around for delgates of unbounded generic type. There is no support //in file code model for these so we dont support it for now. The only way //to detect if the DesignTimeEventInfo has EventHandler of unbounded generic type //is to check if we get the methods correctly here Ref Bug#17783 EventInfo eventInfo = interfaceType.GetEvent(operation); if (eventInfo == null || eventInfo.GetAddMethod(true) == null) { return validationErrors; } Type delegateType = eventInfo.EventHandlerType; if (delegateType == null) delegateType = TypeProvider.GetEventHandlerType(eventInfo); mInfo = delegateType.GetMethod("Invoke"); isEvent = true; } ValidateParameterBinding(manager, activity, isEvent, operation, mInfo, parameterBinding, validationErrors); return validationErrors; } private static void ValidateParameterBinding(ValidationManager manager, Activity activity, bool isEvent, string operation, MethodInfo mInfo, WorkflowParameterBindingCollection parameterBindings, ValidationErrorCollection validationErrors) { Hashtable parameterCollection = new Hashtable(); ParameterInfo[] parameters = mInfo.GetParameters(); bool canBeIntercepted = false; foreach (ParameterInfo parameter in parameters) { if (TypeProvider.IsAssignable(typeof(ExternalDataEventArgs), parameter.ParameterType)) { if (parameter.Position == 1) canBeIntercepted = true; ValidateParameterSerializabiltiy(validationErrors, parameter.ParameterType); } parameterCollection.Add(parameter.Name, parameter); } if (isEvent && (!canBeIntercepted || parameters.Length != 2)) validationErrors.Add(new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_InvalidEventArgsSignature, operation)), ErrorNumbers.Error_FieldNotExists, false, "EventName")); if (mInfo.ReturnType != typeof(void)) parameterCollection.Add("(ReturnValue)", mInfo.ReturnParameter); foreach (WorkflowParameterBinding parameterBinding in parameterBindings) { string paramName = parameterBinding.ParameterName; if (!parameterCollection.ContainsKey(paramName)) { if (isEvent) validationErrors.Add(new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_InvalidEventPropertyName, paramName)), ErrorNumbers.Error_FieldNotExists, false, "ParameterBindings")); else validationErrors.Add(new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_InvalidMethodPropertyName, paramName)), ErrorNumbers.Error_FieldNotExists, false, "ParameterBindings")); continue; } object paramValue = null; if (parameterBinding.IsBindingSet(WorkflowParameterBinding.ValueProperty)) paramValue = parameterBinding.GetBinding(WorkflowParameterBinding.ValueProperty); else paramValue = parameterBinding.GetValue(WorkflowParameterBinding.ValueProperty); if (paramValue == null) continue; ParameterInfo paramInfo = parameterCollection[paramName] as ParameterInfo; if (paramInfo != null) { AccessTypes access = AccessTypes.Read; if (paramInfo.IsOut || paramInfo.IsRetval) access = AccessTypes.Write; else if (paramInfo.ParameterType.IsByRef) access |= AccessTypes.Write; ValidationErrorCollection variableErrors = ValidationHelpers.ValidateProperty(manager, activity, paramValue, new PropertyValidationContext(parameterBinding, null, paramName), new BindValidationContext(paramInfo.ParameterType.IsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType, access)); validationErrors.AddRange(variableErrors); } } } private static void ValidateParameterSerializabiltiy(ValidationErrorCollection validationErrors, Type type) { object[] attrs = type.GetCustomAttributes(typeof(SerializableAttribute), false); Type serializableType = type.GetInterface(typeof(ISerializable).FullName); if (attrs.Length == 0 && serializableType == null) { validationErrors.Add(new ValidationError(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_EventArgumentValidationException), type.FullName), ErrorNumbers.Error_FieldNotExists, false, "EventName")); } } } }