/// <summary>
        /// Generates the "EntityContainer" for all the given entities
        /// </summary>
        /// <remarks>
        /// The EntityContainer is the logical store of the entity instances, and we have to generate
        /// code to instantiate entries in this store
        /// </remarks>
        /// <param name="proxyClass">Code into which to generate code</param>
        /// <param name="entityTypes">Set of all known entity types for which we need storage</param>
        /// <param name="domainServiceDescription">The DomainServiceDescription we're code genning for</param>
        private void GenEntityContainer(CodeTypeDeclaration proxyClass, IEnumerable <Type> entityTypes, DomainServiceDescription domainServiceDescription)
        {
            // ----------------------------------------------------------------
            // inner class
            // ----------------------------------------------------------------
            var innerClass = this.GenEntityContainerInnerClass(proxyClass, entityTypes, domainServiceDescription);

            // ----------------------------------------------------------------
            // method decl: protected override EntityContainer CreateEntityContainer()
            // ----------------------------------------------------------------
            var method = new CodeMemberMethod();

            method.Name       = "CreateEntityContainer";
            method.Attributes = MemberAttributes.Family | MemberAttributes.Override;
            method.ReturnType = CodeGenUtilities.GetTypeReference(TypeConstants.EntityContainerTypeFullName, proxyClass.UserData["Namespace"] as string, false);

            // Add <summary> and <returns> doc comments
            method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(Resource.CodeGen_CreateEntityContainer_Method_Summary_Comment, this.ClientProxyGenerator.IsCSharp));
            method.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_CreateEntityContainer_Method_Returns_Comment, this.ClientProxyGenerator.IsCSharp));

            // ----------------------------------------------------------------
            // method body: return new 'innerClass'
            // ----------------------------------------------------------------
            var innerClassReference = CodeGenUtilities.GetTypeReference(proxyClass.UserData["Namespace"] as string + "." + proxyClass.Name + "." + innerClass.Name, proxyClass.UserData["Namespace"] as string, true);
            var newInnerClassExpr   = new CodeObjectCreateExpression(innerClassReference, new CodeExpression[0]);
            var returnStatement     = new CodeMethodReturnStatement(newInnerClassExpr);

            method.Statements.Add(returnStatement);
            proxyClass.Members.Add(method);
        }
        private void GenerateContractSubmitChangesMethod(CodeTypeDeclaration contractInterface)
        {
            string domainServiceName = this._domainServiceDescription.DomainServiceType.Name;

            // -----------------------------------------------------------------------
            // IAsyncResult BeginSubmitChanges(IEnumerable<EntityOperation> changeSet, AsyncCallback callback, object asyncState)
            // -----------------------------------------------------------------------
            CodeMemberMethod beginQueryMethod = new CodeMemberMethod();

            beginQueryMethod.Name       = "BeginSubmitChanges";
            beginQueryMethod.ReturnType = CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), this.ClientProxyGenerator, contractInterface);
            contractInterface.Members.Add(beginQueryMethod);

            // Generate <summary> doc comment for the Begin method
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Summary_Comment, "SubmitChanges");

            beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // <param> for callback and asyncState
            beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("changeSet", Resource.CodeGen_DomainContext_ServiceContract_Begin_SubmitMethod_Changeset_Parameter_Comment, this.ClientProxyGenerator.IsCSharp));
            beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("callback", Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Callback_Parameter_Comment, this.ClientProxyGenerator.IsCSharp));
            beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("asyncState", Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_AsyncState_Parameter_Comment, this.ClientProxyGenerator.IsCSharp));

            // Generate <returns> doc comment
            beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Returns_Comment, this.ClientProxyGenerator.IsCSharp));

            this.GenerateContractMethodAttributes(contractInterface, beginQueryMethod, domainServiceName, "SubmitChanges");

            CodeTypeReference enumTypeRef = CodeGenUtilities.GetTypeReference(TypeConstants.IEnumerableFullName, contractInterface.UserData["Namespace"] as string, false);

            enumTypeRef.TypeArguments.Add(CodeGenUtilities.GetTypeReference(TypeConstants.ChangeSetEntryTypeFullName, contractInterface.UserData["Namespace"] as string, false));

            beginQueryMethod.Parameters.Add(
                new CodeParameterDeclarationExpression(
                    enumTypeRef,
                    "changeSet"));

            beginQueryMethod.Parameters.Add(
                new CodeParameterDeclarationExpression(
                    CodeGenUtilities.GetTypeReference(typeof(AsyncCallback), this.ClientProxyGenerator, contractInterface),
                    "callback"));

            beginQueryMethod.Parameters.Add(
                new CodeParameterDeclarationExpression(
                    CodeGenUtilities.GetTypeReference(typeof(object), this.ClientProxyGenerator, contractInterface),
                    "asyncState"));

            // -----------------------------------------------------------------------
            // IEnumerable<EntityOperation> EndSubmitChanges(IAsyncResult result)
            // -----------------------------------------------------------------------
            CodeTypeReference resultTypeRef = CodeGenUtilities.GetTypeReference(TypeConstants.DomainServiceFaultFullName, contractInterface.UserData["Namespace"] as string, false);

            resultTypeRef.TypeArguments.Add(CodeGenUtilities.GetTypeReference(TypeConstants.ChangeSetEntryTypeFullName, contractInterface.UserData["Namespace"] as string, false));

            CodeMemberMethod endQueryMethod = new CodeMemberMethod();

            endQueryMethod.Name       = "EndSubmitChanges";
            endQueryMethod.ReturnType = enumTypeRef;
            contractInterface.Members.Add(endQueryMethod);

            // Generate <summary> doc comment for the End method
            comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Summary_Comment, "BeginSubmitChanges");
            endQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // Generate <param> doc comment for the IAsyncResult parameter
            comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Parameter_Comment, "BeginSubmitChanges");
            endQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("result", comment, this.ClientProxyGenerator.IsCSharp));

            // Generate <returns> doc comment
            endQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_DomainContext_ServiceContract_End_SubmitMethod_Returns_Comment, this.ClientProxyGenerator.IsCSharp));

            endQueryMethod.Parameters.Add(
                new CodeParameterDeclarationExpression(
                    CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), this.ClientProxyGenerator, contractInterface),
                    "result"));
        }
        private void GenerateContractMethod(CodeTypeDeclaration contractInterface, DomainOperationEntry operation)
        {
            string domainServiceName = this._domainServiceDescription.DomainServiceType.Name;

            CodeMemberMethod beginMethod = new CodeMemberMethod();

            beginMethod.Name       = "Begin" + operation.Name;
            beginMethod.ReturnType = CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), this.ClientProxyGenerator, contractInterface);
            contractInterface.Members.Add(beginMethod);

            // Generate <summary> doc comment for the Begin method
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Summary_Comment, operation.Name);

            beginMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // Generate <param> doc comment for all the parameters
            foreach (DomainOperationParameter parameter in operation.Parameters)
            {
                comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Parameter_Comment, parameter.Name);
                beginMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(parameter.Name, comment, this.ClientProxyGenerator.IsCSharp));
            }

            // <param> for callback and asyncState
            beginMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("callback", Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Callback_Parameter_Comment, this.ClientProxyGenerator.IsCSharp));
            beginMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("asyncState", Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_AsyncState_Parameter_Comment, this.ClientProxyGenerator.IsCSharp));

            // Generate <returns> doc comment
            beginMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Returns_Comment, this.ClientProxyGenerator.IsCSharp));

            this.GenerateContractMethodAttributes(contractInterface, beginMethod, domainServiceName, operation.Name);

            foreach (DomainOperationParameter parameter in operation.Parameters)
            {
                beginMethod.Parameters.Add(
                    new CodeParameterDeclarationExpression(
                        CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(parameter.ParameterType), this.ClientProxyGenerator, contractInterface),
                        parameter.Name));
            }

            beginMethod.Parameters.Add(
                new CodeParameterDeclarationExpression(
                    CodeGenUtilities.GetTypeReference(typeof(AsyncCallback), this.ClientProxyGenerator, contractInterface),
                    "callback"));

            beginMethod.Parameters.Add(
                new CodeParameterDeclarationExpression(
                    CodeGenUtilities.GetTypeReference(typeof(object), this.ClientProxyGenerator, contractInterface),
                    "asyncState"));

            CodeMemberMethod endMethod = new CodeMemberMethod();

            endMethod.Name = "End" + operation.Name;

            bool   hasSideEffects = true;
            string returnTypeName = null;

            if (operation.Operation == DomainOperation.Query)
            {
                hasSideEffects = ((QueryAttribute)operation.OperationAttribute).HasSideEffects;
                returnTypeName = "QueryResult";
                if (operation.ReturnType == typeof(void))
                {
                    endMethod.ReturnType = CodeGenUtilities.GetTypeReference(TypeConstants.QueryResultFullName, contractInterface.UserData["Namespace"] as string, false);
                }
                else
                {
                    endMethod.ReturnType = CodeGenUtilities.GetTypeReference(TypeConstants.QueryResultFullName, contractInterface.UserData["Namespace"] as string, false);
                    endMethod.ReturnType.TypeArguments.Add(CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(operation.AssociatedType), this.ClientProxyGenerator, contractInterface));
                }
            }
            else
            {
                if (operation.Operation == DomainOperation.Invoke)
                {
                    hasSideEffects = ((InvokeAttribute)operation.OperationAttribute).HasSideEffects;
                }
                returnTypeName       = CodeGenUtilities.TranslateType(operation.ReturnType).Name;
                endMethod.ReturnType = CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(operation.ReturnType), this.ClientProxyGenerator, contractInterface, false);
            }

            // Generate [HasSideEffects(...)].
            beginMethod.CustomAttributes.Add(new CodeAttributeDeclaration(CodeGenUtilities.GetTypeReference(TypeConstants.HasSideEffectsFullName, contractInterface.UserData["Namespace"] as string, false),
                                                                          new CodeAttributeArgument(new CodePrimitiveExpression(hasSideEffects))));

            // Generate <summary> doc comment for the End method
            comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Summary_Comment, beginMethod.Name);
            endMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // Generate <param> doc comment for the IAsyncResult parameter
            comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Parameter_Comment, beginMethod.Name);
            endMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("result", comment, this.ClientProxyGenerator.IsCSharp));

            // Generate <returns> doc comment
            if (operation.ReturnType != typeof(void))
            {
                comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Returns_Comment, returnTypeName, operation.Name);
                endMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(comment, this.ClientProxyGenerator.IsCSharp));
            }

            contractInterface.Members.Add(endMethod);

            endMethod.Parameters.Add(
                new CodeParameterDeclarationExpression(
                    CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), this.ClientProxyGenerator, contractInterface),
                    "result"));
        }
Exemple #4
0
        /// <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));
        }
        private void GenerateEntityQueryMethod(DomainOperationEntry domainOperationEntry)
        {
            string queryMethodName = domainOperationEntry.Name + QuerySuffix;

            Type entityType = TypeUtility.GetElementType(domainOperationEntry.ReturnType);

            CodeMemberMethod queryMethod = new CodeMemberMethod();

            queryMethod.Name       = queryMethodName;
            queryMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; // Final needed, else becomes virtual

            queryMethod.ReturnType = CodeGenUtilities.GetTypeReference(TypeConstants.EntityQueryTypeFullName, this._domainServiceDescription.DomainServiceType.Namespace, false);
            queryMethod.ReturnType.TypeArguments.Add(CodeGenUtilities.GetTypeReference(entityType.FullName, this._domainServiceDescription.DomainServiceType.Namespace, true));

            DomainOperationParameter[] domainOperationEntryParameters = domainOperationEntry.Parameters.ToArray();

            // Generate <summary> doc comment
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Summary_DomainContext, entityType.Name, domainOperationEntry.Name);

            queryMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // Generate <param> doc comments
            foreach (DomainOperationParameter paramInfo in domainOperationEntryParameters)
            {
                comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Query_Method_Parameter_Comment, paramInfo.Name);
                queryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(paramInfo.Name, comment, this.ClientProxyGenerator.IsCSharp));
            }

            // Generate <returns> doc comments
            comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Query_Method_Returns_Comment, domainOperationEntry.AssociatedType.Name);
            queryMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // Propagate custom validation attributes
            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, queryMethod.Name, this._proxyClass.Name, ex.InnerException.Message),
                methodAttributes,
                queryMethod.CustomAttributes,
                queryMethod.Comments);

            // add any domain operation entry parameters first

            CodeVariableReferenceExpression paramsRef = new CodeVariableReferenceExpression("parameters");

            if (domainOperationEntryParameters.Length > 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, new CodeExpression[0]));
                queryMethod.Statements.Add(paramsDef);
            }
            foreach (DomainOperationParameter paramInfo in domainOperationEntryParameters)
            {
                CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression(
                    CodeGenUtilities.GetTypeReference(
                        CodeGenUtilities.TranslateType(paramInfo.ParameterType),
                        this.ClientProxyGenerator,
                        this._proxyClass),
                    paramInfo.Name);

                // Propagate parameter level validation attributes
                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, queryMethod.Name, this._proxyClass.Name, ex.InnerException.Message),
                    paramAttributes,
                    paramDecl.CustomAttributes,
                    queryMethod.Comments,
                    commentHeader);

                // add the parameter to the query method
                queryMethod.Parameters.Add(paramDecl);

                // add the parameter and value to the params dictionary
                queryMethod.Statements.Add(new CodeMethodInvokeExpression(
                                               new CodeMethodReferenceExpression(paramsRef, "Add"),
                                               new CodePrimitiveExpression(paramInfo.Name),
                                               new CodeVariableReferenceExpression(paramInfo.Name)));
            }

            // add argument for queryName
            CodeExpressionCollection arguments = new CodeExpressionCollection();

            arguments.Add(new CodePrimitiveExpression(domainOperationEntry.Name));

            // add argument for parameters
            if (domainOperationEntryParameters.Length > 0)
            {
                arguments.Add(paramsRef);
            }
            else
            {
                arguments.Add(new CodePrimitiveExpression(null));
            }

            // add argument for hasSideEffects
            QueryAttribute queryAttribute = (QueryAttribute)domainOperationEntry.OperationAttribute;

            arguments.Add(new CodePrimitiveExpression(queryAttribute.HasSideEffects));

            // add argument for isComposable
            arguments.Add(new CodePrimitiveExpression(queryAttribute.IsComposable));

            // this.ValidateMethod("methodName", parameters);
            CodeExpression paramsExpr = new CodePrimitiveExpression(null);

            if (domainOperationEntryParameters.Length > 0)
            {
                paramsExpr = paramsRef;
            }
            CodeExpressionStatement validateMethodCall = new CodeExpressionStatement(
                new CodeMethodInvokeExpression(
                    new CodeThisReferenceExpression(),
                    "ValidateMethod",
                    new CodeExpression[]
            {
                new CodePrimitiveExpression(queryMethodName),
                paramsExpr
            }));

            queryMethod.Statements.Add(validateMethodCall);

            // ----------------------------------------------------------------
            // method call: base.CreateQuery(arguments...)
            // ----------------------------------------------------------------
            CodeTypeReference             entityTypeRef     = CodeGenUtilities.GetTypeReference(entityType.FullName, this._domainServiceDescription.DomainServiceType.Namespace, true);
            CodeMethodReferenceExpression createQueryMethod = new CodeMethodReferenceExpression(new CodeBaseReferenceExpression(), "CreateQuery", entityTypeRef);
            CodeMethodReturnStatement     createQueryCall   = new CodeMethodReturnStatement(new CodeMethodInvokeExpression(createQueryMethod, arguments.Cast <CodeExpression>().ToArray()));

            queryMethod.Statements.Add(createQueryCall);

            this._proxyClass.Members.Add(queryMethod);
        }