//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Description { using System; using System.CodeDom; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.Reflection; using System.Runtime; using System.ServiceModel; using System.ServiceModel.Channels; using System.Threading; class ClientClassGenerator : IServiceContractGenerationExtension { bool tryAddHelperMethod = false; bool generateEventAsyncMethods = false; internal ClientClassGenerator(bool tryAddHelperMethod) : this(tryAddHelperMethod, false) { } internal ClientClassGenerator(bool tryAddHelperMethod, bool generateEventAsyncMethods) { this.tryAddHelperMethod = tryAddHelperMethod; this.generateEventAsyncMethods = generateEventAsyncMethods; } static Type clientBaseType = typeof(ClientBase<>); static Type duplexClientBaseType = typeof(DuplexClientBase<>); static Type instanceContextType = typeof(InstanceContext); static Type objectType = typeof(object); static Type objectArrayType = typeof(object[]); static Type exceptionType = typeof(Exception); static Type boolType = typeof(bool); static Type stringType = typeof(string); static Type endpointAddressType = typeof(EndpointAddress); static Type uriType = typeof(Uri); static Type bindingType = typeof(Binding); static Type sendOrPostCallbackType = typeof(SendOrPostCallback); static Type asyncCompletedEventArgsType = typeof(AsyncCompletedEventArgs); static Type eventHandlerType = typeof(EventHandler<>); static Type voidType = typeof(void); static Type asyncResultType = typeof(IAsyncResult); static Type asyncCallbackType = typeof(AsyncCallback); static CodeTypeReference voidTypeRef = new CodeTypeReference(typeof(void)); static CodeTypeReference asyncResultTypeRef = new CodeTypeReference(typeof(IAsyncResult)); static string inputInstanceName = "callbackInstance"; static string invokeAsyncCompletedEventArgsTypeName = "InvokeAsyncCompletedEventArgs"; static string invokeAsyncMethodName = "InvokeAsync"; static string raiseExceptionIfNecessaryMethodName = "RaiseExceptionIfNecessary"; static string beginOperationDelegateTypeName = "BeginOperationDelegate"; static string endOperationDelegateTypeName = "EndOperationDelegate"; static string getDefaultValueForInitializationMethodName = "GetDefaultValueForInitialization"; // IMPORTANT: this table tracks the set of .ctors in ClientBase and DuplexClientBase. // This table must be kept in [....] // for DuplexClientBase, the initial InstanceContext param is assumed; ctor overloads must match between ClientBase and DuplexClientBase static Type[][] ClientCtorParamTypes = new Type[][] { new Type[] { }, new Type[] { stringType, }, new Type[] { stringType, stringType, }, new Type[] { stringType, endpointAddressType, }, new Type[] { bindingType, endpointAddressType, }, }; static string[][] ClientCtorParamNames = new string[][] { new string[] { }, new string[] { "endpointConfigurationName", }, new string[] { "endpointConfigurationName", "remoteAddress", }, new string[] { "endpointConfigurationName", "remoteAddress", }, new string[] { "binding", "remoteAddress", }, }; static Type[] EventArgsCtorParamTypes = new Type[] { objectArrayType, exceptionType, boolType, objectType }; static string[] EventArgsCtorParamNames = new string[] { "results", "exception", "cancelled", "userState" }; static string[] EventArgsPropertyNames = new string[] { "Results", "Error", "Cancelled", "UserState" }; #if DEBUG static BindingFlags ctorBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; static string DebugCheckTable_errorString = "Client code generation table out of [....] with ClientBase and DuplexClientBase ctors. Please investigate."; // check the table against what we would get from reflection static void DebugCheckTable() { Fx.Assert(ClientCtorParamNames.Length == ClientCtorParamTypes.Length, DebugCheckTable_errorString); for (int i = 0; i < ClientCtorParamTypes.Length; i++) { DebugCheckTable_ValidateCtor(clientBaseType.GetConstructor(ctorBindingFlags, null, ClientCtorParamTypes[i], null), ClientCtorParamNames[i]); Type[] duplexCtorTypes1 = DebugCheckTable_InsertAtStart(ClientCtorParamTypes[i], objectType); Type[] duplexCtorTypes2 = DebugCheckTable_InsertAtStart(ClientCtorParamTypes[i], instanceContextType); string[] duplexCtorNames = DebugCheckTable_InsertAtStart(ClientCtorParamNames[i], inputInstanceName); DebugCheckTable_ValidateCtor(duplexClientBaseType.GetConstructor(ctorBindingFlags, null, duplexCtorTypes1, null), duplexCtorNames); DebugCheckTable_ValidateCtor(duplexClientBaseType.GetConstructor(ctorBindingFlags, null, duplexCtorTypes2, null), duplexCtorNames); } // ClientBase<> has extra InstanceContext overloads that we do not call directly from the generated code, but which we // need to account for in this assert Fx.Assert(clientBaseType.GetConstructors(ctorBindingFlags).Length == ClientCtorParamTypes.Length * 2, DebugCheckTable_errorString); // DuplexClientBase<> also has extra object/InstanceContext overloads (but we call these) Fx.Assert(duplexClientBaseType.GetConstructors(ctorBindingFlags).Length == ClientCtorParamTypes.Length * 2, DebugCheckTable_errorString); } static T[] DebugCheckTable_InsertAtStart(T[] arr, T item) { T[] newArr = new T[arr.Length + 1]; newArr[0] = item; Array.Copy(arr, 0, newArr, 1, arr.Length); return newArr; } static void DebugCheckTable_ValidateCtor(ConstructorInfo ctor, string[] paramNames) { Fx.Assert(ctor != null, DebugCheckTable_errorString); ParameterInfo[] parameters = ctor.GetParameters(); Fx.Assert(parameters.Length == paramNames.Length, DebugCheckTable_errorString); for (int i = 0; i < paramNames.Length; i++) { Fx.Assert(parameters[i].Name == paramNames[i], DebugCheckTable_errorString); } } #endif void IServiceContractGenerationExtension.GenerateContract(ServiceContractGenerationContext context) { #if DEBUG // DebugCheckTable(); #endif CodeTypeDeclaration clientType = context.TypeFactory.CreateClassType(); // Have to make sure that client name does not match any methods: member names can not be the same as their enclosing type (CSharp only) clientType.Name = NamingHelper.GetUniqueName(GetClientClassName(context.ContractType.Name), DoesMethodNameExist, context.Operations); CodeTypeReference contractTypeRef = context.ContractTypeReference; if (context.DuplexCallbackType == null) clientType.BaseTypes.Add(new CodeTypeReference(context.ServiceContractGenerator.GetCodeTypeReference(typeof(ClientBase<>)).BaseType, context.ContractTypeReference)); else clientType.BaseTypes.Add(new CodeTypeReference(context.ServiceContractGenerator.GetCodeTypeReference(typeof(DuplexClientBase<>)).BaseType, context.ContractTypeReference)); clientType.BaseTypes.Add(context.ContractTypeReference); if (!(ClientCtorParamNames.Length == ClientCtorParamTypes.Length)) { Fx.Assert("Invalid client generation constructor table initialization"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid client generation constructor table initialization"))); } for (int i = 0; i < ClientCtorParamNames.Length; i++) { if (!(ClientCtorParamNames[i].Length == ClientCtorParamTypes[i].Length)) { Fx.Assert("Invalid client generation constructor table initialization"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid client generation constructor table initialization"))); } CodeConstructor ctor = new CodeConstructor(); ctor.Attributes = MemberAttributes.Public; if (context.DuplexCallbackType != null) { ctor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(InstanceContext), inputInstanceName)); ctor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(inputInstanceName)); } for (int j = 0; j < ClientCtorParamNames[i].Length; j++) { ctor.Parameters.Add(new CodeParameterDeclarationExpression(ClientCtorParamTypes[i][j], ClientCtorParamNames[i][j])); ctor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(ClientCtorParamNames[i][j])); } clientType.Members.Add(ctor); } foreach (OperationContractGenerationContext operationContext in context.Operations) { // Note that we generate all the client-side methods, even inherited ones. if (operationContext.Operation.IsServerInitiated()) continue; CodeTypeReference declaringContractTypeRef = operationContext.DeclaringTypeReference; GenerateClientClassMethod(clientType, contractTypeRef, operationContext.SyncMethod, this.tryAddHelperMethod, declaringContractTypeRef); if (operationContext.IsAsync) { CodeMemberMethod beginMethod = GenerateClientClassMethod(clientType, contractTypeRef, operationContext.BeginMethod, this.tryAddHelperMethod, declaringContractTypeRef); CodeMemberMethod endMethod = GenerateClientClassMethod(clientType, contractTypeRef, operationContext.EndMethod, this.tryAddHelperMethod, declaringContractTypeRef); if (this.generateEventAsyncMethods) { GenerateEventAsyncMethods(context, clientType, operationContext.SyncMethod.Name, beginMethod, endMethod); } } if (operationContext.IsTask) { GenerateClientClassMethod(clientType, contractTypeRef, operationContext.TaskMethod, !operationContext.Operation.HasOutputParameters && this.tryAddHelperMethod, declaringContractTypeRef); } } context.Namespace.Types.Add(clientType); context.ClientType = clientType; context.ClientTypeReference = ServiceContractGenerator.NamespaceHelper.GetCodeTypeReference(context.Namespace, clientType); } static CodeMemberMethod GenerateClientClassMethod(CodeTypeDeclaration clientType, CodeTypeReference contractTypeRef, CodeMemberMethod method, bool addHelperMethod, CodeTypeReference declaringContractTypeRef) { CodeMemberMethod methodImpl = GetImplementationOfMethod(contractTypeRef, method); AddMethodImpl(methodImpl); int methodPosition = clientType.Members.Add(methodImpl); CodeMemberMethod helperMethod = null; if (addHelperMethod) { helperMethod = GenerateHelperMethod(declaringContractTypeRef, methodImpl); if (helperMethod != null) { clientType.Members[methodPosition].CustomAttributes.Add(CreateEditorBrowsableAttribute(EditorBrowsableState.Advanced)); clientType.Members.Add(helperMethod); } } return (helperMethod != null) ? helperMethod : methodImpl; } internal static CodeAttributeDeclaration CreateEditorBrowsableAttribute(EditorBrowsableState editorBrowsableState) { CodeAttributeDeclaration browsableAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(EditorBrowsableAttribute))); CodeTypeReferenceExpression browsableAttributeState = new CodeTypeReferenceExpression(typeof(EditorBrowsableState)); CodeAttributeArgument browsableAttributeValue = new CodeAttributeArgument(new CodeFieldReferenceExpression(browsableAttributeState, editorBrowsableState.ToString())); browsableAttribute.Arguments.Add(browsableAttributeValue); return browsableAttribute; } private static CodeMemberMethod GenerateHelperMethod(CodeTypeReference ifaceType, CodeMemberMethod method) { CodeMemberMethod helperMethod = new CodeMemberMethod(); helperMethod.Name = method.Name; helperMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; CodeMethodInvokeExpression invokeMethod = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeCastExpression(ifaceType, new CodeThisReferenceExpression()), method.Name)); bool hasTypedMessage = false; foreach (CodeParameterDeclarationExpression param in method.Parameters) { CodeTypeDeclaration paramTypeDecl = ServiceContractGenerator.NamespaceHelper.GetCodeType(param.Type); if (paramTypeDecl != null) { hasTypedMessage = true; CodeVariableReferenceExpression inValue = new CodeVariableReferenceExpression("inValue"); helperMethod.Statements.Add(new CodeVariableDeclarationStatement(param.Type, inValue.VariableName, new CodeObjectCreateExpression(param.Type))); invokeMethod.Parameters.Add(inValue); GenerateParameters(helperMethod, paramTypeDecl, inValue, FieldDirection.In); } else { helperMethod.Parameters.Add(new CodeParameterDeclarationExpression(param.Type, param.Name)); invokeMethod.Parameters.Add(new CodeArgumentReferenceExpression(param.Name)); } } if (method.ReturnType.BaseType == voidTypeRef.BaseType) helperMethod.Statements.Add(invokeMethod); else { CodeTypeDeclaration returnTypeDecl = ServiceContractGenerator.NamespaceHelper.GetCodeType(method.ReturnType); if (returnTypeDecl != null) { hasTypedMessage = true; CodeVariableReferenceExpression outVar = new CodeVariableReferenceExpression("retVal"); helperMethod.Statements.Add(new CodeVariableDeclarationStatement(method.ReturnType, outVar.VariableName, invokeMethod)); CodeMethodReturnStatement returnStatement = GenerateParameters(helperMethod, returnTypeDecl, outVar, FieldDirection.Out); if (returnStatement != null) helperMethod.Statements.Add(returnStatement); } else { helperMethod.Statements.Add(new CodeMethodReturnStatement(invokeMethod)); helperMethod.ReturnType = method.ReturnType; } } if (hasTypedMessage) method.PrivateImplementationType = ifaceType; return hasTypedMessage ? helperMethod : null; } private static CodeMethodReturnStatement GenerateParameters(CodeMemberMethod helperMethod, CodeTypeDeclaration codeTypeDeclaration, CodeExpression target, FieldDirection dir) { CodeMethodReturnStatement returnStatement = null; foreach (CodeTypeMember member in codeTypeDeclaration.Members) { CodeMemberField field = member as CodeMemberField; if (field == null) continue; CodeFieldReferenceExpression fieldRef = new CodeFieldReferenceExpression(target, field.Name); CodeTypeDeclaration bodyTypeDecl = ServiceContractGenerator.NamespaceHelper.GetCodeType(field.Type); if (bodyTypeDecl != null) { if (dir == FieldDirection.In) helperMethod.Statements.Add(new CodeAssignStatement(fieldRef, new CodeObjectCreateExpression(field.Type))); returnStatement = GenerateParameters(helperMethod, bodyTypeDecl, fieldRef, dir); continue; } CodeParameterDeclarationExpression param = GetRefParameter(helperMethod.Parameters, dir, field); if (param == null && dir == FieldDirection.Out && helperMethod.ReturnType.BaseType == voidTypeRef.BaseType) { helperMethod.ReturnType = field.Type; returnStatement = new CodeMethodReturnStatement(fieldRef); } else { if (param == null) { param = new CodeParameterDeclarationExpression(field.Type, NamingHelper.GetUniqueName(field.Name, DoesParameterNameExist, helperMethod)); param.Direction = dir; helperMethod.Parameters.Add(param); } if (dir == FieldDirection.Out) helperMethod.Statements.Add(new CodeAssignStatement(new CodeArgumentReferenceExpression(param.Name), fieldRef)); else helperMethod.Statements.Add(new CodeAssignStatement(fieldRef, new CodeArgumentReferenceExpression(param.Name))); } } return returnStatement; } private static CodeParameterDeclarationExpression GetRefParameter(CodeParameterDeclarationExpressionCollection parameters, FieldDirection dir, CodeMemberField field) { foreach (CodeParameterDeclarationExpression p in parameters) { if (p.Name == field.Name) { if (p.Direction != dir && p.Type.BaseType == field.Type.BaseType) { p.Direction = FieldDirection.Ref; return p; } return null; } } return null; } internal static bool DoesMemberNameExist(string name, object typeDeclarationObject) { CodeTypeDeclaration typeDeclaration = (CodeTypeDeclaration)typeDeclarationObject; if (string.Compare(typeDeclaration.Name, name, StringComparison.OrdinalIgnoreCase) == 0) { return true; } foreach (CodeTypeMember member in typeDeclaration.Members) { if (string.Compare(member.Name, name, StringComparison.OrdinalIgnoreCase) == 0) { return true; } } return false; } internal static bool DoesTypeNameExists(string name, object codeTypeDeclarationCollectionObject) { CodeTypeDeclarationCollection codeTypeDeclarations = (CodeTypeDeclarationCollection)codeTypeDeclarationCollectionObject; foreach (CodeTypeDeclaration codeTypeDeclaration in codeTypeDeclarations) { if (string.Compare(codeTypeDeclaration.Name, name, StringComparison.OrdinalIgnoreCase) == 0) { return true; } } return false; } internal static bool DoesTypeAndMemberNameExist(string name, object nameCollection) { object[] nameCollections = (object[])nameCollection; if (DoesTypeNameExists(name, nameCollections[0])) { return true; } if (DoesMemberNameExist(name, nameCollections[1])) { return true; } return false; } internal static bool DoesMethodNameExist(string name, object operationsObject) { Collection operations = (Collection)operationsObject; foreach (OperationContractGenerationContext operationContext in operations) { if (String.Compare(operationContext.SyncMethod.Name, name, StringComparison.OrdinalIgnoreCase) == 0) return true; if (operationContext.IsAsync) { if (String.Compare(operationContext.BeginMethod.Name, name, StringComparison.OrdinalIgnoreCase) == 0) return true; if (String.Compare(operationContext.EndMethod.Name, name, StringComparison.OrdinalIgnoreCase) == 0) return true; } if (operationContext.IsTask) { if (String.Compare(operationContext.TaskMethod.Name, name, StringComparison.OrdinalIgnoreCase) == 0) return true; } } return false; } internal static bool DoesParameterNameExist(string name, object methodObject) { CodeMemberMethod method = (CodeMemberMethod)methodObject; if (String.Compare(method.Name, name, StringComparison.OrdinalIgnoreCase) == 0) return true; CodeParameterDeclarationExpressionCollection parameters = method.Parameters; foreach (CodeParameterDeclarationExpression p in parameters) { if (String.Compare(p.Name, name, StringComparison.OrdinalIgnoreCase) == 0) return true; } return false; } static void AddMethodImpl(CodeMemberMethod method) { CodeMethodInvokeExpression methodInvoke = new CodeMethodInvokeExpression(GetChannelReference(), method.Name); foreach (CodeParameterDeclarationExpression parameter in method.Parameters) { methodInvoke.Parameters.Add(new CodeDirectionExpression(parameter.Direction, new CodeVariableReferenceExpression(parameter.Name))); } if (IsVoid(method)) method.Statements.Add(methodInvoke); else method.Statements.Add(new CodeMethodReturnStatement(methodInvoke)); } static CodeMemberMethod GetImplementationOfMethod(CodeTypeReference ifaceType, CodeMemberMethod method) { CodeMemberMethod m = new CodeMemberMethod(); m.Name = method.Name; m.ImplementationTypes.Add(ifaceType); m.Attributes = MemberAttributes.Public | MemberAttributes.Final; foreach (CodeParameterDeclarationExpression parameter in method.Parameters) { CodeParameterDeclarationExpression newParam = new CodeParameterDeclarationExpression(parameter.Type, parameter.Name); newParam.Direction = parameter.Direction; m.Parameters.Add(newParam); } m.ReturnType = method.ReturnType; return m; } static void GenerateEventAsyncMethods(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName, CodeMemberMethod beginMethod, CodeMemberMethod endMethod) { CodeTypeDeclaration operationCompletedEventArgsType = CreateOperationCompletedEventArgsType(context, syncMethodName, endMethod); CodeMemberEvent operationCompletedEvent = CreateOperationCompletedEvent(context, clientType, syncMethodName, operationCompletedEventArgsType); CodeMemberField beginOperationDelegate = CreateBeginOperationDelegate(context, clientType, syncMethodName); CodeMemberMethod beginOperationMethod = CreateBeginOperationMethod(context, clientType, syncMethodName, beginMethod); CodeMemberField endOperationDelegate = CreateEndOperationDelegate(context, clientType, syncMethodName); CodeMemberMethod endOperationMethod = CreateEndOperationMethod(context, clientType, syncMethodName, endMethod); CodeMemberField operationCompletedDelegate = CreateOperationCompletedDelegate(context, clientType, syncMethodName); CodeMemberMethod operationCompletedMethod = CreateOperationCompletedMethod(context, clientType, syncMethodName, operationCompletedEventArgsType, operationCompletedEvent); CodeMemberMethod eventAsyncMethod = CreateEventAsyncMethod(context, clientType, syncMethodName, beginMethod, beginOperationDelegate, beginOperationMethod, endOperationDelegate, endOperationMethod, operationCompletedDelegate, operationCompletedMethod); CreateEventAsyncMethodOverload(clientType, eventAsyncMethod); // hide the normal async methods from intellisense beginMethod.CustomAttributes.Add(CreateEditorBrowsableAttribute(EditorBrowsableState.Advanced)); endMethod.CustomAttributes.Add(CreateEditorBrowsableAttribute(EditorBrowsableState.Advanced)); } static CodeTypeDeclaration CreateOperationCompletedEventArgsType(ServiceContractGenerationContext context, string syncMethodName, CodeMemberMethod endMethod) { if ((endMethod.Parameters.Count == 1) && (endMethod.ReturnType.BaseType == voidTypeRef.BaseType)) { // no need to create new event args type, use AsyncCompletedEventArgs return null; } CodeTypeDeclaration argsType = context.TypeFactory.CreateClassType(); argsType.BaseTypes.Add(new CodeTypeReference(asyncCompletedEventArgsType)); // define object[] results field. CodeMemberField resultsField = new CodeMemberField(); resultsField.Type = new CodeTypeReference(objectArrayType); CodeFieldReferenceExpression resultsFieldReference = new CodeFieldReferenceExpression(); resultsFieldReference.TargetObject = new CodeThisReferenceExpression(); // create constructor, that assigns the results field. CodeConstructor ctor = new CodeConstructor(); ctor.Attributes = MemberAttributes.Public; for (int i = 0; i < EventArgsCtorParamTypes.Length; i++) { ctor.Parameters.Add(new CodeParameterDeclarationExpression(EventArgsCtorParamTypes[i], EventArgsCtorParamNames[i])); if (i > 0) { ctor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(EventArgsCtorParamNames[i])); } } argsType.Members.Add(ctor); ctor.Statements.Add(new CodeAssignStatement(resultsFieldReference, new CodeVariableReferenceExpression(EventArgsCtorParamNames[0]))); // create properties for the out parameters int asyncResultParamIndex = GetAsyncResultParamIndex(endMethod); int count = 0; for (int i = 0; i < endMethod.Parameters.Count; i++) { if (i != asyncResultParamIndex) { CreateEventAsyncCompletedArgsTypeProperty(argsType, endMethod.Parameters[i].Type, endMethod.Parameters[i].Name, new CodeArrayIndexerExpression(resultsFieldReference, new CodePrimitiveExpression(count++))); } } // create the property for the return type if (endMethod.ReturnType.BaseType != voidTypeRef.BaseType) { CreateEventAsyncCompletedArgsTypeProperty( argsType, endMethod.ReturnType, NamingHelper.GetUniqueName("Result", DoesMemberNameExist, argsType), new CodeArrayIndexerExpression(resultsFieldReference, new CodePrimitiveExpression(count))); } // Name the "results" field after generating the properties to make sure it does // not conflict with the property names. resultsField.Name = NamingHelper.GetUniqueName("results", DoesMemberNameExist, argsType); resultsFieldReference.FieldName = resultsField.Name; argsType.Members.Add(resultsField); // Name the type making sure that it does not conflict with its members and types already present in // the namespace. argsType.Name = NamingHelper.GetUniqueName(GetOperationCompletedEventArgsTypeName(syncMethodName), DoesTypeAndMemberNameExist, new object[] { context.Namespace.Types, argsType }); context.Namespace.Types.Add(argsType); return argsType; } static int GetAsyncResultParamIndex(CodeMemberMethod endMethod) { int index = endMethod.Parameters.Count - 1; if (endMethod.Parameters[index].Type.BaseType != asyncResultTypeRef.BaseType) { // workaround for CSD Dev Framework:10826, the unwrapped end method has IAsyncResult as first param. index = 0; } return index; } static CodeMemberProperty CreateEventAsyncCompletedArgsTypeProperty(CodeTypeDeclaration ownerTypeDecl, CodeTypeReference propertyType, string propertyName, CodeExpression propertyValueExpr) { CodeMemberProperty property = new CodeMemberProperty(); property.Attributes = MemberAttributes.Public | MemberAttributes.Final; property.Type = propertyType; property.Name = propertyName; property.HasSet = false; property.HasGet = true; CodeCastExpression castExpr = new CodeCastExpression(propertyType, propertyValueExpr); CodeMethodReturnStatement returnStmt = new CodeMethodReturnStatement(castExpr); property.GetStatements.Add(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), raiseExceptionIfNecessaryMethodName)); property.GetStatements.Add(returnStmt); ownerTypeDecl.Members.Add(property); return property; } static CodeMemberEvent CreateOperationCompletedEvent(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName, CodeTypeDeclaration operationCompletedEventArgsType) { CodeMemberEvent operationCompletedEvent = new CodeMemberEvent(); operationCompletedEvent.Attributes = MemberAttributes.Public; operationCompletedEvent.Type = new CodeTypeReference(eventHandlerType); if (operationCompletedEventArgsType == null) { operationCompletedEvent.Type.TypeArguments.Add(asyncCompletedEventArgsType); } else { operationCompletedEvent.Type.TypeArguments.Add(operationCompletedEventArgsType.Name); } operationCompletedEvent.Name = NamingHelper.GetUniqueName(GetOperationCompletedEventName(syncMethodName), DoesMethodNameExist, context.Operations); clientType.Members.Add(operationCompletedEvent); return operationCompletedEvent; } static CodeMemberField CreateBeginOperationDelegate(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName) { CodeMemberField beginOperationDelegate = new CodeMemberField(); beginOperationDelegate.Attributes = MemberAttributes.Private; beginOperationDelegate.Type = new CodeTypeReference(beginOperationDelegateTypeName); beginOperationDelegate.Name = NamingHelper.GetUniqueName(GetBeginOperationDelegateName(syncMethodName), DoesMethodNameExist, context.Operations); clientType.Members.Add(beginOperationDelegate); return beginOperationDelegate; } static CodeMemberMethod CreateBeginOperationMethod(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName, CodeMemberMethod beginMethod) { CodeMemberMethod onBeginOperationMethod = new CodeMemberMethod(); onBeginOperationMethod.Attributes = MemberAttributes.Private; onBeginOperationMethod.ReturnType = new CodeTypeReference(asyncResultType); onBeginOperationMethod.Name = NamingHelper.GetUniqueName(GetBeginOperationMethodName(syncMethodName), DoesMethodNameExist, context.Operations); CodeParameterDeclarationExpression inValuesParam = new CodeParameterDeclarationExpression(); inValuesParam.Type = new CodeTypeReference(objectArrayType); inValuesParam.Name = NamingHelper.GetUniqueName("inValues", DoesParameterNameExist, beginMethod); onBeginOperationMethod.Parameters.Add(inValuesParam); CodeMethodInvokeExpression invokeBegin = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), beginMethod.Name); CodeExpression inValuesRef = new CodeVariableReferenceExpression(inValuesParam.Name); for (int i = 0; i < beginMethod.Parameters.Count - 2; i++) { CodeVariableDeclarationStatement variableDecl = new CodeVariableDeclarationStatement(); variableDecl.Type = beginMethod.Parameters[i].Type; variableDecl.Name = beginMethod.Parameters[i].Name; variableDecl.InitExpression = new CodeCastExpression(variableDecl.Type, new CodeArrayIndexerExpression(inValuesRef, new CodePrimitiveExpression(i))); onBeginOperationMethod.Statements.Add(variableDecl); invokeBegin.Parameters.Add(new CodeDirectionExpression(beginMethod.Parameters[i].Direction, new CodeVariableReferenceExpression(variableDecl.Name))); } for (int i = beginMethod.Parameters.Count - 2; i < beginMethod.Parameters.Count; i++) { onBeginOperationMethod.Parameters.Add(new CodeParameterDeclarationExpression( beginMethod.Parameters[i].Type, beginMethod.Parameters[i].Name)); invokeBegin.Parameters.Add(new CodeVariableReferenceExpression(beginMethod.Parameters[i].Name)); } onBeginOperationMethod.Statements.Add(new CodeMethodReturnStatement(invokeBegin)); clientType.Members.Add(onBeginOperationMethod); return onBeginOperationMethod; } static CodeMemberField CreateEndOperationDelegate(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName) { CodeMemberField endOperationDelegate = new CodeMemberField(); endOperationDelegate.Attributes = MemberAttributes.Private; endOperationDelegate.Type = new CodeTypeReference(endOperationDelegateTypeName); endOperationDelegate.Name = NamingHelper.GetUniqueName(GetEndOperationDelegateName(syncMethodName), DoesMethodNameExist, context.Operations); clientType.Members.Add(endOperationDelegate); return endOperationDelegate; } static CodeMemberMethod CreateEndOperationMethod(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName, CodeMemberMethod endMethod) { CodeMemberMethod onEndOperationMethod = new CodeMemberMethod(); onEndOperationMethod.Attributes = MemberAttributes.Private; onEndOperationMethod.ReturnType = new CodeTypeReference(objectArrayType); onEndOperationMethod.Name = NamingHelper.GetUniqueName(GetEndOperationMethodName(syncMethodName), DoesMethodNameExist, context.Operations); int asyncResultParamIndex = GetAsyncResultParamIndex(endMethod); CodeMethodInvokeExpression invokeEnd = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), endMethod.Name); CodeArrayCreateExpression retArray = new CodeArrayCreateExpression(); retArray.CreateType = new CodeTypeReference(objectArrayType); for (int i = 0; i < endMethod.Parameters.Count; i++) { if (i == asyncResultParamIndex) { onEndOperationMethod.Parameters.Add(new CodeParameterDeclarationExpression( endMethod.Parameters[i].Type, endMethod.Parameters[i].Name)); invokeEnd.Parameters.Add(new CodeVariableReferenceExpression(endMethod.Parameters[i].Name)); } else { CodeVariableDeclarationStatement variableDecl = new CodeVariableDeclarationStatement( endMethod.Parameters[i].Type, endMethod.Parameters[i].Name); CodeMethodReferenceExpression getDefaultValueMethodRef = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), getDefaultValueForInitializationMethodName, endMethod.Parameters[i].Type); variableDecl.InitExpression = new CodeMethodInvokeExpression(getDefaultValueMethodRef); onEndOperationMethod.Statements.Add(variableDecl); invokeEnd.Parameters.Add(new CodeDirectionExpression(endMethod.Parameters[i].Direction, new CodeVariableReferenceExpression(variableDecl.Name))); retArray.Initializers.Add(new CodeVariableReferenceExpression(variableDecl.Name)); } } if (endMethod.ReturnType.BaseType != voidTypeRef.BaseType) { CodeVariableDeclarationStatement retValDecl = new CodeVariableDeclarationStatement(); retValDecl.Type = endMethod.ReturnType; retValDecl.Name = NamingHelper.GetUniqueName("retVal", DoesParameterNameExist, endMethod); retValDecl.InitExpression = invokeEnd; retArray.Initializers.Add(new CodeVariableReferenceExpression(retValDecl.Name)); onEndOperationMethod.Statements.Add(retValDecl); } else { onEndOperationMethod.Statements.Add(invokeEnd); } if (retArray.Initializers.Count > 0) { onEndOperationMethod.Statements.Add(new CodeMethodReturnStatement(retArray)); } else { onEndOperationMethod.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(null))); } clientType.Members.Add(onEndOperationMethod); return onEndOperationMethod; } static CodeMemberField CreateOperationCompletedDelegate(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName) { CodeMemberField operationCompletedDelegate = new CodeMemberField(); operationCompletedDelegate.Attributes = MemberAttributes.Private; operationCompletedDelegate.Type = new CodeTypeReference(sendOrPostCallbackType); operationCompletedDelegate.Name = NamingHelper.GetUniqueName(GetOperationCompletedDelegateName(syncMethodName), DoesMethodNameExist, context.Operations); clientType.Members.Add(operationCompletedDelegate); return operationCompletedDelegate; } static CodeMemberMethod CreateOperationCompletedMethod(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName, CodeTypeDeclaration operationCompletedEventArgsType, CodeMemberEvent operationCompletedEvent) { CodeMemberMethod operationCompletedMethod = new CodeMemberMethod(); operationCompletedMethod.Attributes = MemberAttributes.Private; operationCompletedMethod.Name = NamingHelper.GetUniqueName(GetOperationCompletedMethodName(syncMethodName), DoesMethodNameExist, context.Operations); operationCompletedMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(objectType), "state")); operationCompletedMethod.ReturnType = new CodeTypeReference(voidType); CodeVariableDeclarationStatement eventArgsDecl = new CodeVariableDeclarationStatement(invokeAsyncCompletedEventArgsTypeName, "e"); eventArgsDecl.InitExpression = new CodeCastExpression(invokeAsyncCompletedEventArgsTypeName, new CodeArgumentReferenceExpression(operationCompletedMethod.Parameters[0].Name)); CodeObjectCreateExpression newEventArgsExpr; CodeVariableReferenceExpression eventArgsRef = new CodeVariableReferenceExpression(eventArgsDecl.Name); if (operationCompletedEventArgsType != null) { newEventArgsExpr = new CodeObjectCreateExpression(operationCompletedEventArgsType.Name, new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[0]), new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[1]), new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[2]), new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[3])); } else { newEventArgsExpr = new CodeObjectCreateExpression(asyncCompletedEventArgsType, new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[1]), new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[2]), new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[3])); } CodeEventReferenceExpression completedEvent = new CodeEventReferenceExpression(new CodeThisReferenceExpression(), operationCompletedEvent.Name); CodeDelegateInvokeExpression raiseEventExpr = new CodeDelegateInvokeExpression( completedEvent, new CodeThisReferenceExpression(), newEventArgsExpr); CodeConditionStatement ifEventHandlerNotNullBlock = new CodeConditionStatement( new CodeBinaryOperatorExpression( completedEvent, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null)), eventArgsDecl, new CodeExpressionStatement(raiseEventExpr)); operationCompletedMethod.Statements.Add(ifEventHandlerNotNullBlock); clientType.Members.Add(operationCompletedMethod); return operationCompletedMethod; } static CodeMemberMethod CreateEventAsyncMethod(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName, CodeMemberMethod beginMethod, CodeMemberField beginOperationDelegate, CodeMemberMethod beginOperationMethod, CodeMemberField endOperationDelegate, CodeMemberMethod endOperationMethod, CodeMemberField operationCompletedDelegate, CodeMemberMethod operationCompletedMethod) { CodeMemberMethod eventAsyncMethod = new CodeMemberMethod(); eventAsyncMethod.Name = NamingHelper.GetUniqueName(GetEventAsyncMethodName(syncMethodName), DoesMethodNameExist, context.Operations); eventAsyncMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; eventAsyncMethod.ReturnType = new CodeTypeReference(voidType); CodeArrayCreateExpression invokeAsyncInValues = new CodeArrayCreateExpression(new CodeTypeReference(objectArrayType)); for (int i = 0; i < beginMethod.Parameters.Count - 2; i++) { CodeParameterDeclarationExpression beginMethodParameter = beginMethod.Parameters[i]; CodeParameterDeclarationExpression eventAsyncMethodParameter = new CodeParameterDeclarationExpression( beginMethodParameter.Type, beginMethodParameter.Name); eventAsyncMethodParameter.Direction = FieldDirection.In; eventAsyncMethod.Parameters.Add(eventAsyncMethodParameter); invokeAsyncInValues.Initializers.Add(new CodeVariableReferenceExpression(eventAsyncMethodParameter.Name)); } string userStateParamName = NamingHelper.GetUniqueName("userState", DoesParameterNameExist, eventAsyncMethod); eventAsyncMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(objectType), userStateParamName)); eventAsyncMethod.Statements.Add(CreateDelegateIfNotNull(beginOperationDelegate, beginOperationMethod)); eventAsyncMethod.Statements.Add(CreateDelegateIfNotNull(endOperationDelegate, endOperationMethod)); eventAsyncMethod.Statements.Add(CreateDelegateIfNotNull(operationCompletedDelegate, operationCompletedMethod)); CodeMethodInvokeExpression invokeAsync = new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), invokeAsyncMethodName); invokeAsync.Parameters.Add(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), beginOperationDelegate.Name)); if (invokeAsyncInValues.Initializers.Count > 0) { invokeAsync.Parameters.Add(invokeAsyncInValues); } else { invokeAsync.Parameters.Add(new CodePrimitiveExpression(null)); } invokeAsync.Parameters.Add(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), endOperationDelegate.Name)); invokeAsync.Parameters.Add(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), operationCompletedDelegate.Name)); invokeAsync.Parameters.Add(new CodeVariableReferenceExpression(userStateParamName)); eventAsyncMethod.Statements.Add(new CodeExpressionStatement(invokeAsync)); clientType.Members.Add(eventAsyncMethod); return eventAsyncMethod; } static CodeMemberMethod CreateEventAsyncMethodOverload(CodeTypeDeclaration clientType, CodeMemberMethod eventAsyncMethod) { CodeMemberMethod eventAsyncMethodOverload = new CodeMemberMethod(); eventAsyncMethodOverload.Attributes = eventAsyncMethod.Attributes; eventAsyncMethodOverload.Name = eventAsyncMethod.Name; eventAsyncMethodOverload.ReturnType = eventAsyncMethod.ReturnType; CodeMethodInvokeExpression invokeEventAsyncMethod = new CodeMethodInvokeExpression( new CodeThisReferenceExpression(), eventAsyncMethod.Name); for (int i = 0; i < eventAsyncMethod.Parameters.Count - 1; i++) { eventAsyncMethodOverload.Parameters.Add(new CodeParameterDeclarationExpression( eventAsyncMethod.Parameters[i].Type, eventAsyncMethod.Parameters[i].Name)); invokeEventAsyncMethod.Parameters.Add(new CodeVariableReferenceExpression( eventAsyncMethod.Parameters[i].Name)); } invokeEventAsyncMethod.Parameters.Add(new CodePrimitiveExpression(null)); eventAsyncMethodOverload.Statements.Add(invokeEventAsyncMethod); int eventAsyncMethodPosition = clientType.Members.IndexOf(eventAsyncMethod); Fx.Assert(eventAsyncMethodPosition != -1, "The eventAsyncMethod must be added to the clientType before calling CreateEventAsyncMethodOverload"); clientType.Members.Insert(eventAsyncMethodPosition, eventAsyncMethodOverload); return eventAsyncMethodOverload; } static CodeStatement CreateDelegateIfNotNull(CodeMemberField delegateField, CodeMemberMethod delegateMethod) { return new CodeConditionStatement( new CodeBinaryOperatorExpression( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), delegateField.Name), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null)), new CodeAssignStatement( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), delegateField.Name), new CodeDelegateCreateExpression(delegateField.Type, new CodeThisReferenceExpression(), delegateMethod.Name))); } static string GetClassName(string interfaceName) { // maybe strip a leading 'I' if (interfaceName.Length >= 2 && String.Compare(interfaceName, 0, Strings.InterfaceTypePrefix, 0, Strings.InterfaceTypePrefix.Length, StringComparison.Ordinal) == 0 && Char.IsUpper(interfaceName, 1)) return interfaceName.Substring(1); else return interfaceName; } static string GetEventAsyncMethodName(string syncMethodName) { return string.Format(CultureInfo.InvariantCulture, "{0}Async", syncMethodName); } static string GetBeginOperationDelegateName(string syncMethodName) { return string.Format(CultureInfo.InvariantCulture, "onBegin{0}Delegate", syncMethodName); } static string GetBeginOperationMethodName(string syncMethodName) { return string.Format(CultureInfo.InvariantCulture, "OnBegin{0}", syncMethodName); } static string GetEndOperationDelegateName(string syncMethodName) { return string.Format(CultureInfo.InvariantCulture, "onEnd{0}Delegate", syncMethodName); } static string GetEndOperationMethodName(string syncMethodName) { return string.Format(CultureInfo.InvariantCulture, "OnEnd{0}", syncMethodName); } static string GetOperationCompletedDelegateName(string syncMethodName) { return string.Format(CultureInfo.InvariantCulture, "on{0}CompletedDelegate", syncMethodName); } static string GetOperationCompletedMethodName(string syncMethodName) { return string.Format(CultureInfo.InvariantCulture, "On{0}Completed", syncMethodName); } static string GetOperationCompletedEventName(string syncMethodName) { return string.Format(CultureInfo.InvariantCulture, "{0}Completed", syncMethodName); } static string GetOperationCompletedEventArgsTypeName(string syncMethodName) { return string.Format(CultureInfo.InvariantCulture, "{0}CompletedEventArgs", syncMethodName); } static internal string GetClientClassName(string interfaceName) { return GetClassName(interfaceName) + Strings.ClientTypeSuffix; } static bool IsVoid(CodeMemberMethod method) { return method.ReturnType == null || String.Compare(method.ReturnType.BaseType, typeof(void).FullName, StringComparison.Ordinal) == 0; } static CodeExpression GetChannelReference() { return new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), Strings.ClientBaseChannelProperty); } static class Strings { public const string ClientBaseChannelProperty = "Channel"; public const string ClientTypeSuffix = "Client"; public const string InterfaceTypePrefix = "I"; } } }