1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.ServiceModel.Description
9 using System.Collections.ObjectModel;
10 using System.ComponentModel;
11 using System.Globalization;
12 using System.Reflection;
14 using System.ServiceModel;
15 using System.ServiceModel.Channels;
16 using System.Threading;
18 class ClientClassGenerator : IServiceContractGenerationExtension
20 bool tryAddHelperMethod = false;
21 bool generateEventAsyncMethods = false;
23 internal ClientClassGenerator(bool tryAddHelperMethod)
24 : this(tryAddHelperMethod, false)
28 internal ClientClassGenerator(bool tryAddHelperMethod, bool generateEventAsyncMethods)
30 this.tryAddHelperMethod = tryAddHelperMethod;
31 this.generateEventAsyncMethods = generateEventAsyncMethods;
34 static Type clientBaseType = typeof(ClientBase<>);
35 static Type duplexClientBaseType = typeof(DuplexClientBase<>);
36 static Type instanceContextType = typeof(InstanceContext);
37 static Type objectType = typeof(object);
38 static Type objectArrayType = typeof(object[]);
39 static Type exceptionType = typeof(Exception);
40 static Type boolType = typeof(bool);
41 static Type stringType = typeof(string);
42 static Type endpointAddressType = typeof(EndpointAddress);
43 static Type uriType = typeof(Uri);
44 static Type bindingType = typeof(Binding);
45 static Type sendOrPostCallbackType = typeof(SendOrPostCallback);
46 static Type asyncCompletedEventArgsType = typeof(AsyncCompletedEventArgs);
47 static Type eventHandlerType = typeof(EventHandler<>);
48 static Type voidType = typeof(void);
49 static Type asyncResultType = typeof(IAsyncResult);
50 static Type asyncCallbackType = typeof(AsyncCallback);
52 static CodeTypeReference voidTypeRef = new CodeTypeReference(typeof(void));
53 static CodeTypeReference asyncResultTypeRef = new CodeTypeReference(typeof(IAsyncResult));
55 static string inputInstanceName = "callbackInstance";
56 static string invokeAsyncCompletedEventArgsTypeName = "InvokeAsyncCompletedEventArgs";
57 static string invokeAsyncMethodName = "InvokeAsync";
58 static string raiseExceptionIfNecessaryMethodName = "RaiseExceptionIfNecessary";
59 static string beginOperationDelegateTypeName = "BeginOperationDelegate";
60 static string endOperationDelegateTypeName = "EndOperationDelegate";
61 static string getDefaultValueForInitializationMethodName = "GetDefaultValueForInitialization";
63 // IMPORTANT: this table tracks the set of .ctors in ClientBase and DuplexClientBase.
64 // This table must be kept in sync
65 // for DuplexClientBase, the initial InstanceContext param is assumed; ctor overloads must match between ClientBase and DuplexClientBase
66 static Type[][] ClientCtorParamTypes = new Type[][]
69 new Type[] { stringType, },
70 new Type[] { stringType, stringType, },
71 new Type[] { stringType, endpointAddressType, },
72 new Type[] { bindingType, endpointAddressType, },
75 static string[][] ClientCtorParamNames = new string[][]
78 new string[] { "endpointConfigurationName", },
79 new string[] { "endpointConfigurationName", "remoteAddress", },
80 new string[] { "endpointConfigurationName", "remoteAddress", },
81 new string[] { "binding", "remoteAddress", },
84 static Type[] EventArgsCtorParamTypes = new Type[]
92 static string[] EventArgsCtorParamNames = new string[]
100 static string[] EventArgsPropertyNames = new string[]
109 static BindingFlags ctorBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
110 static string DebugCheckTable_errorString = "Client code generation table out of sync with ClientBase and DuplexClientBase ctors. Please investigate.";
112 // check the table against what we would get from reflection
113 static void DebugCheckTable()
115 Fx.Assert(ClientCtorParamNames.Length == ClientCtorParamTypes.Length, DebugCheckTable_errorString);
117 for (int i = 0; i < ClientCtorParamTypes.Length; i++)
119 DebugCheckTable_ValidateCtor(clientBaseType.GetConstructor(ctorBindingFlags, null, ClientCtorParamTypes[i], null), ClientCtorParamNames[i]);
121 Type[] duplexCtorTypes1 = DebugCheckTable_InsertAtStart(ClientCtorParamTypes[i], objectType);
122 Type[] duplexCtorTypes2 = DebugCheckTable_InsertAtStart(ClientCtorParamTypes[i], instanceContextType);
123 string[] duplexCtorNames = DebugCheckTable_InsertAtStart(ClientCtorParamNames[i], inputInstanceName);
125 DebugCheckTable_ValidateCtor(duplexClientBaseType.GetConstructor(ctorBindingFlags, null, duplexCtorTypes1, null), duplexCtorNames);
126 DebugCheckTable_ValidateCtor(duplexClientBaseType.GetConstructor(ctorBindingFlags, null, duplexCtorTypes2, null), duplexCtorNames);
129 // ClientBase<> has extra InstanceContext overloads that we do not call directly from the generated code, but which we
130 // need to account for in this assert
131 Fx.Assert(clientBaseType.GetConstructors(ctorBindingFlags).Length == ClientCtorParamTypes.Length * 2, DebugCheckTable_errorString);
133 // DuplexClientBase<> also has extra object/InstanceContext overloads (but we call these)
134 Fx.Assert(duplexClientBaseType.GetConstructors(ctorBindingFlags).Length == ClientCtorParamTypes.Length * 2, DebugCheckTable_errorString);
137 static T[] DebugCheckTable_InsertAtStart<T>(T[] arr, T item)
139 T[] newArr = new T[arr.Length + 1];
141 Array.Copy(arr, 0, newArr, 1, arr.Length);
145 static void DebugCheckTable_ValidateCtor(ConstructorInfo ctor, string[] paramNames)
147 Fx.Assert(ctor != null, DebugCheckTable_errorString);
149 ParameterInfo[] parameters = ctor.GetParameters();
150 Fx.Assert(parameters.Length == paramNames.Length, DebugCheckTable_errorString);
151 for (int i = 0; i < paramNames.Length; i++)
153 Fx.Assert(parameters[i].Name == paramNames[i], DebugCheckTable_errorString);
158 void IServiceContractGenerationExtension.GenerateContract(ServiceContractGenerationContext context)
161 // DebugCheckTable();
163 CodeTypeDeclaration clientType = context.TypeFactory.CreateClassType();
164 // 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)
165 clientType.Name = NamingHelper.GetUniqueName(GetClientClassName(context.ContractType.Name), DoesMethodNameExist, context.Operations);
166 CodeTypeReference contractTypeRef = context.ContractTypeReference;
167 if (context.DuplexCallbackType == null)
168 clientType.BaseTypes.Add(new CodeTypeReference(context.ServiceContractGenerator.GetCodeTypeReference(typeof(ClientBase<>)).BaseType, context.ContractTypeReference));
170 clientType.BaseTypes.Add(new CodeTypeReference(context.ServiceContractGenerator.GetCodeTypeReference(typeof(DuplexClientBase<>)).BaseType, context.ContractTypeReference));
172 clientType.BaseTypes.Add(context.ContractTypeReference);
174 if (!(ClientCtorParamNames.Length == ClientCtorParamTypes.Length))
176 Fx.Assert("Invalid client generation constructor table initialization");
177 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid client generation constructor table initialization")));
180 for (int i = 0; i < ClientCtorParamNames.Length; i++)
182 if (!(ClientCtorParamNames[i].Length == ClientCtorParamTypes[i].Length))
184 Fx.Assert("Invalid client generation constructor table initialization");
185 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid client generation constructor table initialization")));
188 CodeConstructor ctor = new CodeConstructor();
189 ctor.Attributes = MemberAttributes.Public;
190 if (context.DuplexCallbackType != null)
192 ctor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(InstanceContext), inputInstanceName));
193 ctor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(inputInstanceName));
195 for (int j = 0; j < ClientCtorParamNames[i].Length; j++)
197 ctor.Parameters.Add(new CodeParameterDeclarationExpression(ClientCtorParamTypes[i][j], ClientCtorParamNames[i][j]));
198 ctor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(ClientCtorParamNames[i][j]));
200 clientType.Members.Add(ctor);
203 foreach (OperationContractGenerationContext operationContext in context.Operations)
205 // Note that we generate all the client-side methods, even inherited ones.
206 if (operationContext.Operation.IsServerInitiated()) continue;
207 CodeTypeReference declaringContractTypeRef = operationContext.DeclaringTypeReference;
208 GenerateClientClassMethod(clientType, contractTypeRef, operationContext.SyncMethod, this.tryAddHelperMethod, declaringContractTypeRef);
210 if (operationContext.IsAsync)
212 CodeMemberMethod beginMethod = GenerateClientClassMethod(clientType, contractTypeRef, operationContext.BeginMethod, this.tryAddHelperMethod, declaringContractTypeRef);
213 CodeMemberMethod endMethod = GenerateClientClassMethod(clientType, contractTypeRef, operationContext.EndMethod, this.tryAddHelperMethod, declaringContractTypeRef);
215 if (this.generateEventAsyncMethods)
217 GenerateEventAsyncMethods(context, clientType, operationContext.SyncMethod.Name, beginMethod, endMethod);
221 if (operationContext.IsTask)
223 GenerateClientClassMethod(clientType, contractTypeRef, operationContext.TaskMethod, !operationContext.Operation.HasOutputParameters && this.tryAddHelperMethod, declaringContractTypeRef);
227 context.Namespace.Types.Add(clientType);
228 context.ClientType = clientType;
229 context.ClientTypeReference = ServiceContractGenerator.NamespaceHelper.GetCodeTypeReference(context.Namespace, clientType);
232 static CodeMemberMethod GenerateClientClassMethod(CodeTypeDeclaration clientType, CodeTypeReference contractTypeRef, CodeMemberMethod method, bool addHelperMethod, CodeTypeReference declaringContractTypeRef)
234 CodeMemberMethod methodImpl = GetImplementationOfMethod(contractTypeRef, method);
235 AddMethodImpl(methodImpl);
236 int methodPosition = clientType.Members.Add(methodImpl);
237 CodeMemberMethod helperMethod = null;
241 helperMethod = GenerateHelperMethod(declaringContractTypeRef, methodImpl);
242 if (helperMethod != null)
244 clientType.Members[methodPosition].CustomAttributes.Add(CreateEditorBrowsableAttribute(EditorBrowsableState.Advanced));
245 clientType.Members.Add(helperMethod);
249 return (helperMethod != null) ? helperMethod : methodImpl;
252 internal static CodeAttributeDeclaration CreateEditorBrowsableAttribute(EditorBrowsableState editorBrowsableState)
254 CodeAttributeDeclaration browsableAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(EditorBrowsableAttribute)));
255 CodeTypeReferenceExpression browsableAttributeState = new CodeTypeReferenceExpression(typeof(EditorBrowsableState));
256 CodeAttributeArgument browsableAttributeValue = new CodeAttributeArgument(new CodeFieldReferenceExpression(browsableAttributeState, editorBrowsableState.ToString()));
257 browsableAttribute.Arguments.Add(browsableAttributeValue);
259 return browsableAttribute;
262 private static CodeMemberMethod GenerateHelperMethod(CodeTypeReference ifaceType, CodeMemberMethod method)
264 CodeMemberMethod helperMethod = new CodeMemberMethod();
265 helperMethod.Name = method.Name;
266 helperMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
267 CodeMethodInvokeExpression invokeMethod = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeCastExpression(ifaceType, new CodeThisReferenceExpression()), method.Name));
268 bool hasTypedMessage = false;
269 foreach (CodeParameterDeclarationExpression param in method.Parameters)
271 CodeTypeDeclaration paramTypeDecl = ServiceContractGenerator.NamespaceHelper.GetCodeType(param.Type);
272 if (paramTypeDecl != null)
274 hasTypedMessage = true;
275 CodeVariableReferenceExpression inValue = new CodeVariableReferenceExpression("inValue");
276 helperMethod.Statements.Add(new CodeVariableDeclarationStatement(param.Type, inValue.VariableName, new CodeObjectCreateExpression(param.Type)));
277 invokeMethod.Parameters.Add(inValue);
278 GenerateParameters(helperMethod, paramTypeDecl, inValue, FieldDirection.In);
282 helperMethod.Parameters.Add(new CodeParameterDeclarationExpression(param.Type, param.Name));
283 invokeMethod.Parameters.Add(new CodeArgumentReferenceExpression(param.Name));
286 if (method.ReturnType.BaseType == voidTypeRef.BaseType)
287 helperMethod.Statements.Add(invokeMethod);
290 CodeTypeDeclaration returnTypeDecl = ServiceContractGenerator.NamespaceHelper.GetCodeType(method.ReturnType);
291 if (returnTypeDecl != null)
293 hasTypedMessage = true;
294 CodeVariableReferenceExpression outVar = new CodeVariableReferenceExpression("retVal");
296 helperMethod.Statements.Add(new CodeVariableDeclarationStatement(method.ReturnType, outVar.VariableName, invokeMethod));
297 CodeMethodReturnStatement returnStatement = GenerateParameters(helperMethod, returnTypeDecl, outVar, FieldDirection.Out);
298 if (returnStatement != null)
299 helperMethod.Statements.Add(returnStatement);
303 helperMethod.Statements.Add(new CodeMethodReturnStatement(invokeMethod));
304 helperMethod.ReturnType = method.ReturnType;
308 method.PrivateImplementationType = ifaceType;
309 return hasTypedMessage ? helperMethod : null;
312 private static CodeMethodReturnStatement GenerateParameters(CodeMemberMethod helperMethod, CodeTypeDeclaration codeTypeDeclaration, CodeExpression target, FieldDirection dir)
314 CodeMethodReturnStatement returnStatement = null;
315 foreach (CodeTypeMember member in codeTypeDeclaration.Members)
317 CodeMemberField field = member as CodeMemberField;
320 CodeFieldReferenceExpression fieldRef = new CodeFieldReferenceExpression(target, field.Name);
321 CodeTypeDeclaration bodyTypeDecl = ServiceContractGenerator.NamespaceHelper.GetCodeType(field.Type);
322 if (bodyTypeDecl != null)
324 if (dir == FieldDirection.In)
325 helperMethod.Statements.Add(new CodeAssignStatement(fieldRef, new CodeObjectCreateExpression(field.Type)));
326 returnStatement = GenerateParameters(helperMethod, bodyTypeDecl, fieldRef, dir);
329 CodeParameterDeclarationExpression param = GetRefParameter(helperMethod.Parameters, dir, field);
330 if (param == null && dir == FieldDirection.Out && helperMethod.ReturnType.BaseType == voidTypeRef.BaseType)
332 helperMethod.ReturnType = field.Type;
333 returnStatement = new CodeMethodReturnStatement(fieldRef);
339 param = new CodeParameterDeclarationExpression(field.Type, NamingHelper.GetUniqueName(field.Name, DoesParameterNameExist, helperMethod));
340 param.Direction = dir;
341 helperMethod.Parameters.Add(param);
343 if (dir == FieldDirection.Out)
344 helperMethod.Statements.Add(new CodeAssignStatement(new CodeArgumentReferenceExpression(param.Name), fieldRef));
346 helperMethod.Statements.Add(new CodeAssignStatement(fieldRef, new CodeArgumentReferenceExpression(param.Name)));
349 return returnStatement;
352 private static CodeParameterDeclarationExpression GetRefParameter(CodeParameterDeclarationExpressionCollection parameters, FieldDirection dir, CodeMemberField field)
354 foreach (CodeParameterDeclarationExpression p in parameters)
356 if (p.Name == field.Name)
358 if (p.Direction != dir && p.Type.BaseType == field.Type.BaseType)
360 p.Direction = FieldDirection.Ref;
369 internal static bool DoesMemberNameExist(string name, object typeDeclarationObject)
371 CodeTypeDeclaration typeDeclaration = (CodeTypeDeclaration)typeDeclarationObject;
373 if (string.Compare(typeDeclaration.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
378 foreach (CodeTypeMember member in typeDeclaration.Members)
380 if (string.Compare(member.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
389 internal static bool DoesTypeNameExists(string name, object codeTypeDeclarationCollectionObject)
391 CodeTypeDeclarationCollection codeTypeDeclarations = (CodeTypeDeclarationCollection)codeTypeDeclarationCollectionObject;
392 foreach (CodeTypeDeclaration codeTypeDeclaration in codeTypeDeclarations)
394 if (string.Compare(codeTypeDeclaration.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
403 internal static bool DoesTypeAndMemberNameExist(string name, object nameCollection)
405 object[] nameCollections = (object[])nameCollection;
407 if (DoesTypeNameExists(name, nameCollections[0]))
411 if (DoesMemberNameExist(name, nameCollections[1]))
419 internal static bool DoesMethodNameExist(string name, object operationsObject)
421 Collection<OperationContractGenerationContext> operations = (Collection<OperationContractGenerationContext>)operationsObject;
422 foreach (OperationContractGenerationContext operationContext in operations)
424 if (String.Compare(operationContext.SyncMethod.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
426 if (operationContext.IsAsync)
428 if (String.Compare(operationContext.BeginMethod.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
430 if (String.Compare(operationContext.EndMethod.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
433 if (operationContext.IsTask)
435 if (String.Compare(operationContext.TaskMethod.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
442 internal static bool DoesParameterNameExist(string name, object methodObject)
444 CodeMemberMethod method = (CodeMemberMethod)methodObject;
445 if (String.Compare(method.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
447 CodeParameterDeclarationExpressionCollection parameters = method.Parameters;
448 foreach (CodeParameterDeclarationExpression p in parameters)
450 if (String.Compare(p.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
456 static void AddMethodImpl(CodeMemberMethod method)
458 CodeMethodInvokeExpression methodInvoke = new CodeMethodInvokeExpression(GetChannelReference(), method.Name);
459 foreach (CodeParameterDeclarationExpression parameter in method.Parameters)
461 methodInvoke.Parameters.Add(new CodeDirectionExpression(parameter.Direction, new CodeVariableReferenceExpression(parameter.Name)));
464 method.Statements.Add(methodInvoke);
466 method.Statements.Add(new CodeMethodReturnStatement(methodInvoke));
469 static CodeMemberMethod GetImplementationOfMethod(CodeTypeReference ifaceType, CodeMemberMethod method)
471 CodeMemberMethod m = new CodeMemberMethod();
472 m.Name = method.Name;
473 m.ImplementationTypes.Add(ifaceType);
474 m.Attributes = MemberAttributes.Public | MemberAttributes.Final;
475 foreach (CodeParameterDeclarationExpression parameter in method.Parameters)
477 CodeParameterDeclarationExpression newParam = new CodeParameterDeclarationExpression(parameter.Type, parameter.Name);
478 newParam.Direction = parameter.Direction;
479 m.Parameters.Add(newParam);
481 m.ReturnType = method.ReturnType;
485 static void GenerateEventAsyncMethods(ServiceContractGenerationContext context, CodeTypeDeclaration clientType,
486 string syncMethodName, CodeMemberMethod beginMethod, CodeMemberMethod endMethod)
488 CodeTypeDeclaration operationCompletedEventArgsType = CreateOperationCompletedEventArgsType(context, syncMethodName, endMethod);
489 CodeMemberEvent operationCompletedEvent = CreateOperationCompletedEvent(context, clientType, syncMethodName, operationCompletedEventArgsType);
491 CodeMemberField beginOperationDelegate = CreateBeginOperationDelegate(context, clientType, syncMethodName);
492 CodeMemberMethod beginOperationMethod = CreateBeginOperationMethod(context, clientType, syncMethodName, beginMethod);
494 CodeMemberField endOperationDelegate = CreateEndOperationDelegate(context, clientType, syncMethodName);
495 CodeMemberMethod endOperationMethod = CreateEndOperationMethod(context, clientType, syncMethodName, endMethod);
497 CodeMemberField operationCompletedDelegate = CreateOperationCompletedDelegate(context, clientType, syncMethodName);
498 CodeMemberMethod operationCompletedMethod = CreateOperationCompletedMethod(context, clientType, syncMethodName, operationCompletedEventArgsType, operationCompletedEvent);
500 CodeMemberMethod eventAsyncMethod = CreateEventAsyncMethod(context, clientType, syncMethodName, beginMethod,
501 beginOperationDelegate, beginOperationMethod, endOperationDelegate, endOperationMethod, operationCompletedDelegate, operationCompletedMethod);
503 CreateEventAsyncMethodOverload(clientType, eventAsyncMethod);
505 // hide the normal async methods from intellisense
506 beginMethod.CustomAttributes.Add(CreateEditorBrowsableAttribute(EditorBrowsableState.Advanced));
507 endMethod.CustomAttributes.Add(CreateEditorBrowsableAttribute(EditorBrowsableState.Advanced));
510 static CodeTypeDeclaration CreateOperationCompletedEventArgsType(ServiceContractGenerationContext context,
511 string syncMethodName, CodeMemberMethod endMethod)
513 if ((endMethod.Parameters.Count == 1) && (endMethod.ReturnType.BaseType == voidTypeRef.BaseType))
515 // no need to create new event args type, use AsyncCompletedEventArgs
519 CodeTypeDeclaration argsType = context.TypeFactory.CreateClassType();
520 argsType.BaseTypes.Add(new CodeTypeReference(asyncCompletedEventArgsType));
522 // define object[] results field.
523 CodeMemberField resultsField = new CodeMemberField();
524 resultsField.Type = new CodeTypeReference(objectArrayType);
526 CodeFieldReferenceExpression resultsFieldReference = new CodeFieldReferenceExpression();
527 resultsFieldReference.TargetObject = new CodeThisReferenceExpression();
529 // create constructor, that assigns the results field.
530 CodeConstructor ctor = new CodeConstructor();
531 ctor.Attributes = MemberAttributes.Public;
532 for (int i = 0; i < EventArgsCtorParamTypes.Length; i++)
534 ctor.Parameters.Add(new CodeParameterDeclarationExpression(EventArgsCtorParamTypes[i], EventArgsCtorParamNames[i]));
537 ctor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(EventArgsCtorParamNames[i]));
540 argsType.Members.Add(ctor);
541 ctor.Statements.Add(new CodeAssignStatement(resultsFieldReference, new CodeVariableReferenceExpression(EventArgsCtorParamNames[0])));
543 // create properties for the out parameters
544 int asyncResultParamIndex = GetAsyncResultParamIndex(endMethod);
546 for (int i = 0; i < endMethod.Parameters.Count; i++)
548 if (i != asyncResultParamIndex)
550 CreateEventAsyncCompletedArgsTypeProperty(argsType,
551 endMethod.Parameters[i].Type,
552 endMethod.Parameters[i].Name,
553 new CodeArrayIndexerExpression(resultsFieldReference, new CodePrimitiveExpression(count++)));
557 // create the property for the return type
558 if (endMethod.ReturnType.BaseType != voidTypeRef.BaseType)
560 CreateEventAsyncCompletedArgsTypeProperty(
562 endMethod.ReturnType,
563 NamingHelper.GetUniqueName("Result", DoesMemberNameExist, argsType),
564 new CodeArrayIndexerExpression(resultsFieldReference,
565 new CodePrimitiveExpression(count)));
569 // Name the "results" field after generating the properties to make sure it does
570 // not conflict with the property names.
571 resultsField.Name = NamingHelper.GetUniqueName("results", DoesMemberNameExist, argsType);
572 resultsFieldReference.FieldName = resultsField.Name;
573 argsType.Members.Add(resultsField);
575 // Name the type making sure that it does not conflict with its members and types already present in
577 argsType.Name = NamingHelper.GetUniqueName(GetOperationCompletedEventArgsTypeName(syncMethodName),
578 DoesTypeAndMemberNameExist, new object[] { context.Namespace.Types, argsType });
579 context.Namespace.Types.Add(argsType);
584 static int GetAsyncResultParamIndex(CodeMemberMethod endMethod)
586 int index = endMethod.Parameters.Count - 1;
587 if (endMethod.Parameters[index].Type.BaseType != asyncResultTypeRef.BaseType)
589 // workaround for CSD Dev Framework:10826, the unwrapped end method has IAsyncResult as first param.
596 static CodeMemberProperty CreateEventAsyncCompletedArgsTypeProperty(CodeTypeDeclaration ownerTypeDecl,
597 CodeTypeReference propertyType, string propertyName, CodeExpression propertyValueExpr)
599 CodeMemberProperty property = new CodeMemberProperty();
600 property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
601 property.Type = propertyType;
602 property.Name = propertyName;
603 property.HasSet = false;
604 property.HasGet = true;
606 CodeCastExpression castExpr = new CodeCastExpression(propertyType, propertyValueExpr);
607 CodeMethodReturnStatement returnStmt = new CodeMethodReturnStatement(castExpr);
609 property.GetStatements.Add(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), raiseExceptionIfNecessaryMethodName));
610 property.GetStatements.Add(returnStmt);
611 ownerTypeDecl.Members.Add(property);
616 static CodeMemberEvent CreateOperationCompletedEvent(ServiceContractGenerationContext context,
617 CodeTypeDeclaration clientType, string syncMethodName, CodeTypeDeclaration operationCompletedEventArgsType)
619 CodeMemberEvent operationCompletedEvent = new CodeMemberEvent();
620 operationCompletedEvent.Attributes = MemberAttributes.Public;
621 operationCompletedEvent.Type = new CodeTypeReference(eventHandlerType);
623 if (operationCompletedEventArgsType == null)
625 operationCompletedEvent.Type.TypeArguments.Add(asyncCompletedEventArgsType);
629 operationCompletedEvent.Type.TypeArguments.Add(operationCompletedEventArgsType.Name);
632 operationCompletedEvent.Name = NamingHelper.GetUniqueName(GetOperationCompletedEventName(syncMethodName),
633 DoesMethodNameExist, context.Operations);
635 clientType.Members.Add(operationCompletedEvent);
636 return operationCompletedEvent;
639 static CodeMemberField CreateBeginOperationDelegate(ServiceContractGenerationContext context,
640 CodeTypeDeclaration clientType, string syncMethodName)
642 CodeMemberField beginOperationDelegate = new CodeMemberField();
643 beginOperationDelegate.Attributes = MemberAttributes.Private;
644 beginOperationDelegate.Type = new CodeTypeReference(beginOperationDelegateTypeName);
645 beginOperationDelegate.Name = NamingHelper.GetUniqueName(GetBeginOperationDelegateName(syncMethodName),
646 DoesMethodNameExist, context.Operations);
648 clientType.Members.Add(beginOperationDelegate);
649 return beginOperationDelegate;
652 static CodeMemberMethod CreateBeginOperationMethod(ServiceContractGenerationContext context, CodeTypeDeclaration clientType,
653 string syncMethodName, CodeMemberMethod beginMethod)
655 CodeMemberMethod onBeginOperationMethod = new CodeMemberMethod();
656 onBeginOperationMethod.Attributes = MemberAttributes.Private;
657 onBeginOperationMethod.ReturnType = new CodeTypeReference(asyncResultType);
658 onBeginOperationMethod.Name = NamingHelper.GetUniqueName(GetBeginOperationMethodName(syncMethodName),
659 DoesMethodNameExist, context.Operations);
661 CodeParameterDeclarationExpression inValuesParam = new CodeParameterDeclarationExpression();
662 inValuesParam.Type = new CodeTypeReference(objectArrayType);
663 inValuesParam.Name = NamingHelper.GetUniqueName("inValues", DoesParameterNameExist, beginMethod);
664 onBeginOperationMethod.Parameters.Add(inValuesParam);
666 CodeMethodInvokeExpression invokeBegin = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), beginMethod.Name);
667 CodeExpression inValuesRef = new CodeVariableReferenceExpression(inValuesParam.Name);
669 for (int i = 0; i < beginMethod.Parameters.Count - 2; i++)
671 CodeVariableDeclarationStatement variableDecl = new CodeVariableDeclarationStatement();
672 variableDecl.Type = beginMethod.Parameters[i].Type;
673 variableDecl.Name = beginMethod.Parameters[i].Name;
674 variableDecl.InitExpression = new CodeCastExpression(variableDecl.Type,
675 new CodeArrayIndexerExpression(inValuesRef, new CodePrimitiveExpression(i)));
677 onBeginOperationMethod.Statements.Add(variableDecl);
678 invokeBegin.Parameters.Add(new CodeDirectionExpression(beginMethod.Parameters[i].Direction,
679 new CodeVariableReferenceExpression(variableDecl.Name)));
682 for (int i = beginMethod.Parameters.Count - 2; i < beginMethod.Parameters.Count; i++)
684 onBeginOperationMethod.Parameters.Add(new CodeParameterDeclarationExpression(
685 beginMethod.Parameters[i].Type, beginMethod.Parameters[i].Name));
686 invokeBegin.Parameters.Add(new CodeVariableReferenceExpression(beginMethod.Parameters[i].Name));
689 onBeginOperationMethod.Statements.Add(new CodeMethodReturnStatement(invokeBegin));
690 clientType.Members.Add(onBeginOperationMethod);
691 return onBeginOperationMethod;
694 static CodeMemberField CreateEndOperationDelegate(ServiceContractGenerationContext context,
695 CodeTypeDeclaration clientType, string syncMethodName)
697 CodeMemberField endOperationDelegate = new CodeMemberField();
698 endOperationDelegate.Attributes = MemberAttributes.Private;
699 endOperationDelegate.Type = new CodeTypeReference(endOperationDelegateTypeName);
700 endOperationDelegate.Name = NamingHelper.GetUniqueName(GetEndOperationDelegateName(syncMethodName),
701 DoesMethodNameExist, context.Operations);
703 clientType.Members.Add(endOperationDelegate);
704 return endOperationDelegate;
707 static CodeMemberMethod CreateEndOperationMethod(ServiceContractGenerationContext context, CodeTypeDeclaration clientType, string syncMethodName, CodeMemberMethod endMethod)
709 CodeMemberMethod onEndOperationMethod = new CodeMemberMethod();
710 onEndOperationMethod.Attributes = MemberAttributes.Private;
711 onEndOperationMethod.ReturnType = new CodeTypeReference(objectArrayType);
712 onEndOperationMethod.Name = NamingHelper.GetUniqueName(GetEndOperationMethodName(syncMethodName), DoesMethodNameExist, context.Operations);
714 int asyncResultParamIndex = GetAsyncResultParamIndex(endMethod);
715 CodeMethodInvokeExpression invokeEnd = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), endMethod.Name);
716 CodeArrayCreateExpression retArray = new CodeArrayCreateExpression();
717 retArray.CreateType = new CodeTypeReference(objectArrayType);
718 for (int i = 0; i < endMethod.Parameters.Count; i++)
720 if (i == asyncResultParamIndex)
722 onEndOperationMethod.Parameters.Add(new CodeParameterDeclarationExpression(
723 endMethod.Parameters[i].Type, endMethod.Parameters[i].Name));
724 invokeEnd.Parameters.Add(new CodeVariableReferenceExpression(endMethod.Parameters[i].Name));
728 CodeVariableDeclarationStatement variableDecl = new CodeVariableDeclarationStatement(
729 endMethod.Parameters[i].Type, endMethod.Parameters[i].Name);
730 CodeMethodReferenceExpression getDefaultValueMethodRef = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), getDefaultValueForInitializationMethodName, endMethod.Parameters[i].Type);
731 variableDecl.InitExpression = new CodeMethodInvokeExpression(getDefaultValueMethodRef);
732 onEndOperationMethod.Statements.Add(variableDecl);
734 invokeEnd.Parameters.Add(new CodeDirectionExpression(endMethod.Parameters[i].Direction,
735 new CodeVariableReferenceExpression(variableDecl.Name)));
737 retArray.Initializers.Add(new CodeVariableReferenceExpression(variableDecl.Name));
741 if (endMethod.ReturnType.BaseType != voidTypeRef.BaseType)
743 CodeVariableDeclarationStatement retValDecl = new CodeVariableDeclarationStatement();
744 retValDecl.Type = endMethod.ReturnType;
745 retValDecl.Name = NamingHelper.GetUniqueName("retVal", DoesParameterNameExist, endMethod);
746 retValDecl.InitExpression = invokeEnd;
747 retArray.Initializers.Add(new CodeVariableReferenceExpression(retValDecl.Name));
749 onEndOperationMethod.Statements.Add(retValDecl);
753 onEndOperationMethod.Statements.Add(invokeEnd);
756 if (retArray.Initializers.Count > 0)
758 onEndOperationMethod.Statements.Add(new CodeMethodReturnStatement(retArray));
762 onEndOperationMethod.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(null)));
765 clientType.Members.Add(onEndOperationMethod);
766 return onEndOperationMethod;
769 static CodeMemberField CreateOperationCompletedDelegate(ServiceContractGenerationContext context,
770 CodeTypeDeclaration clientType, string syncMethodName)
772 CodeMemberField operationCompletedDelegate = new CodeMemberField();
773 operationCompletedDelegate.Attributes = MemberAttributes.Private;
774 operationCompletedDelegate.Type = new CodeTypeReference(sendOrPostCallbackType);
775 operationCompletedDelegate.Name = NamingHelper.GetUniqueName(GetOperationCompletedDelegateName(syncMethodName),
776 DoesMethodNameExist, context.Operations);
778 clientType.Members.Add(operationCompletedDelegate);
779 return operationCompletedDelegate;
782 static CodeMemberMethod CreateOperationCompletedMethod(ServiceContractGenerationContext context, CodeTypeDeclaration clientType,
783 string syncMethodName, CodeTypeDeclaration operationCompletedEventArgsType, CodeMemberEvent operationCompletedEvent)
785 CodeMemberMethod operationCompletedMethod = new CodeMemberMethod();
786 operationCompletedMethod.Attributes = MemberAttributes.Private;
787 operationCompletedMethod.Name = NamingHelper.GetUniqueName(GetOperationCompletedMethodName(syncMethodName),
788 DoesMethodNameExist, context.Operations);
790 operationCompletedMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(objectType), "state"));
791 operationCompletedMethod.ReturnType = new CodeTypeReference(voidType);
793 CodeVariableDeclarationStatement eventArgsDecl =
794 new CodeVariableDeclarationStatement(invokeAsyncCompletedEventArgsTypeName, "e");
796 eventArgsDecl.InitExpression = new CodeCastExpression(invokeAsyncCompletedEventArgsTypeName,
797 new CodeArgumentReferenceExpression(operationCompletedMethod.Parameters[0].Name));
799 CodeObjectCreateExpression newEventArgsExpr;
800 CodeVariableReferenceExpression eventArgsRef = new CodeVariableReferenceExpression(eventArgsDecl.Name);
801 if (operationCompletedEventArgsType != null)
803 newEventArgsExpr = new CodeObjectCreateExpression(operationCompletedEventArgsType.Name,
804 new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[0]),
805 new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[1]),
806 new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[2]),
807 new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[3]));
811 newEventArgsExpr = new CodeObjectCreateExpression(asyncCompletedEventArgsType,
812 new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[1]),
813 new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[2]),
814 new CodePropertyReferenceExpression(eventArgsRef, EventArgsPropertyNames[3]));
817 CodeEventReferenceExpression completedEvent = new CodeEventReferenceExpression(new CodeThisReferenceExpression(), operationCompletedEvent.Name);
819 CodeDelegateInvokeExpression raiseEventExpr = new CodeDelegateInvokeExpression(
821 new CodeThisReferenceExpression(),
824 CodeConditionStatement ifEventHandlerNotNullBlock = new CodeConditionStatement(
825 new CodeBinaryOperatorExpression(
827 CodeBinaryOperatorType.IdentityInequality,
828 new CodePrimitiveExpression(null)),
830 new CodeExpressionStatement(raiseEventExpr));
832 operationCompletedMethod.Statements.Add(ifEventHandlerNotNullBlock);
834 clientType.Members.Add(operationCompletedMethod);
835 return operationCompletedMethod;
838 static CodeMemberMethod CreateEventAsyncMethod(ServiceContractGenerationContext context, CodeTypeDeclaration clientType,
839 string syncMethodName, CodeMemberMethod beginMethod,
840 CodeMemberField beginOperationDelegate, CodeMemberMethod beginOperationMethod,
841 CodeMemberField endOperationDelegate, CodeMemberMethod endOperationMethod,
842 CodeMemberField operationCompletedDelegate, CodeMemberMethod operationCompletedMethod)
844 CodeMemberMethod eventAsyncMethod = new CodeMemberMethod();
845 eventAsyncMethod.Name = NamingHelper.GetUniqueName(GetEventAsyncMethodName(syncMethodName),
846 DoesMethodNameExist, context.Operations);
847 eventAsyncMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
848 eventAsyncMethod.ReturnType = new CodeTypeReference(voidType);
850 CodeArrayCreateExpression invokeAsyncInValues = new CodeArrayCreateExpression(new CodeTypeReference(objectArrayType));
851 for (int i = 0; i < beginMethod.Parameters.Count - 2; i++)
853 CodeParameterDeclarationExpression beginMethodParameter = beginMethod.Parameters[i];
854 CodeParameterDeclarationExpression eventAsyncMethodParameter = new CodeParameterDeclarationExpression(
855 beginMethodParameter.Type, beginMethodParameter.Name);
857 eventAsyncMethodParameter.Direction = FieldDirection.In;
858 eventAsyncMethod.Parameters.Add(eventAsyncMethodParameter);
859 invokeAsyncInValues.Initializers.Add(new CodeVariableReferenceExpression(eventAsyncMethodParameter.Name));
862 string userStateParamName = NamingHelper.GetUniqueName("userState", DoesParameterNameExist, eventAsyncMethod);
863 eventAsyncMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(objectType), userStateParamName));
865 eventAsyncMethod.Statements.Add(CreateDelegateIfNotNull(beginOperationDelegate, beginOperationMethod));
866 eventAsyncMethod.Statements.Add(CreateDelegateIfNotNull(endOperationDelegate, endOperationMethod));
867 eventAsyncMethod.Statements.Add(CreateDelegateIfNotNull(operationCompletedDelegate, operationCompletedMethod));
869 CodeMethodInvokeExpression invokeAsync = new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), invokeAsyncMethodName);
870 invokeAsync.Parameters.Add(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), beginOperationDelegate.Name));
871 if (invokeAsyncInValues.Initializers.Count > 0)
873 invokeAsync.Parameters.Add(invokeAsyncInValues);
877 invokeAsync.Parameters.Add(new CodePrimitiveExpression(null));
879 invokeAsync.Parameters.Add(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), endOperationDelegate.Name));
880 invokeAsync.Parameters.Add(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), operationCompletedDelegate.Name));
881 invokeAsync.Parameters.Add(new CodeVariableReferenceExpression(userStateParamName));
883 eventAsyncMethod.Statements.Add(new CodeExpressionStatement(invokeAsync));
885 clientType.Members.Add(eventAsyncMethod);
886 return eventAsyncMethod;
889 static CodeMemberMethod CreateEventAsyncMethodOverload(CodeTypeDeclaration clientType, CodeMemberMethod eventAsyncMethod)
891 CodeMemberMethod eventAsyncMethodOverload = new CodeMemberMethod();
892 eventAsyncMethodOverload.Attributes = eventAsyncMethod.Attributes;
893 eventAsyncMethodOverload.Name = eventAsyncMethod.Name;
894 eventAsyncMethodOverload.ReturnType = eventAsyncMethod.ReturnType;
896 CodeMethodInvokeExpression invokeEventAsyncMethod = new CodeMethodInvokeExpression(
897 new CodeThisReferenceExpression(), eventAsyncMethod.Name);
899 for (int i = 0; i < eventAsyncMethod.Parameters.Count - 1; i++)
901 eventAsyncMethodOverload.Parameters.Add(new CodeParameterDeclarationExpression(
902 eventAsyncMethod.Parameters[i].Type,
903 eventAsyncMethod.Parameters[i].Name));
905 invokeEventAsyncMethod.Parameters.Add(new CodeVariableReferenceExpression(
906 eventAsyncMethod.Parameters[i].Name));
908 invokeEventAsyncMethod.Parameters.Add(new CodePrimitiveExpression(null));
910 eventAsyncMethodOverload.Statements.Add(invokeEventAsyncMethod);
912 int eventAsyncMethodPosition = clientType.Members.IndexOf(eventAsyncMethod);
913 Fx.Assert(eventAsyncMethodPosition != -1,
914 "The eventAsyncMethod must be added to the clientType before calling CreateEventAsyncMethodOverload");
916 clientType.Members.Insert(eventAsyncMethodPosition, eventAsyncMethodOverload);
917 return eventAsyncMethodOverload;
920 static CodeStatement CreateDelegateIfNotNull(CodeMemberField delegateField, CodeMemberMethod delegateMethod)
922 return new CodeConditionStatement(
923 new CodeBinaryOperatorExpression(
924 new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), delegateField.Name),
925 CodeBinaryOperatorType.IdentityEquality,
926 new CodePrimitiveExpression(null)),
927 new CodeAssignStatement(
928 new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), delegateField.Name),
929 new CodeDelegateCreateExpression(delegateField.Type,
930 new CodeThisReferenceExpression(), delegateMethod.Name)));
933 static string GetClassName(string interfaceName)
935 // maybe strip a leading 'I'
936 if (interfaceName.Length >= 2 &&
937 String.Compare(interfaceName, 0, Strings.InterfaceTypePrefix, 0, Strings.InterfaceTypePrefix.Length, StringComparison.Ordinal) == 0 &&
938 Char.IsUpper(interfaceName, 1))
939 return interfaceName.Substring(1);
941 return interfaceName;
944 static string GetEventAsyncMethodName(string syncMethodName)
946 return string.Format(CultureInfo.InvariantCulture, "{0}Async", syncMethodName);
949 static string GetBeginOperationDelegateName(string syncMethodName)
951 return string.Format(CultureInfo.InvariantCulture, "onBegin{0}Delegate", syncMethodName);
954 static string GetBeginOperationMethodName(string syncMethodName)
956 return string.Format(CultureInfo.InvariantCulture, "OnBegin{0}", syncMethodName);
959 static string GetEndOperationDelegateName(string syncMethodName)
961 return string.Format(CultureInfo.InvariantCulture, "onEnd{0}Delegate", syncMethodName);
964 static string GetEndOperationMethodName(string syncMethodName)
966 return string.Format(CultureInfo.InvariantCulture, "OnEnd{0}", syncMethodName);
969 static string GetOperationCompletedDelegateName(string syncMethodName)
971 return string.Format(CultureInfo.InvariantCulture, "on{0}CompletedDelegate", syncMethodName);
974 static string GetOperationCompletedMethodName(string syncMethodName)
976 return string.Format(CultureInfo.InvariantCulture, "On{0}Completed", syncMethodName);
979 static string GetOperationCompletedEventName(string syncMethodName)
981 return string.Format(CultureInfo.InvariantCulture, "{0}Completed", syncMethodName);
984 static string GetOperationCompletedEventArgsTypeName(string syncMethodName)
986 return string.Format(CultureInfo.InvariantCulture, "{0}CompletedEventArgs", syncMethodName);
989 static internal string GetClientClassName(string interfaceName)
991 return GetClassName(interfaceName) + Strings.ClientTypeSuffix;
994 static bool IsVoid(CodeMemberMethod method)
996 return method.ReturnType == null || String.Compare(method.ReturnType.BaseType, typeof(void).FullName, StringComparison.Ordinal) == 0;
999 static CodeExpression GetChannelReference()
1001 return new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), Strings.ClientBaseChannelProperty);
1004 static class Strings
1006 public const string ClientBaseChannelProperty = "Channel";
1007 public const string ClientTypeSuffix = "Client";
1008 public const string InterfaceTypePrefix = "I";