/// <summary>Modifies the code document object model prior to the contract generation process.</summary> /// <param name="context">The code generated context to use to modify the code document prior to generation.</param> public void GenerateContract(ServiceContractGenerationContext context) { // Disable generation of the Event-Based Async Pattern, which has a conflicting naming scheme. context.ServiceContractGenerator.Options &= ~ServiceContractGenerationOptions.EventBasedAsynchronousMethods; string contractName = context.Contract.Name; string clientTypeName = TaskAsyncWsdlImportExtension.DeriveClientTypeName(contractName); // Look up the client class, and create it if it doesn't already exist. if (TaskAsyncWsdlImportExtension.FindClientType(clientTypeName, context.ServiceContractGenerator.TargetCompileUnit.Namespaces) == null) { // Create the new type CodeTypeDeclaration newClient = new CodeTypeDeclaration(clientTypeName) { Attributes = MemberAttributes.Public, IsPartial = true }; newClient.BaseTypes.Add(new CodeTypeReference(typeof(ClientBase <>)) { TypeArguments = { new CodeTypeReference(contractName) } }); newClient.BaseTypes.Add(new CodeTypeReference(contractName)); // Add the new type to the right namespace CodeNamespace contractNamespace = (from ns in context.ServiceContractGenerator.TargetCompileUnit.Namespaces.Cast <CodeNamespace>() from type in ns.Types.Cast <CodeTypeDeclaration>() where type == context.ContractType select ns).FirstOrDefault(); contractNamespace.Types.Add(newClient); } }
/// <summary>Generates the TAP implementation for a single operation.</summary> /// <param name="context">Information about the operation.</param> public void GenerateOperation(OperationContractGenerationContext context) { if (context.IsAsync) { string contractName = context.Contract.ContractType.Name; string clientTypeName = TaskAsyncWsdlImportExtension.DeriveClientTypeName(contractName); // Get the class to contain the new method. CodeTypeDeclaration clientClass = TaskAsyncWsdlImportExtension.FindClientType(clientTypeName, context.ServiceContractGenerator.TargetCompileUnit.Namespaces); // First, set up the new method, with attributes, name, parameters, and return type. CodeMemberMethod newTaskBasedMethod = new CodeMemberMethod() { Attributes = MemberAttributes.Final | MemberAttributes.Public, Name = context.SyncMethod.Name + "Async" }; newTaskBasedMethod.Parameters.AddRange(context.SyncMethod.Parameters); bool returnsVoid = context.SyncMethod.ReturnType == null || context.SyncMethod.ReturnType.BaseType == "System.Void"; if (returnsVoid) { newTaskBasedMethod.ReturnType = new CodeTypeReference(typeof(Task)); } else { var returnType = new CodeTypeReference(typeof(Task <>)); returnType.TypeArguments.Add(context.EndMethod.ReturnType); newTaskBasedMethod.ReturnType = returnType; } // Second, create the Task.Factory.FromAsync or Task<TResult>.Factory.FromAsync invoker. CodePropertyReferenceExpression getTaskFactory = new CodePropertyReferenceExpression(); getTaskFactory.PropertyName = "Factory"; CodeMethodInvokeExpression invokeFromAsync = new CodeMethodInvokeExpression(); if (returnsVoid) { getTaskFactory.TargetObject = new CodeTypeReferenceExpression(typeof(Task)); } else { var taskOfReturnType = new CodeTypeReference(typeof(Task <>)); taskOfReturnType.TypeArguments.Add(context.SyncMethod.ReturnType); getTaskFactory.TargetObject = new CodeTypeReferenceExpression(taskOfReturnType); } invokeFromAsync.Method = new CodeMethodReferenceExpression(getTaskFactory, "FromAsync"); newTaskBasedMethod.Statements.Add(new CodeMethodReturnStatement(invokeFromAsync)); // Create the end delegate for the FromAsync call. var endDelegate = new CodeDelegateCreateExpression(); endDelegate.MethodName = context.EndMethod.Name; endDelegate.TargetObject = new CodeCastExpression(contractName, new CodeThisReferenceExpression()); if (returnsVoid) { endDelegate.DelegateType = new CodeTypeReference(typeof(Action <IAsyncResult>)); } else { endDelegate.DelegateType = new CodeTypeReference(typeof(Func <,>)); endDelegate.DelegateType.TypeArguments.Add(typeof(IAsyncResult)); endDelegate.DelegateType.TypeArguments.Add(context.SyncMethod.ReturnType); } // If there are <= 3 parameters to the APM's Begin method, use a delegate-based // overload, as that's what TPL provides overloads for built-in. If not, // use an overload that accepts an IAsyncResult as the first parameter. if (context.SyncMethod.Parameters.Count <= 3) { // Create the begin delegate for the FromAsync call // FromAsync(beginDelegate, endDelegate, null); var beginDelegate = new CodeDelegateCreateExpression(); beginDelegate.MethodName = context.BeginMethod.Name; beginDelegate.TargetObject = new CodeCastExpression(contractName, new CodeThisReferenceExpression()); switch (context.SyncMethod.Parameters.Count) { case 0: beginDelegate.DelegateType = new CodeTypeReference(typeof(Func <, ,>)); break; case 1: beginDelegate.DelegateType = new CodeTypeReference(typeof(Func <, , ,>)); break; case 2: beginDelegate.DelegateType = new CodeTypeReference(typeof(Func <, , , ,>)); break; case 3: beginDelegate.DelegateType = new CodeTypeReference(typeof(Func <, , , , ,>)); break; } beginDelegate.DelegateType.TypeArguments.AddRange(context.SyncMethod.Parameters.Cast <CodeParameterDeclarationExpression>().Select(p => p.Type).ToArray()); beginDelegate.DelegateType.TypeArguments.Add(typeof(AsyncCallback)); beginDelegate.DelegateType.TypeArguments.Add(typeof(Object)); beginDelegate.DelegateType.TypeArguments.Add(typeof(IAsyncResult)); invokeFromAsync.Parameters.Add(beginDelegate); invokeFromAsync.Parameters.Add(endDelegate); invokeFromAsync.Parameters.AddRange((from parameter in context.SyncMethod.Parameters.Cast <CodeParameterDeclarationExpression>() select new CodeVariableReferenceExpression(parameter.Name)).ToArray()); invokeFromAsync.Parameters.Add(new CodePrimitiveExpression(null)); } else // > 3 parameters, so use the IAsyncResult overload { // FromAsync(BeginMethod(inputParams, ..., asyncCallback, state), endDelegate) var invokeBeginExpression = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(new CodeCastExpression(contractName, new CodeThisReferenceExpression()), context.BeginMethod.Name)); invokeBeginExpression.Parameters.AddRange((from parameter in context.SyncMethod.Parameters.Cast <CodeParameterDeclarationExpression>() select new CodeVariableReferenceExpression(parameter.Name)).ToArray()); invokeBeginExpression.Parameters.Add(new CodePrimitiveExpression(null)); // AsyncCallback invokeBeginExpression.Parameters.Add(new CodePrimitiveExpression(null)); // state invokeFromAsync.Parameters.Add(invokeBeginExpression); invokeFromAsync.Parameters.Add(endDelegate); } // Finally, add the new method to the class clientClass.Members.Add(newTaskBasedMethod); } }