private void GenerateContractServiceKnownTypes(CodeTypeDeclaration contractInterface, DomainOperationEntry operation, HashSet <Type> registeredTypes)
        {
            List <Attribute> knownTypeAttributes = new List <Attribute>();

            foreach (DomainOperationParameter parameter in operation.Parameters)
            {
                Type t = CodeGenUtilities.TranslateType(parameter.ParameterType);

                // All Nullable<T> types are unwrapped to the underlying non-nullable, because
                // that is they type we need to represent, not typeof(Nullable<T>)
                t = TypeUtility.GetNonNullableType(t);

                if (TypeUtility.IsPredefinedListType(t) || TypeUtility.IsComplexTypeCollection(t))
                {
                    Type elementType = TypeUtility.GetElementType(t);
                    if (elementType != null)
                    {
                        t = elementType.MakeArrayType();
                    }
                }

                // Check if the type is a simple type or already registered
                if (registeredTypes.Contains(t) || !this.TypeRequiresRegistration(t))
                {
                    continue;
                }

                // Record the type to prevent redundant [ServiceKnownType]'s.
                // This loop executes within a larger loop over multiple
                // DomainOperationEntries that may have already processed it.
                registeredTypes.Add(t);

                // If we determine we need to generate this enum type on the client,
                // then we need to register that intent and conjure a virtual type
                // here in our list of registered types to account for the fact it
                // could get a different root namespace on the client.
                if (t.IsEnum && this.ClientProxyGenerator.NeedToGenerateEnumType(t))
                {
                    // Request deferred generation of the enum
                    this.ClientProxyGenerator.RegisterUseOfEnumType(t);

                    // Compose a virtual type that will reflect the correct namespace
                    // on the client when the [ServiceKnownType] is created.
                    t = new VirtualType(t.Name, CodeGenUtilities.TranslateNamespace(t, this.ClientProxyGenerator), t.Assembly, t.BaseType);
                }

                knownTypeAttributes.Add(new ServiceKnownTypeAttribute(t));
            }

            if (knownTypeAttributes.Count > 0)
            {
                CustomAttributeGenerator.GenerateCustomAttributes(
                    this.ClientProxyGenerator,
                    contractInterface,
                    knownTypeAttributes,
                    contractInterface.CustomAttributes,
                    contractInterface.Comments,
                    true /* force propagation, we want a compile error if these types aren't present on client */);
            }
        }
        /// <summary>
        /// Generates the client proxy code for the given type.
        /// </summary>
        public override void Generate()
        {
            // ----------------------------------------------------------------
            // namespace
            // ----------------------------------------------------------------
            CodeNamespace ns = this.ClientProxyGenerator.GetOrGenNamespace(this.Type);

            // Missing namespace bails out of code-gen -- error has been logged
            if (ns == null)
            {
                return;
            }

            // ----------------------------------------------------------------
            // public partial class {Type} : (Base)
            // ----------------------------------------------------------------
            this.ProxyClass                = CodeGenUtilities.CreateTypeDeclaration(this.Type);
            this.ProxyClass.IsPartial      = true; // makes this a partial type
            this.ProxyClass.TypeAttributes = TypeAttributes.Public;

            // Abstract classes must be preserved as abstract to avoid explicit instantiation on client
            bool isAbstract = (this.Type.IsAbstract);

            if (isAbstract)
            {
                this.ProxyClass.TypeAttributes |= TypeAttributes.Abstract;
            }

            // Determine all types derived from this one.
            // Note this list does not assume the current type is the visible root.  That is a separate test.
            IEnumerable <Type> derivedTypes = this.GetDerivedTypes();

            // If this type doesn't have any derivatives, seal it.  Cannot seal abstracts.
            if (!isAbstract && !derivedTypes.Any())
            {
                this.ProxyClass.TypeAttributes |= TypeAttributes.Sealed;
            }

            // Add all base types including interfaces
            this.AddBaseTypes(ns);
            ns.Types.Add(this.ProxyClass);

            AttributeCollection typeAttributes = this.Type.Attributes();

            // Add <summary> xml comment to class
            string comment = this.GetSummaryComment();

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

            // ----------------------------------------------------------------
            // Add default ctr
            // ----------------------------------------------------------------
            CodeConstructor constructor = new CodeConstructor();

            // Default ctor is public for concrete types but protected for abstracts.
            // This prevents direct instantiation on client
            constructor.Attributes = isAbstract ? MemberAttributes.Family : MemberAttributes.Public;

            // add default ctor doc comments
            comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Default_Constructor_Summary_Comments, this.Type.Name);
            constructor.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // add call to default OnCreated method
            constructor.Statements.Add(this.NotificationMethodGen.OnCreatedMethodInvokeExpression);
            this.ProxyClass.Members.Add(constructor);

            // ----------------------------------------------------------------
            // [KnownType(...), ...]
            // ----------------------------------------------------------------

            // We need to generate a [KnownType] for all derived entities on the visible root.
            if (!this.IsDerivedType)
            {
                // Generate a [KnownType] for every derived type.
                // We specifically exclude [KnownTypes] from the set of attributes we ask
                // the metadata pipeline to generate below, meaning we take total control
                // here for which [KnownType] attributes get through the metadata pipeline.
                //
                // Note, we sort in alphabetic order to give predictability in baselines and
                // client readability.  For cosmetic reasons, we sort by short or long name
                // depending on what our utility helpers will actually generated
                foreach (Type derivedType in derivedTypes.OrderBy(t => this.ClientProxyGenerator.ClientProxyCodeGenerationOptions.UseFullTypeNames ? t.FullName : t.Name))
                {
                    CodeAttributeDeclaration knownTypeAttrib = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.KnownTypeAttribute), this.ClientProxyGenerator, this.ProxyClass);
                    knownTypeAttrib.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(CodeGenUtilities.GetTypeReference(derivedType, this.ClientProxyGenerator, this.ProxyClass))));
                    this.ProxyClass.CustomAttributes.Add(knownTypeAttrib);
                }
            }

            this.ValidateTypeAttributes(typeAttributes);

            // ----------------------------------------------------------------
            // [DataContract(Namespace=X, Name=Y)]
            // ----------------------------------------------------------------
            CodeAttributeDeclaration dataContractAttrib = CodeGenUtilities.CreateDataContractAttributeDeclaration(this.Type, this.ClientProxyGenerator, this.ProxyClass);

            this.ProxyClass.CustomAttributes.Add(dataContractAttrib);

            // ----------------------------------------------------------------
            // Propagate all type-level Attributes across (except DataContractAttribute since that is handled above)
            // -----------------------------------------------------------------
            CustomAttributeGenerator.GenerateCustomAttributes(
                this.ClientProxyGenerator,
                this.ProxyClass,
                ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeType, ex.Message, this.ProxyClass.Name, ex.InnerException.Message),
                this.FilterTypeAttributes(typeAttributes),
                this.ProxyClass.CustomAttributes,
                this.ProxyClass.Comments);

            // ----------------------------------------------------------------
            // gen proxy getter/setter for each property
            // ----------------------------------------------------------------
            this.GenerateProperties();

            // ----------------------------------------------------------------
            // gen additional methods/events
            // ----------------------------------------------------------------
            this.GenerateAdditionalMembers();

            // Register created CodeTypeDeclaration with mapping
            this._typeMapping[this.Type] = this.ProxyClass;
        }
        /// <summary>
        /// Generates a property getter/setter pair into the given proxy class to match the given property info.
        /// </summary>
        /// <param name="propertyDescriptor">PropertyDescriptor for the property to generate for.</param>
        protected virtual void GenerateProperty(PropertyDescriptor propertyDescriptor)
        {
            string propertyName = propertyDescriptor.Name;
            Type   propertyType = CodeGenUtilities.TranslateType(propertyDescriptor.PropertyType);

            // ----------------------------------------------------------------
            // Property type ref
            // ----------------------------------------------------------------
            var propTypeReference = CodeGenUtilities.GetTypeReference(propertyType, this.ClientProxyGenerator, this.ProxyClass);

            // ----------------------------------------------------------------
            // Property decl
            // ----------------------------------------------------------------
            var property = new CodeMemberProperty();

            property.Name       = propertyName;
            property.Type       = propTypeReference;
            property.Attributes = MemberAttributes.Public | MemberAttributes.Final; // final needed, else becomes virtual
            List <Attribute> propertyAttributes = propertyDescriptor.ExplicitAttributes().Cast <Attribute>().ToList();

            // Generate <summary> for property
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_Property_Summary_Comment, propertyName);

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

            // ----------------------------------------------------------------
            // [DataMember] -> Add if not already present.
            // ----------------------------------------------------------------
            // Add if not already present.

            if (!propertyAttributes.OfType <DataMemberAttribute>().Any())
            {
                CodeAttributeDeclaration dataMemberAtt = CodeGenUtilities.CreateAttributeDeclaration(typeof(DataMemberAttribute), this.ClientProxyGenerator, this.ProxyClass);
                property.CustomAttributes.Add(dataMemberAtt);
            }

            // Here, we check for the existence of [ReadOnly(true)] attributes generated when
            // the property does not not have a setter.  We want to inject an [Editable(false)]
            // attribute into the pipeline.
            ReadOnlyAttribute readOnlyAttr = propertyAttributes.OfType <ReadOnlyAttribute>().SingleOrDefault();

            if (readOnlyAttr != null && !propertyAttributes.OfType <EditableAttribute>().Any())
            {
                propertyAttributes.Add(new EditableAttribute(!readOnlyAttr.IsReadOnly));

                // REVIEW:  should we strip out [ReadOnly] attributes here?
            }

            // Here we check if the type has a RoundtripOriginalAttribute. In that case we strip it out from the property
            if (this._isRoundtripType)
            {
                propertyAttributes.RemoveAll(attr => attr.GetType() == typeof(RoundtripOriginalAttribute));
            }

            // Here we check for database generated fields. In that case we strip any RequiredAttribute from the property.
            if (propertyAttributes.Any(a => a.GetType().Name == "DatabaseGeneratedAttribute"))
            {
                propertyAttributes.RemoveAll(attr => attr.GetType() == typeof(RequiredAttribute));
            }

            // Here, we check for the presence of a complex type. If it exists we need to add a DisplayAttribute
            // if not already there. DataSources windows do not handle complex types
            if (TypeUtility.IsSupportedComplexType(propertyType) && !propertyAttributes.OfType <DisplayAttribute>().Any())
            {
                CodeAttributeDeclaration displayAttribute = CodeGenUtilities.CreateDisplayAttributeDeclaration(this.ClientProxyGenerator, this.ProxyClass);
                property.CustomAttributes.Add(displayAttribute);
            }

            // ----------------------------------------------------------------
            // Propagate the custom attributes
            // ----------------------------------------------------------------

            CustomAttributeGenerator.GenerateCustomAttributes(
                this.ClientProxyGenerator,
                this.ProxyClass,
                ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, property.Name, this.ProxyClass.Name, ex.InnerException.Message),
                propertyAttributes.Cast <Attribute>(),
                property.CustomAttributes,
                property.Comments);

            // ----------------------------------------------------------------
            // backing private field (CodeDom doesn't yet know about auto properties)
            // ----------------------------------------------------------------
            string fieldName = CodeGenUtilities.MakeCompliantFieldName(propertyName);
            var    field     = new CodeMemberField(propTypeReference, fieldName);

            this.ProxyClass.Members.Add(field);
            var fieldRef = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName);
            var valueRef = new CodePropertySetValueReferenceExpression();

            // ----------------------------------------------------------------
            // getter body
            // ----------------------------------------------------------------
            property.GetStatements.Add(new CodeMethodReturnStatement(fieldRef));

            // ----------------------------------------------------------------
            // setter body
            // ----------------------------------------------------------------
            List <CodeStatement> bodyStatements = new List <CodeStatement>();

            // this.OnPropertyXxxChanging(PropType value);
            bodyStatements.Add(this.NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changing"));

            bool propertyIsReadOnly = this.IsPropertyReadOnly(propertyDescriptor);

            if (!propertyIsReadOnly)
            {
                bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaiseDataMemberChanging", new CodePrimitiveExpression(propertyDescriptor.Name))));
            }

            // Generate the validation tests.
            CodeStatement validationCode = GeneratePropertySetterValidation(propertyDescriptor.Name);

            bodyStatements.Add(validationCode);

            // this._field = value
            bodyStatements.Add(new CodeAssignStatement(fieldRef, valueRef));

            if (!propertyIsReadOnly)
            {
                bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaiseDataMemberChanged", new CodePrimitiveExpression(propertyDescriptor.Name))));
            }
            else
            {
                // even read-only members need to raise PropertyChanged
                bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaisePropertyChanged", new CodePrimitiveExpression(propertyDescriptor.Name))));
            }

            // this.OnPropertyXxxChanged();
            bodyStatements.Add(this.NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changed"));

            // if (this._field != value)...
            CodeExpression valueTest = CodeGenUtilities.MakeNotEqual(propertyType, fieldRef, valueRef, this.ClientProxyGenerator.IsCSharp);

            CodeConditionStatement body = new CodeConditionStatement(valueTest, bodyStatements.ToArray <CodeStatement>());

            property.SetStatements.Add(body);

            // add property
            this.ProxyClass.Members.Add(property);
        }
        /// <summary>
        /// Generates the client proxy code for a domain service.
        /// </summary>
        public override void Generate()
        {
            // ----------------------------------------------------------------
            // Namespace
            // ----------------------------------------------------------------
            Type                domainServiceType = this._domainServiceDescription.DomainServiceType;
            CodeNamespace       ns         = this.ClientProxyGenerator.GetOrGenNamespace(domainServiceType);
            AttributeCollection attributes = this._domainServiceDescription.Attributes;

            // Missing namespace bails out of code-gen -- error has been logged
            if (ns == null)
            {
                return;
            }

            // ----------------------------------------------------------------
            // public partial sealed class {Name} : DomainContext
            // ----------------------------------------------------------------
            string clientTypeName = DomainContextTypeName(this._domainServiceDescription);

            CodeTypeDeclaration proxyClass = CodeGenUtilities.CreateTypeDeclaration(clientTypeName, domainServiceType.Namespace);

            proxyClass.IsPartial      = true;
            proxyClass.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed;
            ns.Types.Add(proxyClass);

            CodeTypeReference domainContextTypeName = CodeGenUtilities.GetTypeReference(TypeConstants.DomainContextTypeFullName, ns.Name, false);

            proxyClass.BaseTypes.Add(domainContextTypeName);

            // Add <summary> xml comment to class
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Class_Summary_Comment, domainServiceType.Name);

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

            // ----------------------------------------------------------------
            // [DomainIdentifier], etc attributes move through metadata pipeline
            // ----------------------------------------------------------------
            CustomAttributeGenerator.GenerateCustomAttributes(
                this.ClientProxyGenerator,
                proxyClass,
                ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeType, ex.Message, proxyClass.Name, ex.InnerException.Message),
                attributes.Cast <Attribute>(),
                proxyClass.CustomAttributes,
                proxyClass.Comments);

            // ----------------------------------------------------------------
            // Add default OnCreated partial method
            // ----------------------------------------------------------------
            NotificationMethodGenerator notificationMethodGen = new NotificationMethodGenerator(this.ClientProxyGenerator);

            proxyClass.Members.AddRange(notificationMethodGen.PartialMethodsSnippetBlock);

            // ----------------------------------------------------------------
            // Generate a contract interface for the service.
            // ----------------------------------------------------------------
            CodeTypeDeclaration contractInterface = this.GenerateContract(proxyClass);

            // ----------------------------------------------------------------
            // Generate constructors
            // ----------------------------------------------------------------
            EnableClientAccessAttribute enableClientAccessAttribute = attributes.OfType <EnableClientAccessAttribute>().Single();

            this.GenerateConstructors(proxyClass, contractInterface, enableClientAccessAttribute, notificationMethodGen.OnCreatedMethodInvokeExpression);

            // ----------------------------------------------------------------
            // Separate proxies for each domain operation entry
            // ----------------------------------------------------------------
            DomainOperationEntryProxyGenerator methodProxyGenerator = new DomainOperationEntryProxyGenerator(this.ClientProxyGenerator, proxyClass, this._domainServiceDescription);

            methodProxyGenerator.Generate();

            // ----------------------------------------------------------------
            // Invoke operations
            // ----------------------------------------------------------------
            InvokeOperationProxyGenerator invokeOperationProxyGenerator = new InvokeOperationProxyGenerator(this.ClientProxyGenerator, proxyClass, this._domainServiceDescription);

            invokeOperationProxyGenerator.Generate();

            // ----------------------------------------------------------------
            // EntityContainer instantiation
            // ----------------------------------------------------------------

            // The entity container holds a collection of EntityLists, one per visible entity root type.
            // The derived entity types are stored in their respective root's list and do not get their own.
            this.GenEntityContainer(proxyClass, this._domainServiceDescription.RootEntityTypes, this._domainServiceDescription);

            // Register created CodeTypeDeclaration with mapping
            this._typeMapping[domainServiceType] = proxyClass;
        }
        private void GenerateCustomMethod(string customMethodName, DomainOperationEntry customMethod)
        {
            this.ClientProxyGenerator.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_Generating_InvokeMethod, customMethodName));

            // ----------------------------------------------------------------
            // Method decl
            // ----------------------------------------------------------------
            CodeMemberMethod method = new CodeMemberMethod();

            method.Name       = customMethodName;
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            // The custom method parameter list is the same as the domain operation entries -- except for the first parameter
            // which is the entity.  We omit that first parameter in code gen
            DomainOperationParameter[] paramInfos = customMethod.Parameters.ToArray();

            // Generate <summary> doc comment for the custom method body
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_Custom_Method_Summary_Comment, customMethodName);

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

            // Generate <param> doc comment for all the parameters
            for (int i = 1; i < paramInfos.Length; ++i)
            {
                comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_Custom_Method_Parameter_Comment, paramInfos[i].Name);
                method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(paramInfos[i].Name, comment, this.ClientProxyGenerator.IsCSharp));
            }

            // Propagate custom validation attributes from the DomainOperationEntry to this custom method
            var methodAttributes = customMethod.Attributes.Cast <Attribute>().ToList();

            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);

            // Add [CustomMethod("...")] property
            var  customMethodAttribute    = customMethod.OperationAttribute as EntityActionAttribute;
            bool allowMultipleInvocations = customMethodAttribute != null && customMethodAttribute.AllowMultipleInvocations;

            method.CustomAttributes.Add(

                new CodeAttributeDeclaration(
                    CodeGenUtilities.GetTypeReference(TypeConstants.EntityActionAttributeFullName, (string)this._proxyClass.UserData["Namespace"], false),
                    new CodeAttributeArgument(new CodePrimitiveExpression(customMethodName)),
                    new CodeAttributeArgument("AllowMultipleInvocations",
                                              new CodePrimitiveExpression(allowMultipleInvocations))
                    ));

            // ----------------------------------------------------------------
            // generate custom method body:
            //    this.OnMethodNameInvoking(params);
            //    base.Invoke(methodName, params);
            //    this.OnMethodNameInvoked();
            // ----------------------------------------------------------------
            List <CodeExpression> invokeParams = new List <CodeExpression>();

            invokeParams.Add(new CodePrimitiveExpression(customMethodName));

            // Create an expression for each parameter, and also use this loop to
            // propagate the custom attributes for each parameter in the DomainOperationEntry to the custom method.
            for (int i = 1; i < paramInfos.Length; ++i)
            {
                DomainOperationParameter paramInfo = paramInfos[i];

                // build up the method parameter signature from the DomainOperationEntry.MethodInfo
                CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression(
                    CodeGenUtilities.GetTypeReference(
                        CodeGenUtilities.TranslateType(paramInfo.ParameterType),
                        this.ClientProxyGenerator,
                        this._proxyClass),
                    paramInfo.Name);

                // Propagate parameter level validation attributes from custom 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);

                // build up the invoke call parameters
                invokeParams.Add(new CodeVariableReferenceExpression(paramInfo.Name));
            }

            // generate 'OnCustomMethodInvoked/Invoking'
            string methodInvokingName = customMethodName + "Invoking";
            string methodInvokedName  = customMethodName + "Invoked";

            this._notificationMethodGen.AddMethodFor(methodInvokingName, method.Parameters, null);
            this._notificationMethodGen.AddMethodFor(methodInvokedName, null);

            method.Statements.Add(this._notificationMethodGen.GetMethodInvokeExpressionStatementFor(methodInvokingName));
            method.Statements.Add(
                new CodeExpressionStatement(
                    new CodeMethodInvokeExpression(
                        new CodeBaseReferenceExpression(), "InvokeAction", invokeParams.ToArray())));
            method.Statements.Add(this._notificationMethodGen.GetMethodInvokeExpressionStatementFor(methodInvokedName));

            this._proxyClass.Members.Add(method);

            // ----------------------------------------------------------------
            // generate Is<CustomMethod>Invoked property:
            // [Display(AutoGenerateField=false)]
            // public bool IsMyCustomMethodInvoked { get { base.IsActionInvoked(methodName);}}
            // ----------------------------------------------------------------
            CodeMemberProperty invokedProperty = new CodeMemberProperty();

            invokedProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            invokedProperty.HasGet     = true;
            invokedProperty.HasSet     = false;
            invokedProperty.Type       = new CodeTypeReference(typeof(bool));
            invokedProperty.GetStatements.Add(new CodeMethodReturnStatement(
                                                  new CodeMethodInvokeExpression(
                                                      new CodeBaseReferenceExpression(),
                                                      "IsActionInvoked",
                                                      new CodeExpression[] { new CodeArgumentReferenceExpression("\"" + customMethodName + "\"") })));
            invokedProperty.Name = GetIsInvokedPropertyName(customMethodName);

            // Generate <summary> doc comment
            comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_IsInvoked_Property_Summary_Comment, customMethodName);
            invokedProperty.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            CodeAttributeDeclaration displayAttribute = CodeGenUtilities.CreateDisplayAttributeDeclaration(this.ClientProxyGenerator, this._proxyClass);

            invokedProperty.CustomAttributes.Add(displayAttribute);

            this._proxyClass.Members.Add(invokedProperty);
        }
Пример #6
0
        /// <summary>
        /// Creates a new <see cref="CodeTypeDeclaration"/> that is the generated form of
        /// the given <paramref name="enumType"/>.
        /// </summary>
        /// <param name="enumType">The enum type to generate.</param>
        /// <param name="codeGenerator">The current proxy generator context.</param>
        /// <returns>The newly generated enum type declaration.</returns>
        internal static CodeTypeDeclaration CreateEnumTypeDeclaration(Type enumType, CodeDomClientCodeGenerator codeGenerator)
        {
            System.Diagnostics.Debug.Assert(enumType.IsEnum, "Type must be an enum type");

            CodeTypeDeclaration typeDecl = CodeGenUtilities.CreateTypeDeclaration(enumType);

            typeDecl.IsEnum = true;

            // Always force generated enums to be public
            typeDecl.TypeAttributes |= TypeAttributes.Public;

            // Enums deriving from anything but int get an explicit base type
            Type underlyingType = enumType.GetEnumUnderlyingType();

            if (underlyingType != typeof(int))
            {
                typeDecl.BaseTypes.Add(new CodeTypeReference(underlyingType));
            }

            typeDecl.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment($"Enum {enumType.Name}", codeGenerator.IsCSharp));

            // Generate [DataContract] if it appears in the original only.  Use Reflection only because that matches
            // what WCF will do.
            DataContractAttribute dataContractAttr = (DataContractAttribute)Attribute.GetCustomAttribute(enumType, typeof(DataContractAttribute));

            if (dataContractAttr != null)
            {
                CodeAttributeDeclaration attrDecl = CodeGenUtilities.CreateDataContractAttributeDeclaration(enumType, codeGenerator, typeDecl);
                typeDecl.CustomAttributes.Add(attrDecl);
            }

            string[] memberNames   = Enum.GetNames(enumType);
            Type     enumValueType = Enum.GetUnderlyingType(enumType);

            for (int i = 0; i < memberNames.Length; ++i)
            {
                string            memberName  = memberNames[i];
                CodeTypeReference enumTypeRef = CodeGenUtilities.GetTypeReference(enumValueType, codeGenerator, typeDecl);
                CodeMemberField   enumMember  = new CodeMemberField(enumTypeRef, memberName);

                enumMember.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(memberName, codeGenerator.IsCSharp));

                // Generate an initializer for the enum member.
                // GetRawConstantValue is the safest way to get the raw value of the enum field
                // and works for both Reflection and ReflectionOnly loaded assemblies.
                FieldInfo fieldInfo = enumType.GetField(memberName);
                if (fieldInfo != null)
                {
                    object memberValue = fieldInfo.GetRawConstantValue();

                    Debug.Assert(memberValue != null, "Enum type's GetRawConstantValue should never return null");

                    // We special-case MinValue and MaxValue for the integral types
                    // because VisualBasic will generate overflow compiler error for
                    // Int64.MinValue.   If we detect a known MinValue or MaxValue for
                    // this integral type, we generate that reference, otherwise we
                    // just generate a constant integral value of the enum's type
                    object[] minMaxValues = null;
                    CodeGenUtilities.integralMinMaxValues.TryGetValue(underlyingType, out minMaxValues);
                    Debug.Assert(minMaxValues == null || minMaxValues.Length == 3, "integralMinMaxValues elements must always contain 3 values");

                    // Gen xxx.MinValue if it matches, but give precedence to matching a true zero,
                    // which is the min value for the unsigned integral types
                    // minMaxValues[0]: the MinValue for this type
                    // minMaxValues[1]: the MaxValue for this type
                    // minMaxValues[2]: the zero for this type (memberValue is not boxed and cannot be cast)
                    if (minMaxValues != null && !memberValue.Equals(minMaxValues[2]) && memberValue.Equals(minMaxValues[0]))
                    {
                        enumMember.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(underlyingType), "MinValue");
                    }
                    // Gen xxx.MaxValue if it matches
                    else if (minMaxValues != null && memberValue.Equals(minMaxValues[1]))
                    {
                        enumMember.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(underlyingType), "MaxValue");
                    }
                    // All other cases generate an integral constant.
                    // CodeDom knows how to generate the right integral constant based on memberValue's type.
                    else
                    {
                        enumMember.InitExpression = new CodePrimitiveExpression(memberValue);
                    }
                }

                typeDecl.Members.Add(enumMember);

                // Generate an [EnumMember] if appropriate
                EnumMemberAttribute enumMemberAttr = (EnumMemberAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(EnumMemberAttribute));
                if (enumMemberAttr != null)
                {
                    CodeAttributeDeclaration enumAttrDecl = CodeGenUtilities.CreateEnumMemberAttributeDeclaration(fieldInfo, codeGenerator, typeDecl);
                    enumMember.CustomAttributes.Add(enumAttrDecl);
                }

                // Propagate any other attributes that can be seen by the client
                CustomAttributeGenerator.GenerateCustomAttributes(
                    codeGenerator,
                    typeDecl,
                    ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, fieldInfo.Name, typeDecl.Name, ex.InnerException.Message),
                    fieldInfo.GetCustomAttributes(false).Cast <Attribute>().Where(a => a.GetType() != typeof(EnumMemberAttribute)),
                    enumMember.CustomAttributes,
                    enumMember.Comments);
            }

            // Attributes marked with [Flag] propagate it
            if (enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0)
            {
                CodeAttributeDeclaration attrDecl = CodeGenUtilities.CreateAttributeDeclaration(typeof(FlagsAttribute), codeGenerator, typeDecl);
                typeDecl.CustomAttributes.Add(attrDecl);
            }
            return(typeDecl);
        }
Пример #7
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);
        }