private void GenerateInvokeMethodReturn(DomainOperationEntry domainOperationEntry, string parameterDictionaryString, InvokeKind invokeKind) { InvokeAttribute invokeAttribute = (InvokeAttribute)domainOperationEntry.OperationAttribute; string returnTypeNameString = CodeGenUtilities.GetTypeName(CodeGenUtilities.TranslateType(domainOperationEntry.ReturnType)); if (invokeKind == InvokeKind.Async) { returnTypeNameString = (domainOperationEntry.ReturnType == typeof(void)) ? string.Empty : string.Format("<{0}>", returnTypeNameString); this.Write("return this.InvokeOperationAsync"); this.Write(this.ToStringHelper.ToStringWithCulture(returnTypeNameString)); this.Write("(\""); this.Write(this.ToStringHelper.ToStringWithCulture(domainOperationEntry.Name)); this.Write("\", "); this.Write(this.ToStringHelper.ToStringWithCulture(parameterDictionaryString)); this.Write(", \r\n"); this.Write(this.ToStringHelper.ToStringWithCulture(CodeGenUtilities.GetBooleanString(invokeAttribute.HasSideEffects, true))); this.Write(", cancellationToken);\r\n"); } else { this.Write("return this."); this.Write(this.ToStringHelper.ToStringWithCulture(this.GetInvokeMethodReturnTypeName(domainOperationEntry, invokeKind))); this.Write("(\""); this.Write(this.ToStringHelper.ToStringWithCulture(domainOperationEntry.Name)); this.Write("\", typeof("); this.Write(this.ToStringHelper.ToStringWithCulture(returnTypeNameString)); this.Write("), "); this.Write(this.ToStringHelper.ToStringWithCulture(parameterDictionaryString)); this.Write(", \r\n"); this.Write(this.ToStringHelper.ToStringWithCulture(CodeGenUtilities.GetBooleanString(invokeAttribute.HasSideEffects, true))); this.Write(",\r\n"); if (invokeKind == InvokeKind.WithCallback) { this.Write("callback, userState);\r\n"); } else { this.Write("null, null);\r\n"); } } }
/// <summary> /// Generates an invoke operation. /// </summary> /// <param name="domainOperationEntry">The invoke operation.</param> /// <param name="invokeKind">the type of invoke method to generate.</param> private void GenerateInvokeOperation(DomainOperationEntry domainOperationEntry, InvokeKind invokeKind) { // Determine the name of the generated invoke function string methodName = domainOperationEntry.Name; // ---------------------------------------------------------------- // Check for name conflicts // ---------------------------------------------------------------- if ((invokeKind == InvokeKind.WithCallback) && this._proxyClass.Members.Cast <CodeTypeMember>().Any(c => c.Name == methodName && c.GetType() != typeof(CodeMemberMethod))) { this.ClientProxyGenerator.LogError( string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_NamingCollision_MemberAlreadyExists, this._proxyClass.Name, methodName)); return; } // ---------------------------------------------------------------- // InvokeResult<T> InvokeOperation(args); // // InvokeResult<T> InvokeOperation(args, callback, userState); // // Task<T> InvokeOperationAsync(args); // ---------------------------------------------------------------- CodeTypeReference operationReturnType = null; Type returnType = CodeGenUtilities.TranslateType(domainOperationEntry.ReturnType); var methodReturnTypeName = (invokeKind == InvokeKind.Async) ? TypeConstants.InvokeResultTypeFullName: TypeConstants.InvokeOperationTypeFullName; CodeTypeReference invokeOperationType = CodeGenUtilities.GetTypeReference(methodReturnTypeName, (string)this._proxyClass.UserData["Namespace"], false); if (returnType != typeof(void)) { // If this is an enum type, we need to ensure it is either shared or // can be generated. Failure to use this enum is only a warning and causes // this invoke operation to be skipped. The test for legality also causes // the enum to be generated if required. Type enumType = TypeUtility.GetNonNullableType(returnType); if (enumType.IsEnum) { string errorMessage = null; if (!this.ClientProxyGenerator.CanExposeEnumType(enumType, out errorMessage)) { this.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Domain_Op_Enum_Error, methodName, this._proxyClass.Name, enumType.FullName, errorMessage)); return; } else { // Register use of this enum type, which could cause deferred generation this.ClientProxyGenerator.RegisterUseOfEnumType(enumType); } } operationReturnType = CodeGenUtilities.GetTypeReference(returnType, this.ClientProxyGenerator, this._proxyClass); operationReturnType.Options |= CodeTypeReferenceOptions.GenericTypeParameter; invokeOperationType.TypeArguments.Add(operationReturnType); } // InvokeResults are wrapped in task (always) if (invokeKind == InvokeKind.Async) { invokeOperationType = new CodeTypeReference(typeof(Task).FullName, invokeOperationType); } CodeMemberMethod method = new CodeMemberMethod() { Attributes = MemberAttributes.Public | MemberAttributes.Final, Name = (invokeKind == InvokeKind.Async) ? (methodName + "Async") : methodName, ReturnType = invokeOperationType, }; this._proxyClass.Members.Add(method); ReadOnlyCollection <DomainOperationParameter> operationParameters = domainOperationEntry.Parameters; // Generate the <summary> doc comments string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Invoke_Method_Summary_Comment, domainOperationEntry.Name); method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // Generate <param> doc comments foreach (DomainOperationParameter parameter in operationParameters) { comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Invoke_Method_Parameter_Comment, parameter.Name); method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(parameter.Name, comment, this.ClientProxyGenerator.IsCSharp)); } // Conditionally add the callback and userState <param> doc comments if (invokeKind == InvokeKind.WithCallback) { method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("callback", Resource.CodeGen_DomainContext_Invoke_Method_Callback_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("userState", Resource.CodeGen_DomainContext_Invoke_Method_UserState_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); } else if (invokeKind == InvokeKind.Async) { method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("cancellationToken", Resource.CodeGen_DomainContext_Invoke_Method_CancellationToken_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); } // Generate <returns> doc comments method.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_DomainContext_Invoke_Returns_Comment, this.ClientProxyGenerator.IsCSharp)); // Propagate custom validation attributes from the DomainOperationEntry to this invoke operation. IEnumerable <Attribute> methodAttributes = domainOperationEntry.Attributes.Cast <Attribute>(); CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeMethod, ex.Message, method.Name, this._proxyClass.Name, ex.InnerException.Message), methodAttributes, method.CustomAttributes, method.Comments); // ---------------------------------------------------------------- // generate invoke operation body: // return base.InvokeOperation<T>(methodName, typeof(T), parameters, hasSideEffects, callback, userState); // or return base.InvokeOperationAsync<T>(methodName, parameters, hasSideEffects); // ---------------------------------------------------------------- List <CodeExpression> invokeParams = new List <CodeExpression>(); invokeParams.Add(new CodePrimitiveExpression(methodName)); // add the return Type parameter if (invokeKind != InvokeKind.Async) { invokeParams.Add(new CodeTypeOfExpression(operationReturnType)); } // add any operation parameters CodeVariableReferenceExpression paramsRef = new CodeVariableReferenceExpression("parameters"); if (operationParameters.Count > 0) { // need to generate the user parameters dictionary CodeTypeReference dictionaryTypeReference = CodeGenUtilities.GetTypeReference( typeof(Dictionary <string, object>), this.ClientProxyGenerator, this._proxyClass); CodeVariableDeclarationStatement paramsDef = new CodeVariableDeclarationStatement( dictionaryTypeReference, "parameters", new CodeObjectCreateExpression(dictionaryTypeReference, Array.Empty <CodeExpression>())); method.Statements.Add(paramsDef); } foreach (DomainOperationParameter paramInfo in operationParameters) { // If this is an enum type, we need to ensure it is either shared or // can be generated. Failure to use this enum logs an error and exits. // The test for legality also causes the enum to be generated if required. Type enumType = TypeUtility.GetNonNullableType(paramInfo.ParameterType); if (enumType.IsEnum) { string errorMessage = null; if (!this.ClientProxyGenerator.CanExposeEnumType(enumType, out errorMessage)) { this.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Domain_Op_Enum_Error, method.Name, this._proxyClass.Name, enumType.FullName, errorMessage)); return; } else { // Register use of this enum type, which could cause deferred generation this.ClientProxyGenerator.RegisterUseOfEnumType(enumType); } } // add the parameter to the method CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference( CodeGenUtilities.TranslateType(paramInfo.ParameterType), this.ClientProxyGenerator, this._proxyClass), paramInfo.Name); // Propagate parameter level validation attributes from domain operation entry IEnumerable <Attribute> paramAttributes = paramInfo.Attributes.Cast <Attribute>(); string commentHeader = string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_Parameter_FailedToGenerate, paramInfo.Name); CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeMethodParameter, ex.Message, paramDecl.Name, method.Name, this._proxyClass.Name, ex.InnerException.Message), paramAttributes, paramDecl.CustomAttributes, method.Comments, commentHeader); method.Parameters.Add(paramDecl); // add the parameter and value to the params dictionary method.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(paramsRef, "Add"), new CodePrimitiveExpression(paramInfo.Name), new CodeVariableReferenceExpression(paramInfo.Name))); } // add parameters if present if (operationParameters.Count > 0) { invokeParams.Add(paramsRef); } else { invokeParams.Add(new CodePrimitiveExpression(null)); } InvokeAttribute invokeAttribute = (InvokeAttribute)domainOperationEntry.OperationAttribute; invokeParams.Add(new CodePrimitiveExpression(invokeAttribute.HasSideEffects)); switch (invokeKind) { case InvokeKind.WithCallback: { CodeTypeReference callbackType = CodeGenUtilities.GetTypeReference(typeof(Action).FullName, (string)this._proxyClass.UserData["Namespace"], false); callbackType.TypeArguments.Add(invokeOperationType); // add callback method parameter method.Parameters.Add(new CodeParameterDeclarationExpression(callbackType, "callback")); invokeParams.Add(new CodeVariableReferenceExpression("callback")); // add the userState parameter to the end method.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(typeof(object), this.ClientProxyGenerator, this._proxyClass), "userState")); invokeParams.Add(new CodeVariableReferenceExpression("userState")); } break; case InvokeKind.WithoutCallback: invokeParams.Add(new CodePrimitiveExpression(null)); invokeParams.Add(new CodePrimitiveExpression(null)); break; case InvokeKind.Async: var cancellationTokenType = CodeGenUtilities.GetTypeReference(typeof(CancellationToken), this.ClientProxyGenerator, this._proxyClass); var cancellationTokenParmeter = new CodeParameterDeclarationExpression(cancellationTokenType, "cancellationToken"); // For C# add " = default(CancellationToken)" // For VB add "Optional" ByVal .... = Nothing // otherwise fall back to adding [Optional] attribute, this is the same as adding "= default(CancellationToken)" if (ClientProxyGenerator.IsCSharp) { cancellationTokenParmeter.Name = string.Format("{0} = default({1})", cancellationTokenParmeter.Name, cancellationTokenType.BaseType); } else if (ClientProxyGenerator.IsVB) // VB { // Set an invalid field direction in order to have VB Code gen from generating cancellationTokenParmeter.Direction = (FieldDirection)0xff; cancellationTokenParmeter.Name = "Optional ByVal " + cancellationTokenParmeter.Name; cancellationTokenParmeter.Type = new CodeTypeReference(cancellationTokenType.BaseType + " = Nothing"); } else { // Add [Optional] attribute cancellationTokenParmeter.CustomAttributes.Add(new CodeAttributeDeclaration( CodeGenUtilities.GetTypeReference(typeof(OptionalAttribute), this.ClientProxyGenerator, this._proxyClass))); } method.Parameters.Add(cancellationTokenParmeter); invokeParams.Add(new CodeVariableReferenceExpression("cancellationToken")); break; } // this.ValidateMethod("methodName", parameters); CodeExpression paramsExpr = new CodePrimitiveExpression(null); if (operationParameters.Count > 0) { paramsExpr = paramsRef; } CodeExpressionStatement validateMethodCall = new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeThisReferenceExpression(), "ValidateMethod", new CodeExpression[] { new CodePrimitiveExpression(methodName), paramsExpr })); method.Statements.Add(validateMethodCall); var invokeMethod = (invokeKind == InvokeKind.Async) ? "InvokeOperationAsync" : "InvokeOperation"; var typeParameters = (domainOperationEntry.ReturnType == typeof(void)) ? Array.Empty <CodeTypeReference>() : new[] { operationReturnType }; var invokeMethodReference = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), invokeMethod, typeParameters); CodeExpression invokeCall = new CodeMethodInvokeExpression(invokeMethodReference, invokeParams.ToArray()); method.Statements.Add(new CodeMethodReturnStatement(invokeCall)); }