public static AttributeDeclaration GetAttributeDeclaration(Attribute attribute, ClientCodeGenerator textTemplateClientCodeGenerator, bool forcePropagation)
        {
            Type attributeType = attribute.GetType();

            // Check if this attribute should be blocked
            if (IsAttributeBlocked(attributeType))
            {
                return(null);
            }

            ICustomAttributeBuilder cab = GetCustomAttributeBuilder(attributeType);
            AttributeDeclaration    attributeDeclaration = null;

            if (cab != null)
            {
                try
                {
                    attributeDeclaration = cab.GetAttributeDeclaration(attribute);
                }
                catch (AttributeBuilderException)
                {
                    return(null);
                }
                if (attributeDeclaration != null)
                {
                    if (!forcePropagation)
                    {
                        // Verify attribute's shared type|property|method requirements are met
                        ValidateAttributeDeclarationRequirements(attributeDeclaration, textTemplateClientCodeGenerator);
                    }
                }
            }

            return(attributeDeclaration);
        }
        /// <summary>
        /// Retrieves the appropriate custom attribute builder for a given attribute instance
        /// </summary>
        /// <param name="attributeType">The attribute type.  It cannot be null.</param>
        /// <returns>The custom attribute builder for it.</returns>
        private static ICustomAttributeBuilder GetCustomAttributeBuilder(Type attributeType)
        {
            if (attributeType == null)
            {
                throw new ArgumentNullException("attributeType");
            }

            ICustomAttributeBuilder cab = null;

            // We maintain a cache of known builder instances, created lazily
            if (KnownBuilders.TryGetValue(attributeType, out cab))
            {
                return(cab);
            }

            // Don't have a builder instance yet
            // See if we have a registered builder type for this attribute
            Type cabType = null;

            if (!KnownBuilderTypes.TryGetValue(attributeType, out cabType))
            {
                // Don't have an explicit builder -- see if we this attribute derives from
                // a known builder type and assume it is okay to use it.
                foreach (KeyValuePair <Type, Type> pair in KnownBuilderTypes)
                {
                    if (pair.Key.IsAssignableFrom(attributeType))
                    {
                        cabType = pair.Value;
                        break;
                    }
                }
            }

            // If don't have a builder -- see if the attribute is visible to the
            // client.  If so, we will attempt to build with our standard builder
            if (cabType == null)
            {
                cabType = typeof(StandardCustomAttributeBuilder);
            }

            // If we found a builder type, instantiate it now.  We'll reuse it
            cab = Activator.CreateInstance(cabType) as ICustomAttributeBuilder;

            // Don't cache null builders, because we may be re-using this cache for a next code-gen
            // run where we may have a different set of shared types.
            // TODO: We need to get rid of our static caches, because caches look different between
            //       builds of different DomainServices. E.g. for one DomainService an attribute may
            //       be shared, while in another it's not, and thus the KnownBuilders mapping is
            //       different between the two.
            //       Instead of static caches, we should consider storing state per build in a context
            //       object. The context object would not be shared against different builds of different
            //       DomainServices.
            if (cab != null)
            {
                KnownBuilders[attributeType] = cab;
            }

            return(cab);
        }
Exemple #3
0
        /// <summary>
        /// Retrieves the appropriate custom attribute builder for a given attribute instance
        /// </summary>
        /// <param name="attributeType">The attribute type.  It cannot be null.</param>
        /// <returns>The custom attribute builder for it.</returns>
        private static ICustomAttributeBuilder GetCustomAttributeBuilder(Type attributeType)
        {
            if (attributeType == null)
            {
                throw new ArgumentNullException("attributeType");
            }

            ICustomAttributeBuilder cab = null;

            // We maintain a cache of known builder instances, created lazily
            if (KnownBuilders.TryGetValue(attributeType, out cab))
            {
                return(cab);
            }

            // Don't have a builder instance yet
            // See if we have a registered builder type for this attribute
            Type cabType = null;

            if (!KnownBuilderTypes.TryGetValue(attributeType, out cabType))
            {
                // Don't have an explicit builder -- see if we this attribute derives from
                // a known builder type and assume it is okay to use it.
                foreach (KeyValuePair <Type, Type> pair in KnownBuilderTypes)
                {
                    if (pair.Key.IsAssignableFrom(attributeType))
                    {
                        cabType = pair.Value;
                        break;
                    }
                }
            }

            // If don't have a builder -- see if the attribute is visible to the
            // client.  If so, we will attempt to build with our standard builder
            if (cabType == null)
            {
                cabType = typeof(StandardCustomAttributeBuilder);
            }

            // If we found a builder type, instantiate it now.  We'll reuse it
            if (cabType != null)
            {
                cab = Activator.CreateInstance(cabType) as ICustomAttributeBuilder;
            }

            // Don't cache null builders, because we may be re-using this cache for a next code-gen
            // run where we may have a different set of shared types.
            if (cab != null)
            {
                KnownBuilders[attributeType] = cab;
            }

            return(cab);
        }
        /// <summary>
        /// Generates code for the given set of custom attributes
        /// </summary>
        /// <param name="proxyGenerator">Root client proxy generator</param>
        /// <param name="referencingType">The referencing type</param>
        /// <param name="getLogWarningMessage">The function to call to get the warning message to be logged</param>
        /// <param name="attributes">Collection of attributes for which to generate code</param>
        /// <param name="comments">Collection of comments that should be updated if errors are discovered.</param>
        /// <param name="customCommentHeader">A custom comment header that will be displayed for any generated comment errors.</param>
        /// <param name="forcePropagation">Indicates whether or not to force attribute propagation.</param>
        /// <returns>The collection of generated attribute declarations corresponding to <paramref name="attributes"/></returns>
        private static IEnumerable <CodeAttributeDeclaration> GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, Func <AttributeBuilderException, string> getLogWarningMessage, IEnumerable <Attribute> attributes, CodeCommentStatementCollection comments, string customCommentHeader, bool forcePropagation)
        {
            bool emittedErrorCommentHeader         = false;
            List <CodeAttributeDeclaration> result = new List <CodeAttributeDeclaration>(attributes.Count());

            // Enumerate over attributes sorted by name.  Here, we sort by name to ensure that our
            // generated baselines (including possible error comments!) are ordered consistently.
            foreach (Attribute attribute in attributes.OrderBy(a => a.GetType().Name))
            {
                Type attributeType = attribute.GetType();

                // Check if this attribute should be blocked
                if (IsAttributeBlocked(attributeType))
                {
                    continue;
                }

                bool attributePropagated        = false;
                bool isDataAnnotationsAttribute = string.Equals(attributeType.Namespace, typeof(ValidationAttribute).Namespace, StringComparison.Ordinal);

                ICustomAttributeBuilder cab = GetCustomAttributeBuilder(attributeType);

                if (cab != null)
                {
                    AttributeDeclaration attributeDeclaration = null;
                    // If the attempt to build the attribute fails, log a clean error.
                    // One common exception path is InvalidOperationException arising from
                    // attributes that have been improperly constructed (see DisplayAttribute)
                    try
                    {
                        attributeDeclaration = cab.GetAttributeDeclaration(attribute);
                    }
                    catch (AttributeBuilderException attributeBuilderException)
                    {
                        // Ensure we've generated the attribute generation failure error header
                        GenerateCustomAttributesErrorCommentHeader(comments, customCommentHeader, ref emittedErrorCommentHeader);

                        // Generate comments stating the attribute couldn't be generated
                        comments.AddRange(ConstructCodeAttributeFailureComments(attributeBuilderException.Message));

                        // Log the build warning if a method was specified to get the warning message
                        if (getLogWarningMessage != null)
                        {
                            string warningMessage = getLogWarningMessage(attributeBuilderException);
                            proxyGenerator.LogWarning(warningMessage);
                        }

                        // Move on to the next attribute
                        continue;
                    }

                    // Null is acceptable indicator that code-gen was not possible.
                    if (attributeDeclaration != null)
                    {
                        if (!forcePropagation)
                        {
                            // Verify attribute's shared type|property|method requirements are met
                            ValidateAttributeDeclarationRequirements(proxyGenerator, attributeDeclaration);
                        }

                        if (attributeDeclaration.HasErrors)
                        {
                            // Only generate comments if the attribute is a DataAnnotations attribute
                            if (isDataAnnotationsAttribute)
                            {
                                // Ensure we've generated the attribute generation failure error header
                                GenerateCustomAttributesErrorCommentHeader(comments, customCommentHeader, ref emittedErrorCommentHeader);

                                // Generate attribute and an error message as comments
                                comments.AddRange(ConstructCodeAttributeFailureComments(proxyGenerator, attributeDeclaration));
                            }
                        }
                        else
                        {
                            // Generate the attribute declaration
                            CodeAttributeDeclaration codeAttributeDeclaration = CreateCodeAttributeDeclaration(proxyGenerator, referencingType, attributeDeclaration);
                            result.Add(codeAttributeDeclaration);
                            attributePropagated = true;
                        }
                    }
                }

                // We generate VS warnings in certain scenarios:
                //  - A DataAnnotation attribute type was not available on the client, user needs to add a reference.
                //  - An attribute subclassed ValidationAttribute (custom or framework) and we couldn't build it.
                if (!attributePropagated)
                {
                    // Was it a DA attribute that wasn't available?  If so, log a warning.
                    if (isDataAnnotationsAttribute)
                    {
                        CodeMemberShareKind shareKind = proxyGenerator.GetTypeShareKind(attributeType);
                        if (shareKind == CodeMemberShareKind.NotShared)
                        {
                            // Indicate that a reference to 'System.ComponentModel.DataAnnotations' is required.
                            proxyGenerator.LogWarning(
                                string.Format(
                                    CultureInfo.CurrentCulture,
                                    Resource.ClientCodeGen_Attribute_RequiresDataAnnotations,
                                    attributeType,
                                    proxyGenerator.ClientProjectName));
                        }
                    }
                    // Was it a validation attribute that we couldn't build?  If so, log a warning.
                    else if (cab == null && typeof(ValidationAttribute).IsAssignableFrom(attributeType))
                    {
                        // Indicate that a builder was not found, attribute does not meet heuristics.
                        proxyGenerator.LogWarning(
                            string.Format(
                                CultureInfo.CurrentCulture,
                                Resource.ClientCodeGen_Attribute_RequiresBuilder,
                                attributeType));
                    }
                }
            }

            // Issue -- CodeDom outputs the attributes in the order they are generated.
            // To allow consistent output for easy baseline comparisons, sort the list.
            result.Sort(new Comparison <CodeAttributeDeclaration>((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal)));
            return(result);
        }