Example #1
0
        /// <summary>
        /// Generates a domain method on the domain service.
        /// </summary>
        /// <param name="domainMethod">The domain method to generate code for.</param>
        private void GenerateDomainOperationEntry(DomainOperationEntry domainMethod)
        {
            // ----------------------------------------------------------------
            // Method decl
            // ----------------------------------------------------------------
            CodeMemberMethod method = new CodeMemberMethod();

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

            // ----------------------------------------------------------------
            // generate domain method body:
            //    entity.<methodName>(params);
            // ----------------------------------------------------------------
            List <CodeExpression> invokeParams = new List <CodeExpression>();

            // The domain method parameter list is the same as the domain operation entries.
            DomainOperationParameter[] paramInfos = domainMethod.Parameters.ToArray();

            // Generate the <summary> and <param> doc comments
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainClient_Custom_Method_Summary_Comment, domainMethod.Name, domainMethod.AssociatedType.Name);

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

            // Generate <param> doc comment for all the parameters
            // The first param is the entity
            comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Custom_Method_Entity_Parameter_Comment, domainMethod.AssociatedType.Name);
            method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(paramInfos[0].Name, comment, this.ClientProxyGenerator.IsCSharp));

            // All subsequent params
            for (int i = 1; i < paramInfos.Length; ++i)
            {
                comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Custom_Method_Parameter_Comment, paramInfos[i].Name);
                method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(paramInfos[i].Name, comment, this.ClientProxyGenerator.IsCSharp));
            }

            // Create an expression for each parameter
            for (int i = 0; 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);

                method.Parameters.Add(paramDecl);

                // Skip the entity parameter.
                if (i > 0)
                {
                    // build up the invoke call parameters
                    invokeParams.Add(new CodeVariableReferenceExpression(paramInfo.Name));
                }
            }

            method.Statements.Add(
                new CodeExpressionStatement(
                    new CodeMethodInvokeExpression(
                        new CodeVariableReferenceExpression(paramInfos[0].Name), domainMethod.Name, invokeParams.ToArray())));

            this._proxyClass.Members.Add(method);
        }
Example #2
0
        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);
        }
Example #3
0
 /// <summary>
 /// Generates a (potentially multi-line) Xml doc comment for a Summary element.
 /// </summary>
 /// <param name="comment">The formatted string to embed within a Summary element.  If it contains line breaks, it will become multiple comments.</param>
 /// <param name="isCSharp">Whether or not the doc comment is for C#.</param>
 /// <returns>The collection of generated Xml doc comments.</returns>
 internal static CodeCommentStatementCollection GenerateSummaryCodeComment(string comment, bool isCSharp)
 {
     System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(comment), "comment cannot be empty");
     return(CodeGenUtilities.GetDocComments("<summary>" + Environment.NewLine + comment + Environment.NewLine + "</summary>", isCSharp));
 }
Example #4
0
 /// <summary>
 /// Generates a (potentially multi-line) Xml doc comment for a Returns element.
 /// </summary>
 /// <param name="comment">The formatted string to embed in the Returns element.</param>
 /// <param name="isCSharp">Whether or not the doc comment is for C#.</param>
 /// <returns>The collection of generated Xml doc comments.</returns>
 internal static CodeCommentStatementCollection GenerateReturnsCodeComment(string comment, bool isCSharp)
 {
     System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(comment), "comment cannot be empty");
     return(CodeGenUtilities.GetDocComments("<returns>" + comment + "</returns>", isCSharp));
 }
Example #5
0
        /// <summary>
        /// Gets a <see cref="CodeTypeReference"/> for a CLR type.
        /// </summary>
        /// <param name="type">A CLR type.</param>
        /// <param name="codeGenerator">A <see cref="CodeDomClientCodeGenerator"/>.</param>
        /// <param name="referencingType">The referencing type.</param>
        /// <param name="optimizeAttributeName">Indicates whether or not to optimize <see cref="Attribute"/> names by removing the "Attribute" suffix.</param>
        /// <param name="forceUseFullyQualifiedName">Indicates whether or not to generate the type using the fully qualified name irrespective the global setting.</param>
        /// <returns>A <see cref="CodeTypeReference"/> for a CLR type.</returns>
        internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType, bool optimizeAttributeName, bool forceUseFullyQualifiedName)
        {
            string typeName      = type.Name;
            string typeNamespace = type.Namespace;

            // Add an import statement to the referencing type if needed
            CodeNamespace     ns = codeGenerator.GetNamespace(referencingType);
            CodeTypeReference codeTypeReference = null;

            // Attribute?  If so, we special case these and remove the 'Attribute' suffix if present.
            if (optimizeAttributeName)
            {
                typeName = OptimizeAttributeName(type);
            }

            // Determine if we should generate this type with a full type name
            bool useFullyQualifiedName = forceUseFullyQualifiedName || CodeGenUtilities.useFullTypeNames || RegisterTypeName(typeNamespace, typeName, ns.Name);

            // Make sure we take into account root namespace in VB codegen.
            typeNamespace = TranslateNamespace(type, codeGenerator);

            // Conditionally add an import statement.  Skip this step if we need to generate a full
            // type name, if we're already in the target namespace, or if the type is in the global namespace.
            if (!useFullyQualifiedName && !ns.Name.Equals(type.Namespace) && !string.IsNullOrEmpty(type.Namespace))
            {
                // If the namespace is already imported, the following line will be a no-op.
                ns.Imports.Add(new CodeNamespaceImport(typeNamespace));
            }

            // If forced using Fully Qualified names, dont look up or store the code reference in the cache. That is because,
            // we force the use of fully qualified names only in certain cases. Caching at this time will cause the fully qualified name
            // to be used every time.
            bool useCache = !forceUseFullyQualifiedName;

            // See if we already have a reference for this type
            Tuple <CodeNamespace, Type> tupleKey = new Tuple <CodeNamespace, Type>(ns, type);

            if (!useCache || !CodeGenUtilities.codeTypeReferences.TryGetValue(tupleKey, out codeTypeReference))
            {
                if (useFullyQualifiedName && !string.IsNullOrEmpty(typeNamespace))
                {
                    // While this splicing may seem awkward, we perform this task
                    // rather than rely on 'type.FullName' as we may have performed
                    // a VB root namespace translation task above.
                    typeName = typeNamespace + "." + typeName;
                }

                // If not, create a new type reference. Use the constructor for CodeTypeReference
                // that takes a type's name rather than type to generate short names.
                if (type.IsArray)
                {
                    codeTypeReference = new CodeTypeReference(
                        CodeGenUtilities.GetTypeReference(type.GetElementType(), codeGenerator, referencingType, /* optimizeAttributeName */ false, forceUseFullyQualifiedName),
                        type.GetArrayRank());
                }
                else if (type.IsGenericType)
                {
                    Type[] genericArguments           = type.GetGenericArguments();
                    CodeTypeReference[] typeArguments = new CodeTypeReference[genericArguments.Length];
                    for (int i = 0; i < genericArguments.Length; i++)
                    {
                        typeArguments[i] = GetTypeReference(genericArguments[i], codeGenerator, referencingType);
                    }
                    codeTypeReference = new CodeTypeReference(typeName, typeArguments);
                }
                else
                {
                    // Generate language-specific shorthands for core types by using CodeTypeReference constructor that takes a Type
                    if (type.IsPrimitive || type == typeof(void) || type == typeof(decimal) || type == typeof(string) || type == typeof(object))
                    {
                        codeTypeReference = new CodeTypeReference(type);
                    }
                    else
                    {
                        codeTypeReference = new CodeTypeReference(typeName);
                    }
                }

                // Keep track of the CLR type for identification purposes.
                codeTypeReference.UserData["ClrType"] = type;

                // Cache for later use.
                if (useCache)
                {
                    CodeGenUtilities.codeTypeReferences.Add(tupleKey, codeTypeReference);
                }
            }

            return(codeTypeReference);
        }
Example #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);
        }
Example #7
0
        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"));
        }
Example #8
0
        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"));
        }
Example #9
0
        /// <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;
        }
Example #10
0
        private void GenerateConstructors(CodeTypeDeclaration proxyClass, CodeTypeDeclaration contractInterface, EnableClientAccessAttribute enableClientAccessAttribute, CodeMethodInvokeExpression onCreatedExpression)
        {
            CodeTypeReference uriTypeRef     = CodeGenUtilities.GetTypeReference(typeof(Uri), this.ClientProxyGenerator, proxyClass);
            CodeTypeReference uriKindTypeRef = CodeGenUtilities.GetTypeReference(typeof(UriKind), this.ClientProxyGenerator, proxyClass);

            string            containingNamespace   = proxyClass.UserData["Namespace"] as string;
            CodeTypeReference contractTypeParameter =
                CodeGenUtilities.GetTypeReference(
                    containingNamespace + "." + proxyClass.Name + "." + contractInterface.Name,
                    containingNamespace,
                    true);

            // construct relative URI
            string         relativeServiceUri    = string.Format(CultureInfo.InvariantCulture, "{0}.svc", this._domainServiceDescription.DomainServiceType.FullName.Replace('.', '-'));
            CodeExpression relativeUriExpression = new CodeObjectCreateExpression(
                uriTypeRef,
                new CodePrimitiveExpression(relativeServiceUri),
                new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(uriKindTypeRef), "Relative"));

            // ----------------------------------------------------------------
            // Default ctor decl (using relative URI)
            // ----------------------------------------------------------------

            // ctor parameters
            List <CodeParameterDeclarationExpression> ctorParams = null;

            // base params
            List <CodeExpression> baseParams = new List <CodeExpression>(1);

            baseParams.Add(relativeUriExpression);

            // add <summary> doc comments
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Default_Constructor_Summary_Comments, proxyClass.Name);
            CodeCommentStatementCollection comments = CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp);

            // <comments>...</comments>
            // public .ctor() : this(new Uri("Foo-Bar.svc", UriKind.Relative))
            GenerateConstructor(proxyClass, ctorParams, baseParams, comments, false);

            // ----------------------------------------------------------------
            // DomainContext(System.Uri serviceUri) ctor decl
            // ----------------------------------------------------------------

            // ctor params
            ctorParams = new List <CodeParameterDeclarationExpression>(1);
            ctorParams.Add(new CodeParameterDeclarationExpression(uriTypeRef, "serviceUri"));

            // add <summary> and <param> comments
            comments = CodeGenUtilities.GenerateSummaryCodeComment(string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Summary_ServiceUri, proxyClass.Name), this.ClientProxyGenerator.IsCSharp);
            comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("serviceUri", string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Param_ServiceUri, this._domainServiceDescription.DomainServiceType.Name), this.ClientProxyGenerator.IsCSharp));

            // <comments>...</comments>
            // public .ctor(Uri serviceUri) : this(DomainContext.CreateDomainClient(typeof(TContract), serviceUri, true/false))

            // ctor base parameters
            baseParams = new List <CodeExpression>(1);
            CodeTypeReference domainContextRef = CodeGenUtilities.GetTypeReference(TypeConstants.DomainContextTypeFullName, proxyClass.UserData["Namespace"] as string, false);

            baseParams.Add(new CodeMethodInvokeExpression(
                               new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(domainContextRef), "CreateDomainClient"),
                               new CodeTypeOfExpression(contractTypeParameter),
                               new CodeArgumentReferenceExpression("serviceUri"),
                               new CodePrimitiveExpression(enableClientAccessAttribute.RequiresSecureEndpoint)));

            GenerateConstructor(proxyClass, ctorParams, baseParams, comments, false);

            // -----------------------------------------------------------------------
            // DomainContext(DomainClient domainClient) ctor decl
            // -----------------------------------------------------------------------

            // ctor parameters --[(DomainClient domainClient)]
            ctorParams = new List <CodeParameterDeclarationExpression>(1);
            ctorParams.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(TypeConstants.DomainClientTypeFullName, proxyClass.UserData["Namespace"] as string, false), "domainClient"));

            // parameters to invoke on base -- [: base(domainClient)]
            baseParams = new List <CodeExpression>(1);
            baseParams.Add(new CodeArgumentReferenceExpression("domainClient"));

            // add <summary> and <param> comments
            comments = CodeGenUtilities.GenerateSummaryCodeComment(string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Summary_DomainClientAccumulating, proxyClass.Name), this.ClientProxyGenerator.IsCSharp);
            comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("domainClient", string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Param_DomainClient), this.ClientProxyGenerator.IsCSharp));

            // <comments>...</comments>
            // public .ctor(DomainClient domainClient) : base(domainClient)]
            CodeConstructor proxyCtor = GenerateConstructor(proxyClass, ctorParams, baseParams, comments, true);

            proxyCtor.Statements.Add(onCreatedExpression);
        }
Example #11
0
        //// TODO: we need to refactor this to comply with FxCop
        private CodeTypeDeclaration GenEntityContainerInnerClass(CodeTypeDeclaration proxyClass, IEnumerable <Type> entityTypes, DomainServiceDescription domainServiceDescription)
        {
            // ----------------------------------------------------------------
            // class xxxEntityContainer : EntityContainer
            // ----------------------------------------------------------------
            string containingNamespace = this.ClientProxyGenerator.GetNamespace(proxyClass).Name;
            var    innerClass          = CodeGenUtilities.CreateTypeDeclaration(proxyClass.Name + "EntityContainer", containingNamespace);

            innerClass.BaseTypes.Add(CodeGenUtilities.GetTypeReference(TypeConstants.EntityContainerTypeFullName, containingNamespace, false));
            innerClass.TypeAttributes = TypeAttributes.NotPublic | TypeAttributes.Sealed;
            proxyClass.Members.Add(innerClass);

            // ----------------------------------------------------------------
            // ctor
            // ----------------------------------------------------------------
            var ctor = new CodeConstructor();

            ctor.Attributes = MemberAttributes.Public;
            innerClass.Members.Add(ctor);

            // Convert to a set for faster lookups.
            HashSet <Type> entityTypesToUse = new HashSet <Type>();

            foreach (Type entityType in entityTypes)
            {
                entityTypesToUse.Add(entityType);
            }

            // ----------------------------------------------------------------
            // each entity type gets 'CreateEntitySet<entityType>()' statement in ctor
            // ----------------------------------------------------------------
            foreach (Type entityType in entityTypes.OrderBy(t => t.FullName))
            {
                // Skip entity types which have base classes.
                if (entityTypesToUse.Any(t => t != entityType && t.IsAssignableFrom(entityType)))
                {
                    continue;
                }

                // ----------------------------------------------------------------
                // Build EntitySetOperations enum value
                // ----------------------------------------------------------------
                var            enumTypeReference   = CodeGenUtilities.GetTypeReference(TypeConstants.EntitySetOperationsTypeFullName, containingNamespace, false);
                CodeExpression entitySetOperations = null;

                // Check to see what update operations are supported, and build up the EntitySetOperations flags expression
                bool canInsert = domainServiceDescription.IsOperationSupported(entityType, DomainOperation.Insert);
                bool canEdit   = domainServiceDescription.IsOperationSupported(entityType, DomainOperation.Update);
                bool canDelete = domainServiceDescription.IsOperationSupported(entityType, DomainOperation.Delete);

                CodeTypeReferenceExpression enumTypeReferenceExp = new CodeTypeReferenceExpression(enumTypeReference);

                if (!canInsert && !canEdit && !canDelete)
                {
                    // if no update operations are supported, set to 'None'
                    entitySetOperations = new CodeFieldReferenceExpression(enumTypeReferenceExp, "None");
                }
                else if (canInsert && canEdit && canDelete)
                {
                    // if all operations are supported, set to 'All'
                    entitySetOperations = new CodeFieldReferenceExpression(enumTypeReferenceExp, "All");
                }
                else
                {
                    if (canInsert)
                    {
                        entitySetOperations = new CodeFieldReferenceExpression(enumTypeReferenceExp, "Add");
                    }
                    if (canEdit)
                    {
                        CodeFieldReferenceExpression setOp = new CodeFieldReferenceExpression(enumTypeReferenceExp, "Edit");
                        if (entitySetOperations == null)
                        {
                            entitySetOperations = setOp;
                        }
                        else
                        {
                            entitySetOperations = new CodeBinaryOperatorExpression(entitySetOperations, CodeBinaryOperatorType.BitwiseOr, setOp);
                        }
                    }
                    if (canDelete)
                    {
                        CodeFieldReferenceExpression setOp = new CodeFieldReferenceExpression(enumTypeReferenceExp, "Remove");
                        if (entitySetOperations == null)
                        {
                            entitySetOperations = setOp;
                        }
                        else
                        {
                            entitySetOperations = new CodeBinaryOperatorExpression(entitySetOperations, CodeBinaryOperatorType.BitwiseOr, setOp);
                        }
                    }
                }


                // ----------------------------------------------------------------
                // method call: this.CreateEntitySet<entityType>
                // ----------------------------------------------------------------
                var entityTypeReference = CodeGenUtilities.GetTypeReference(entityType, this.ClientProxyGenerator, proxyClass);
                var methodRef           = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), "CreateEntitySet", entityTypeReference);
                var methodCall          = new CodeMethodInvokeExpression(methodRef, entitySetOperations);
                ctor.Statements.Add(methodCall);
            }

            return(innerClass);
        }