/// <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);
            }
        }
Esempio n. 2
0
        /// <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);
            }
        }