/// <summary> /// In cases where we are emitting types that aren't already in the root namespace, /// we need to translate the generated type namespace so that it resides in the /// root namespace. /// </summary> /// <remarks> /// Here, we're interested in cases where we're generating VB code /// and our target project has a non-null root namespace. /// </remarks> /// <param name="type">The type who namespace should be translated</param> /// <param name="codeGenerator">The current proxy generator</param> /// <returns>A translated namespace string</returns> internal static string TranslateNamespace(Type type, CodeDomClientCodeGenerator codeGenerator) { // Set default namespace string typeNamespace; if (!typeNamespaceTranslations.TryGetValue(type, out typeNamespace)) { // Set the appropriate namespace if (NeedToPrefaceRootNamespace(type, codeGenerator)) { typeNamespace = string.IsNullOrEmpty(type.Namespace) ? rootNamespace : (rootNamespace + "." + type.Namespace); } else { typeNamespace = type.Namespace; } // Cache the value for next time typeNamespaceTranslations[type] = typeNamespace; } return(typeNamespace); }
/// <summary> /// Initializes a new instance of the <see cref="DataContractProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code. Cannot be <c>null</c>.</param> /// <param name="type">The type to generate. Cannot be null.</param> /// <param name="typeMapping">A dictionary of <see cref="DomainService"/> and related types that maps to their corresponding client-side <see cref="CodeTypeReference"/> representations.</param> protected DataContractProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, Type type, IDictionary<Type, CodeTypeDeclaration> typeMapping) : base(proxyGenerator) { this.Type = type; this._typeMapping = typeMapping; this.NotificationMethodGen = new NotificationMethodGenerator(proxyGenerator); this._isRoundtripType = type.Attributes()[typeof(RoundtripOriginalAttribute)] != null; }
/// <summary> /// Initializes a new instance of the <see cref="CustomMethodProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code. Cannot be null.</param> /// <param name="proxyClass">Entity <see cref="CodeTypeDeclaration"/> into which to generate code</param> /// <param name="entityType">The type of the entity. Cannot be null.</param> /// <param name="domainServiceDescriptions">Collection of all <see cref="DomainServiceDescription"/>s defined in this project</param> /// <param name="notificationMethodGen">Code generator for OnMethodName() methods</param> public CustomMethodProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration proxyClass, Type entityType, ICollection<DomainServiceDescription> domainServiceDescriptions, NotificationMethodGenerator notificationMethodGen) : base(proxyGenerator) { this._entityType = entityType; this._proxyClass = proxyClass; this._domainServiceDescriptions = domainServiceDescriptions; this._notificationMethodGen = notificationMethodGen; }
/// <summary> /// Initializes a new instance of the <see cref="CustomMethodProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code. Cannot be null.</param> /// <param name="proxyClass">Entity <see cref="CodeTypeDeclaration"/> into which to generate code</param> /// <param name="entityType">The type of the entity. Cannot be null.</param> /// <param name="domainServiceDescriptions">Collection of all <see cref="DomainServiceDescription"/>s defined in this project</param> /// <param name="notificationMethodGen">Code generator for OnMethodName() methods</param> public CustomMethodProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration proxyClass, Type entityType, ICollection <DomainServiceDescription> domainServiceDescriptions, NotificationMethodGenerator notificationMethodGen) : base(proxyGenerator) { this._entityType = entityType; this._proxyClass = proxyClass; this._domainServiceDescriptions = domainServiceDescriptions; this._notificationMethodGen = notificationMethodGen; }
/// <summary> /// Initializes a new instance of the <see cref="DataContractProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code. Cannot be <c>null</c>.</param> /// <param name="type">The type to generate. Cannot be null.</param> /// <param name="typeMapping">A dictionary of <see cref="DomainService"/> and related types that maps to their corresponding client-side <see cref="CodeTypeReference"/> representations.</param> protected DataContractProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, Type type, IDictionary <Type, CodeTypeDeclaration> typeMapping) : base(proxyGenerator) { this.Type = type; this._typeMapping = typeMapping; this.NotificationMethodGen = new NotificationMethodGenerator(proxyGenerator); this._isRoundtripType = type.Attributes()[typeof(RoundtripOriginalAttribute)] != null; }
/// <summary> /// Generates code for the given custom attributes and adds them to the given <see cref="CodeAttributeDeclarationCollection"/> /// </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 to generate</param> /// <param name="outputCollection">The collection to which the generated attributes will be added</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> public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, Func <AttributeBuilderException, string> getLogWarningMessage, IEnumerable <Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments, string customCommentHeader, bool forcePropagation) { IEnumerable <CodeAttributeDeclaration> cads = GenerateCustomAttributes(proxyGenerator, referencingType, getLogWarningMessage, attributes, comments, customCommentHeader, forcePropagation); foreach (var cad in cads) { outputCollection.Add(cad); } }
/// <summary> /// Creates the CodeDom CodeExpression for the given value. Returns null if unable to generate a CodeExpression. /// </summary> /// <remarks>This method exists solely to help generate code for all object types that can appear in an /// attribute declaration, such as typeof()</remarks> /// <param name="proxyGenerator">The context for generating code. It cannot be null.</param> /// <param name="referencingType">The referencing type</param> /// <param name="value">The value. Null is permitted.</param> /// <returns>The code expression</returns> private static CodeExpression CreateCodeExpression(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, object value) { Type typeOfValue = value == null ? null : value.GetType(); if (value == null || typeOfValue.IsPrimitive || value is string) { CodeExpression e = new CodePrimitiveExpression(value); // Workaround CodeDom issue -- it looks like CodePrimitiveExpression is fooled and generates double // literals as integers when there is no fraction. We take a general strategy of forcing an explicit // compile time cast to ensure we recompile the same type. if (value != null && (value is double || value is float)) { e = new CodeCastExpression(value.GetType(), e); } return(e); } // typeof(T) requires special handling Type valueAsType = value as Type; if (valueAsType != null) { // Verify the type is shared // Don't know counts as not shared CodeMemberShareKind shareKind = proxyGenerator.GetTypeShareKind(valueAsType); if ((shareKind & CodeMemberShareKind.Shared) == 0) { // Here we return a fully-qualified type name to ensure we don't cause compilation // errors by adding invalid 'using' statements into our codedom graph. CodeTypeReference valueTypeReference = CodeGenUtilities.GetTypeReference(valueAsType, proxyGenerator, referencingType, false, /*Use fully qualified name*/ true); valueTypeReference.Options = CodeTypeReferenceOptions.GlobalReference; return(new CodeTypeOfExpression(valueTypeReference)); } return(new CodeTypeOfExpression(CodeGenUtilities.GetTypeReference(valueAsType, proxyGenerator, referencingType))); } // Enum values need special handling if (typeOfValue.IsEnum) { string enumValueName = Enum.GetName(typeOfValue, value); string enumTypeName; if (proxyGenerator.ClientProxyCodeGenerationOptions.UseFullTypeNames) { enumTypeName = typeOfValue.FullName; } else { enumTypeName = typeOfValue.Name; } return(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(enumTypeName), enumValueName)); } return(null); }
/// <summary> /// Generates code for the given custom attributes and adds them to the given <see cref="CodeAttributeDeclarationCollection"/> /// </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 to generate</param> /// <param name="outputCollection">The collection to which the generated attributes will be added</param> /// <param name="comments">Collection of comments that should be updated if errors are discovered.</param> public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, Func <AttributeBuilderException, string> getLogWarningMessage, IEnumerable <Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments) { GenerateCustomAttributes( proxyGenerator, referencingType, getLogWarningMessage, attributes, outputCollection, comments, Resource.ClientCodeGen_Attribute_FailedToGenerate); }
/// <summary> /// Creates a <see cref="CodeAttributeDeclaration"/> for a <see cref="DisplayAttribute"/>. /// </summary> /// <param name="codeGenerator">The client proxy generator</param> /// <param name="referencingType">The type on whose member <see cref="DisplayAttribute"/> will be applied</param> /// <returns>The new attribute declaration</returns> internal static CodeAttributeDeclaration CreateDisplayAttributeDeclaration(CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { CodeAttributeDeclaration displayAttributeDeclaration = CodeGenUtilities.CreateAttributeDeclaration( typeof(DisplayAttribute), codeGenerator, referencingType); displayAttributeDeclaration.Arguments.Add(new CodeAttributeArgument("AutoGenerateField", new CodePrimitiveExpression(false))); return(displayAttributeDeclaration); }
/// <summary> /// Generates code for the given custom attributes and adds them to the given <see cref="CodeAttributeDeclarationCollection"/> /// </summary> /// <param name="proxyGenerator">Root client proxy generator</param> /// <param name="referencingType">The referencing type</param> /// <param name="attributes">Collection of attributes to generate</param> /// <param name="outputCollection">The collection to which the generated attributes will be added</param> /// <param name="comments">Collection of comments that should be updated if errors are discovered.</param> /// <param name="forcePropagation">Indicates whether or not to force attribute propagation.</param> public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, IEnumerable <Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments, bool forcePropagation) { GenerateCustomAttributes( proxyGenerator, referencingType, null, attributes, outputCollection, comments, Resource.ClientCodeGen_Attribute_FailedToGenerate, forcePropagation); }
/// <summary> /// Initializes a new instance of the <see cref="EntityProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code. Cannot be null.</param> /// <param name="entityType">The type of the entity. Cannot be null.</param> /// <param name="allDomainServiceDescriptions">Collection of all <see cref="DomainServiceDescription"/> defined in this project</param> /// <param name="typeMapping">A dictionary of <see cref="DomainService"/> and related entity types that maps to their corresponding client-side <see cref="CodeTypeReference"/> representations.</param> public EntityProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, Type entityType, ICollection<DomainServiceDescription> allDomainServiceDescriptions, IDictionary<Type, CodeTypeDeclaration> typeMapping) : base(proxyGenerator, entityType, typeMapping) { this._domainServiceDescriptionAggregate = new DomainServiceDescriptionAggregate(allDomainServiceDescriptions.Where(dsd => dsd.EntityTypes.Contains(entityType))); this._allDomainServiceDescriptions = allDomainServiceDescriptions; // Determine this entity's logical (visible) base type based on the type hierarchy // and list of known types. Null meant it was already the root. this._visibleBaseType = this._domainServiceDescriptionAggregate.GetEntityBaseType(this.Type); this._generateGetIdentity = !this.IsDerivedType; this._keyProperties = new List<PropertyDescriptor>(); }
internal void Initialize(ICodeGenerationHost host, IEnumerable <DomainServiceDescription> descriptions, ClientCodeGenerationOptions options) { if (host == null) { throw new ArgumentNullException(nameof(host)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } if (descriptions == null) { throw new ArgumentNullException(nameof(descriptions)); } // Initialize all the instance variables this._host = host; this._clientProxyCodeGenerationOptions = options; this._domainServiceDescriptions = descriptions.ToList(); this._compileUnit = new CodeCompileUnit(); this._namespaces = new Dictionary <string, CodeNamespace>(); this._enumTypesToGenerate = new HashSet <Type>(); CodeDomClientCodeGenerator.ValidateOptions(this._clientProxyCodeGenerationOptions); // Unconditionally initialize some options CodeGeneratorOptions cgo = new CodeGeneratorOptions(); cgo.IndentString = " "; cgo.VerbatimOrder = false; cgo.BlankLinesBetweenMembers = true; cgo.BracingStyle = "C"; this._options = cgo; // Choose the provider for the language. C# is the default if unspecified. string language = this.ClientProxyCodeGenerationOptions.Language; bool isCSharp = String.IsNullOrEmpty(language) || String.Equals(language, "C#", StringComparison.OrdinalIgnoreCase); this._provider = isCSharp ? (CodeDomProvider) new CSharpCodeProvider() : (CodeDomProvider) new VBCodeProvider(); // Configure our code gen utility package CodeGenUtilities.Initialize(!this.IsCSharp, this.ClientProxyCodeGenerationOptions.UseFullTypeNames, this.ClientProxyCodeGenerationOptions.ClientRootNamespace); }
/// <summary> /// Initializes a new instance of the <see cref="NotificationMethodGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The current <see cref="CodeDomClientCodeGenerator"/>.</param> /// <param name="indentLevel">The indentation level for the code to write.</param> public NotificationMethodGenerator(CodeDomClientCodeGenerator proxyGenerator, IndentationLevel indentLevel) { this.proxyGenerator = proxyGenerator; this.isCSharp = proxyGenerator.IsCSharp; int level = (int)indentLevel; if (level < 0) { level = (int)DefaultIndentLevel; } while (level-- > 0) { this.indent += IndentString; } this.AddMethodFor("Created", Resource.CommentOnCreated); // add default partial method. }
/// <summary> /// Determines if we need to preface the root namespace to the type /// </summary> /// <param name="type">The type in question</param> /// <param name="codeGenerator">The current proxy generator</param> /// <returns><c>true</c> if if we need to preface the root namespace to the type, <c>false</c> otherwise</returns> private static bool NeedToPrefaceRootNamespace(Type type, CodeDomClientCodeGenerator codeGenerator) { // System assemblies never preface with the root namespace if (type.Assembly.IsSystemAssembly()) { return(false); } bool isVbProjectWithRootNamespace = !codeGenerator.IsCSharp && !string.IsNullOrEmpty(codeGenerator.ClientProxyCodeGenerationOptions.ClientRootNamespace); bool typeIsGeneratedOnTheClient = (type.IsEnum && codeGenerator.NeedToGenerateEnumType(type)) || codeGenerator.DomainServiceDescriptions.Any(dsd => dsd.EntityTypes.Contains(type) || dsd.ComplexTypes.Contains(type) || dsd.DomainServiceType == type); bool typeNameStartsWithRootNamespace = string.Equals(type.Namespace, rootNamespace, StringComparison.Ordinal) || (!string.IsNullOrEmpty(type.Namespace) && type.Namespace.StartsWith(rootNamespace + ".", StringComparison.Ordinal)); return(isVbProjectWithRootNamespace && typeIsGeneratedOnTheClient && !typeNameStartsWithRootNamespace); }
/// <summary> /// Creates a <see cref="CodeAttributeDeclaration"/> for the given <see cref="AttributeDeclaration"/>. /// </summary> /// <param name="proxyGenerator">The context for generating code. It cannot be null.</param> /// <param name="referencingType">The referencing type.</param> /// <param name="attributeDeclaration">The <see cref="AttributeDeclaration"/> to build.</param> /// <returns>A <see cref="CodeAttributeDeclaration"/>.</returns> private static CodeAttributeDeclaration CreateCodeAttributeDeclaration(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, AttributeDeclaration attributeDeclaration) { CodeAttributeDeclaration codeAttributeDeclaration = CodeGenUtilities.CreateAttributeDeclaration(attributeDeclaration.AttributeType, proxyGenerator, referencingType); // Add ctor args foreach (object arg in attributeDeclaration.ConstructorArguments) { CodeExpression expression = CreateCodeExpression(proxyGenerator, referencingType, arg); codeAttributeDeclaration.Arguments.Add(new CodeAttributeArgument(expression)); } // Add named params foreach (KeyValuePair <string, object> pair in attributeDeclaration.NamedParameters) { CodeExpression expression = CreateCodeExpression(proxyGenerator, referencingType, pair.Value); codeAttributeDeclaration.Arguments.Add(new CodeAttributeArgument(pair.Key, expression)); } return(codeAttributeDeclaration); }
/// <summary> /// Generates an attribute declaration string. This is used in scenarios where an attribute declaration is needed /// in the form of a comment. CodeDOM does not support generation of standalone attributes. /// </summary> /// <param name="proxyGenerator">The context for generating code. It cannot be null.</param> /// <param name="attributeDeclaration">The <see cref="AttributeDeclaration"/> to represent.</param> /// <returns>An attribute declaration.</returns> private static string GenerateCodeAttribute(CodeDomClientCodeGenerator proxyGenerator, AttributeDeclaration attributeDeclaration) { StringBuilder result = new StringBuilder(); bool isCSharp = proxyGenerator.IsCSharp; result.Append(isCSharp ? '[' : '<'); result.Append(attributeDeclaration.AttributeType.Name); result.Append('('); // Add ctor args if (attributeDeclaration.ConstructorArguments.Count > 0) { foreach (object value in attributeDeclaration.ConstructorArguments) { result.Append(ConvertValueToCode(value, isCSharp)); result.Append(", "); } } // Add named params if (attributeDeclaration.NamedParameters.Count > 0) { foreach (KeyValuePair <string, object> pair in attributeDeclaration.NamedParameters) { result.Append(pair.Key); result.Append(isCSharp ? " = " : " := "); result.Append(ConvertValueToCode(pair.Value, isCSharp)); result.Append(", "); } } if (attributeDeclaration.ConstructorArguments.Count > 0 || attributeDeclaration.NamedParameters.Count > 0) { result.Remove(result.Length - 2, 2); } result.Append(')'); result.Append(isCSharp ? "]" : "> _"); return(result.ToString()); }
/// <summary> /// Creates an attribute declaration based on the specified attribute /// </summary> /// <param name="attributeType">type of the attribute</param> /// <param name="codeGenerator">A <see cref="CodeDomClientCodeGenerator"/>.</param> /// <param name="referencingType">The referencing type.</param> /// <returns>the attribute declaration</returns> internal static CodeAttributeDeclaration CreateAttributeDeclaration(Type attributeType, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { if (!typeof(Attribute).IsAssignableFrom(attributeType)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Resource.Type_Must_Be_Attribute, attributeType.Name), nameof(attributeType)); } CodeTypeReference attribute = GetTypeReference(attributeType, codeGenerator, referencingType, true); return(new CodeAttributeDeclaration(attribute)); }
/// <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> /// <returns>A <see cref="CodeTypeReference"/> for a CLR type.</returns> internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { return GetTypeReference(type, codeGenerator, referencingType, false, false); }
/// <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); }
/// <summary> /// Verifies that a <see cref="AttributeDeclaration"/>'s shared type requirements are met. /// </summary> /// <param name="proxyGenerator">The context for code generation</param> /// <param name="attributeDeclaration">The <see cref="AttributeDeclaration"/> to verify.</param> private static void ValidateAttributeDeclarationRequirements(CodeDomClientCodeGenerator proxyGenerator, AttributeDeclaration attributeDeclaration) { // Verify the attribute itself is shared. CodeMemberShareKind shareKind = proxyGenerator.GetTypeShareKind(attributeDeclaration.AttributeType); // If there is no PDB or this type has no human-authored code, we cannot determine // whether it is shared and get a null value. This requires a special message to // explain why we treat the type as not shared. if (shareKind == CodeMemberShareKind.Unknown) { attributeDeclaration.Errors.Add( string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_NoPDB, attributeDeclaration.AttributeType, attributeDeclaration.AttributeType.Assembly.GetName().Name, proxyGenerator.ClientProjectName)); } else if (shareKind == CodeMemberShareKind.NotShared) { attributeDeclaration.Errors.Add( string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared, attributeDeclaration.AttributeType, proxyGenerator.ClientProjectName)); } // Verify shared types. Here, we order by type name so that any generated errors // are presented in a consistent order. foreach (var type in attributeDeclaration.RequiredTypes.OrderBy(t => t.FullName)) { shareKind = proxyGenerator.GetTypeShareKind(type); // Missing PDB or lack of user code means we cannot know -- issue special warning if (shareKind == CodeMemberShareKind.Unknown) { attributeDeclaration.Errors.Add( string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_Type_NoPDB, attributeDeclaration.AttributeType, type, type.Assembly.GetName().Name, proxyGenerator.ClientProjectName)); } else if (shareKind == CodeMemberShareKind.NotShared) { attributeDeclaration.Errors.Add( string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_Type, attributeDeclaration.AttributeType, type, proxyGenerator.ClientProjectName)); } } // Verify shared methods. Here, we order by method name so that any generated errors // are presented in a consistent order. foreach (var method in attributeDeclaration.RequiredMethods.OrderBy(p => p.Name)) { shareKind = proxyGenerator.GetMethodShareKind(method); if (shareKind == CodeMemberShareKind.NotShared) { attributeDeclaration.Errors.Add( string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_Method, attributeDeclaration.AttributeType, method.Name, method.DeclaringType, proxyGenerator.ClientProjectName)); } } // Verify shared properties. Here, we order by property name so that any generated errors // are presented in a consistent order. foreach (var property in attributeDeclaration.RequiredProperties.OrderBy(p => p.Name)) { shareKind = proxyGenerator.GetPropertyShareKind(property.DeclaringType, property.Name); if (shareKind == CodeMemberShareKind.NotShared) { attributeDeclaration.Errors.Add( string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_Property, attributeDeclaration.AttributeType, property.Name, property.DeclaringType, proxyGenerator.ClientProjectName)); } } }
/// <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 the optimize <see cref="Attribute"/> names by removing the "Attribute" suffix.</param> /// <returns>A <see cref="CodeTypeReference"/> for a CLR type.</returns> internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType, bool optimizeAttributeName) { return(GetTypeReference(type, codeGenerator, referencingType, optimizeAttributeName, false)); }
/// <summary> /// Creates a <see cref="CodeAttributeDeclaration"/> for a <see cref="DataContractAttribute"/> /// </summary> /// <param name="sourceType">The type to which the attribute will be applied (to use as a reference)</param> /// <param name="codeGenerator">The client proxy generator</param> /// <param name="referencingType">The type referencing this declaration</param> /// <returns>The new attribute declaration</returns> internal static CodeAttributeDeclaration CreateDataContractAttributeDeclaration(Type sourceType, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { CodeAttributeDeclaration dataContractAttrib = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.DataContractAttribute), codeGenerator, referencingType); string dataContractNamespace = CodeGenUtilities.GetContractNamespace(sourceType); string dataContractName = null; // If the user specified a DataContract, we should copy the namespace and name. DataContractAttribute sourceDataContractAttrib = (DataContractAttribute)Attribute.GetCustomAttribute(sourceType, typeof(DataContractAttribute)); if (sourceDataContractAttrib != null) { if (sourceDataContractAttrib.Namespace != null) { dataContractNamespace = sourceDataContractAttrib.Namespace; } if (sourceDataContractAttrib.Name != null) { dataContractName = sourceDataContractAttrib.Name; } } dataContractAttrib.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(dataContractNamespace))); if (dataContractName != null) { dataContractAttrib.Arguments.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(dataContractName))); } return dataContractAttrib; }
/// <summary> /// Determines if we need to preface the root namespace to the type /// </summary> /// <param name="type">The type in question</param> /// <param name="codeGenerator">The current proxy generator</param> /// <returns><c>true</c> if if we need to preface the root namespace to the type, <c>false</c> otherwise</returns> private static bool NeedToPrefaceRootNamespace(Type type, CodeDomClientCodeGenerator codeGenerator) { // System assemblies never preface with the root namespace if (type.Assembly.IsSystemAssembly()) { return false; } bool isVbProjectWithRootNamespace = !codeGenerator.IsCSharp && !string.IsNullOrEmpty(codeGenerator.ClientProxyCodeGenerationOptions.ClientRootNamespace); bool typeIsGeneratedOnTheClient = (type.IsEnum && codeGenerator.NeedToGenerateEnumType(type)) || codeGenerator.DomainServiceDescriptions.Any(dsd => dsd.EntityTypes.Contains(type) || dsd.ComplexTypes.Contains(type) || dsd.DomainServiceType == type); bool typeNameStartsWithRootNamespace = string.Equals(type.Namespace, rootNamespace, StringComparison.Ordinal) || (!string.IsNullOrEmpty(type.Namespace) && type.Namespace.StartsWith(rootNamespace + ".", StringComparison.Ordinal)); return isVbProjectWithRootNamespace && typeIsGeneratedOnTheClient && !typeNameStartsWithRootNamespace; }
/// <summary> /// Initializes a new instance of the <see cref="InvokeOperationProxyGenerator"/> class. /// </summary> /// <param name="clientProxyGenerator">The client proxy generator against which this will generate code. Cannot be null.</param> /// <param name="proxyClass">The class into which to inject the generated code</param> /// <param name="domainServiceDescription">The description for the DomainService we're generating for</param> public InvokeOperationProxyGenerator(CodeDomClientCodeGenerator clientProxyGenerator, CodeTypeDeclaration proxyClass, DomainServiceDescription domainServiceDescription) : base(clientProxyGenerator) { this._proxyClass = proxyClass; this._domainServiceDescription = domainServiceDescription; }
/// <summary> /// Creates an attribute declaration for <see cref="EnumMemberAttribute"/> /// </summary> /// <param name="memberInfo">The member that may contain an existing <see cref="EnumMemberAttribute"/></param> /// <param name="codeGenerator">The proxy generator</param> /// <param name="referencingType">The referencing type</param> /// <returns>A new attribute declaration</returns> internal static CodeAttributeDeclaration CreateEnumMemberAttributeDeclaration(MemberInfo memberInfo, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { CodeAttributeDeclaration enumMemberDecl = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.EnumMemberAttribute), codeGenerator, referencingType); // If the user specified a DataContract, we should copy the namespace and name. EnumMemberAttribute enumMemberAttrib = (EnumMemberAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(EnumMemberAttribute)); if (enumMemberAttrib != null) { string value = enumMemberAttrib.Value; if (!string.IsNullOrEmpty(value)) { enumMemberDecl.Arguments.Add(new CodeAttributeArgument("Value", new CodePrimitiveExpression(value))); } } return(enumMemberDecl); }
/// <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); }
/// <summary> /// Initializes a new instance of the <see cref="DomainOperationEntryProxyGenerator"/> class. /// </summary> /// <param name="clientProxyGenerator">The client proxy generator against which this will generate code. Cannot be null.</param> /// <param name="proxyClass">The class into which to inject the generated code</param> /// <param name="domainServiceDescription">The description for the DomainService we're generating for</param> public DomainOperationEntryProxyGenerator(CodeDomClientCodeGenerator clientProxyGenerator, CodeTypeDeclaration proxyClass, DomainServiceDescription domainServiceDescription) : base(clientProxyGenerator) { this._proxyClass = proxyClass; this._domainServiceDescription = domainServiceDescription; }
/// <summary> /// Creates a <see cref="CodeAttributeDeclaration"/> for a <see cref="DataContractAttribute"/> /// </summary> /// <param name="sourceType">The type to which the attribute will be applied (to use as a reference)</param> /// <param name="codeGenerator">The client proxy generator</param> /// <param name="referencingType">The type referencing this declaration</param> /// <returns>The new attribute declaration</returns> internal static CodeAttributeDeclaration CreateDataContractAttributeDeclaration(Type sourceType, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { CodeAttributeDeclaration dataContractAttrib = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.DataContractAttribute), codeGenerator, referencingType); string dataContractNamespace = CodeGenUtilities.GetContractNamespace(sourceType); string dataContractName = null; // If the user specified a DataContract, we should copy the namespace and name. DataContractAttribute sourceDataContractAttrib = (DataContractAttribute)Attribute.GetCustomAttribute(sourceType, typeof(DataContractAttribute)); if (sourceDataContractAttrib != null) { if (sourceDataContractAttrib.Namespace != null) { dataContractNamespace = sourceDataContractAttrib.Namespace; } if (sourceDataContractAttrib.Name != null) { dataContractName = sourceDataContractAttrib.Name; } } dataContractAttrib.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(dataContractNamespace))); if (dataContractName != null) { dataContractAttrib.Arguments.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(dataContractName))); } return(dataContractAttrib); }
/// <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); }
/// <summary> /// Initializes a new instance of the <see cref="WebContextGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code.</param> internal WebContextGenerator(CodeDomClientCodeGenerator proxyGenerator) : base(proxyGenerator) { }
/// <summary> /// Initializes a new instance of the <see cref="NotificationMethodGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The current <see cref="CodeDomClientCodeGenerator"/>.</param> public NotificationMethodGenerator(CodeDomClientCodeGenerator proxyGenerator) : this(proxyGenerator, DefaultIndentLevel) { }
/// <summary> /// Initializes a new instance of the <see cref="ComplexTypeProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code. Cannot be null.</param> /// <param name="complexType">The complex type. Cannot be null.</param> /// <param name="domainServiceDescription"><see cref="DomainServiceDescription"/> that exposes this complex type.</param> /// <param name="typeMapping">A dictionary of <see cref="DomainService"/> and related complex types that maps to their corresponding client-side <see cref="CodeTypeReference"/> representations.</param> public ComplexTypeProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, Type complexType, DomainServiceDescription domainServiceDescription, IDictionary <Type, CodeTypeDeclaration> typeMapping) : base(proxyGenerator, complexType, typeMapping) { this._domainServiceDescription = domainServiceDescription; }
/// <summary> /// Generates code for the given custom attributes and adds them to the given <see cref="CodeAttributeDeclarationCollection"/> /// </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 to generate</param> /// <param name="outputCollection">The collection to which the generated attributes will be added</param> /// <param name="comments">Collection of comments that should be updated if errors are discovered.</param> public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, Func<AttributeBuilderException, string> getLogWarningMessage, IEnumerable<Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments) { GenerateCustomAttributes( proxyGenerator, referencingType, getLogWarningMessage, attributes, outputCollection, comments, Resource.ClientCodeGen_Attribute_FailedToGenerate); }
/// <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> /// <returns>A <see cref="CodeTypeReference"/> for a CLR type.</returns> internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { return(GetTypeReference(type, codeGenerator, referencingType, false, false)); }
/// <summary> /// Creates an attribute declaration for <see cref="EnumMemberAttribute"/> /// </summary> /// <param name="memberInfo">The member that may contain an existing <see cref="EnumMemberAttribute"/></param> /// <param name="codeGenerator">The proxy generator</param> /// <param name="referencingType">The referencing type</param> /// <returns>A new attribute declaration</returns> internal static CodeAttributeDeclaration CreateEnumMemberAttributeDeclaration(MemberInfo memberInfo, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { CodeAttributeDeclaration enumMemberDecl = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.EnumMemberAttribute), codeGenerator, referencingType); // If the user specified a DataContract, we should copy the namespace and name. EnumMemberAttribute enumMemberAttrib = (EnumMemberAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(EnumMemberAttribute)); if (enumMemberAttrib != null) { string value = enumMemberAttrib.Value; if (!string.IsNullOrEmpty(value)) { enumMemberDecl.Arguments.Add(new CodeAttributeArgument("Value", new CodePrimitiveExpression(value))); } } return enumMemberDecl; }
/// <summary> /// In cases where we are emitting types that aren't already in the root namespace, /// we need to translate the generated type namespace so that it resides in the /// root namespace. /// </summary> /// <remarks> /// Here, we're interested in cases where we're generating VB code /// and our target project has a non-null root namespace. /// </remarks> /// <param name="type">The type who namespace should be translated</param> /// <param name="codeGenerator">The current proxy generator</param> /// <returns>A translated namespace string</returns> internal static string TranslateNamespace(Type type, CodeDomClientCodeGenerator codeGenerator) { // Set default namespace string typeNamespace; if (!typeNamespaceTranslations.TryGetValue(type, out typeNamespace)) { // Set the appropriate namespace if (NeedToPrefaceRootNamespace(type, codeGenerator)) { typeNamespace = string.IsNullOrEmpty(type.Namespace) ? rootNamespace : (rootNamespace + "." + type.Namespace); } else { typeNamespace = type.Namespace; } // Cache the value for next time typeNamespaceTranslations[type] = typeNamespace; } return typeNamespace; }
/// <summary> /// Creates a <see cref="CodeAttributeDeclaration"/> for a <see cref="DisplayAttribute"/>. /// </summary> /// <param name="codeGenerator">The client proxy generator</param> /// <param name="referencingType">The type on whose member <see cref="DisplayAttribute"/> will be applied</param> /// <returns>The new attribute declaration</returns> internal static CodeAttributeDeclaration CreateDisplayAttributeDeclaration(CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { CodeAttributeDeclaration displayAttributeDeclaration = CodeGenUtilities.CreateAttributeDeclaration( typeof(DisplayAttribute), codeGenerator, referencingType); displayAttributeDeclaration.Arguments.Add(new CodeAttributeArgument("AutoGenerateField", new CodePrimitiveExpression(false))); return displayAttributeDeclaration; }
/// <summary> /// Initializes a new instance of the <see cref="DomainServiceProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code. Cannot be null.</param> /// <param name="domainServiceDescription">The domain service description to use as source metadata</param> /// <param name="typeMapping">A dictionary of <see cref="DomainService"/> and related entity types that maps to their corresponding client-side <see cref="CodeTypeReference"/> representations.</param> public DomainServiceProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, DomainServiceDescription domainServiceDescription, IDictionary <Type, CodeTypeDeclaration> typeMapping) : base(proxyGenerator) { this._domainServiceDescription = domainServiceDescription; this._typeMapping = typeMapping; }
/// <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; }
/// <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)); } // 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); // 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; }
/// <summary> /// Generate comments indicating attribute propagation failure. /// </summary> /// <param name="proxyGenerator">The context for generating code. It cannot be <c>null</c>.</param> /// <param name="attributeDeclaration">The attribute declaration to generate as a comment.</param> /// <returns>A collection of comments.</returns> private static CodeCommentStatementCollection ConstructCodeAttributeFailureComments(CodeDomClientCodeGenerator proxyGenerator, AttributeDeclaration attributeDeclaration) { // We are generating failure comments in the following example form: // // // Unable to generate the following attribute(s) due to the following error(s): // // - The attribute 'System.ComponentModel.DataAnnotations.CustomValidationAttribute' references type 'TestDomainServices.ServerOnlyValidator'. This is not accessible in the client project. // // [CustomValidationAttribute(typeof(TestDomainServices.ServerOnlyValidator), "IsObjectValid")] // // CodeCommentStatementCollection comments = new CodeCommentStatementCollection(); foreach (string error in attributeDeclaration.Errors) { comments.Add(new CodeCommentStatement(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_FailedToGenerate_ErrorTemplate, error))); } comments.Add(new CodeCommentStatement(GenerateCodeAttribute(proxyGenerator, attributeDeclaration))); comments.Add(new CodeCommentStatement(string.Empty /* blank comment */)); return(comments); }
/// <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; }
/// <summary> /// Generate comments indicating attribute propagation failure. /// </summary> /// <param name="proxyGenerator">The context for generating code. It cannot be <c>null</c>.</param> /// <param name="attributeDeclaration">The attribute declaration to generate as a comment.</param> /// <returns>A collection of comments.</returns> private static CodeCommentStatementCollection ConstructCodeAttributeFailureComments(CodeDomClientCodeGenerator proxyGenerator, AttributeDeclaration attributeDeclaration) { // We are generating failure comments in the following example form: // // // Unable to generate the following attribute(s) due to the following error(s): // // - The attribute 'System.ComponentModel.DataAnnotations.CustomValidationAttribute' references type 'TestDomainServices.ServerOnlyValidator'. This is not accessible in the client project. // // [CustomValidationAttribute(typeof(TestDomainServices.ServerOnlyValidator), "IsObjectValid")] // // CodeCommentStatementCollection comments = new CodeCommentStatementCollection(); foreach (string error in attributeDeclaration.Errors) { comments.Add(new CodeCommentStatement(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_FailedToGenerate_ErrorTemplate, error))); } comments.Add(new CodeCommentStatement(GenerateCodeAttribute(proxyGenerator, attributeDeclaration))); comments.Add(new CodeCommentStatement(string.Empty /* blank comment */)); return comments; }
/// <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 the optimize <see cref="Attribute"/> names by removing the "Attribute" suffix.</param> /// <returns>A <see cref="CodeTypeReference"/> for a CLR type.</returns> internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType, bool optimizeAttributeName) { return GetTypeReference(type, codeGenerator, referencingType, optimizeAttributeName, false); }
/// <summary> /// Generates an attribute declaration string. This is used in scenarios where an attribute declaration is needed /// in the form of a comment. CodeDOM does not support generation of standalone attributes. /// </summary> /// <param name="proxyGenerator">The context for generating code. It cannot be null.</param> /// <param name="attributeDeclaration">The <see cref="AttributeDeclaration"/> to represent.</param> /// <returns>An attribute declaration.</returns> private static string GenerateCodeAttribute(CodeDomClientCodeGenerator proxyGenerator, AttributeDeclaration attributeDeclaration) { StringBuilder result = new StringBuilder(); bool isCSharp = proxyGenerator.IsCSharp; result.Append(isCSharp ? '[' : '<'); result.Append(attributeDeclaration.AttributeType.Name); result.Append('('); // Add ctor args if (attributeDeclaration.ConstructorArguments.Count > 0) { foreach (object value in attributeDeclaration.ConstructorArguments) { result.Append(ConvertValueToCode(value, isCSharp)); result.Append(", "); } } // Add named params if (attributeDeclaration.NamedParameters.Count > 0) { foreach (KeyValuePair<string, object> pair in attributeDeclaration.NamedParameters) { result.Append(pair.Key); result.Append(isCSharp ? " = " : " := "); result.Append(ConvertValueToCode(pair.Value, isCSharp)); result.Append(", "); } } if (attributeDeclaration.ConstructorArguments.Count > 0 || attributeDeclaration.NamedParameters.Count > 0) { result.Remove(result.Length - 2, 2); } result.Append(')'); result.Append(isCSharp ? "]" : "> _"); return result.ToString(); }
/// <summary> /// Creates a <see cref="CodeAttributeDeclaration"/> for the given <see cref="AttributeDeclaration"/>. /// </summary> /// <param name="proxyGenerator">The context for generating code. It cannot be null.</param> /// <param name="referencingType">The referencing type.</param> /// <param name="attributeDeclaration">The <see cref="AttributeDeclaration"/> to build.</param> /// <returns>A <see cref="CodeAttributeDeclaration"/>.</returns> private static CodeAttributeDeclaration CreateCodeAttributeDeclaration(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, AttributeDeclaration attributeDeclaration) { CodeAttributeDeclaration codeAttributeDeclaration = CodeGenUtilities.CreateAttributeDeclaration(attributeDeclaration.AttributeType, proxyGenerator, referencingType); // Add ctor args foreach (object arg in attributeDeclaration.ConstructorArguments) { CodeExpression expression = CreateCodeExpression(proxyGenerator, referencingType, arg); codeAttributeDeclaration.Arguments.Add(new CodeAttributeArgument(expression)); } // Add named params foreach (KeyValuePair<string, object> pair in attributeDeclaration.NamedParameters) { CodeExpression expression = CreateCodeExpression(proxyGenerator, referencingType, pair.Value); codeAttributeDeclaration.Arguments.Add(new CodeAttributeArgument(pair.Key, expression)); } return codeAttributeDeclaration; }
/// <summary> /// Creates an attribute declaration based on the specified attribute /// </summary> /// <param name="attributeType">type of the attribute</param> /// <param name="codeGenerator">A <see cref="CodeDomClientCodeGenerator"/>.</param> /// <param name="referencingType">The referencing type.</param> /// <returns>the attribute declaration</returns> internal static CodeAttributeDeclaration CreateAttributeDeclaration(Type attributeType, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { if (!typeof(Attribute).IsAssignableFrom(attributeType)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Resource.Type_Must_Be_Attribute, attributeType.Name), "attributeType"); } CodeTypeReference attribute = GetTypeReference(attributeType, codeGenerator, referencingType, true); return new CodeAttributeDeclaration(attribute); }
/// <summary> /// Creates the CodeDom CodeExpression for the given value. Returns null if unable to generate a CodeExpression. /// </summary> /// <remarks>This method exists solely to help generate code for all object types that can appear in an /// attribute declaration, such as typeof()</remarks> /// <param name="proxyGenerator">The context for generating code. It cannot be null.</param> /// <param name="referencingType">The referencing type</param> /// <param name="value">The value. Null is permitted.</param> /// <returns>The code expression</returns> private static CodeExpression CreateCodeExpression(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, object value) { Type typeOfValue = value == null ? null : value.GetType(); if (value == null || typeOfValue.IsPrimitive || value is string) { CodeExpression e = new CodePrimitiveExpression(value); // Workaround CodeDom issue -- it looks like CodePrimitiveExpression is fooled and generates double // literals as integers when there is no fraction. We take a general strategy of forcing an explicit // compile time cast to ensure we recompile the same type. if (value != null && (value is double || value is float)) { e = new CodeCastExpression(value.GetType(), e); } return e; } // typeof(T) requires special handling Type valueAsType = value as Type; if (valueAsType != null) { // Verify the type is shared // Don't know counts as not shared CodeMemberShareKind shareKind = proxyGenerator.GetTypeShareKind(valueAsType); if ((shareKind & CodeMemberShareKind.Shared) == 0) { // Here we return a fully-qualified type name to ensure we don't cause compilation // errors by adding invalid 'using' statements into our codedom graph. CodeTypeReference valueTypeReference = CodeGenUtilities.GetTypeReference(valueAsType, proxyGenerator, referencingType, false, /*Use fully qualified name*/ true); valueTypeReference.Options = CodeTypeReferenceOptions.GlobalReference; return new CodeTypeOfExpression(valueTypeReference); } return new CodeTypeOfExpression(CodeGenUtilities.GetTypeReference(valueAsType, proxyGenerator, referencingType)); } // Enum values need special handling if (typeOfValue.IsEnum) { string enumValueName = Enum.GetName(typeOfValue, value); string enumTypeName; if (proxyGenerator.ClientProxyCodeGenerationOptions.UseFullTypeNames) { enumTypeName = typeOfValue.FullName; } else { enumTypeName = typeOfValue.Name; } return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(enumTypeName), enumValueName); } return null; }
/// <summary> /// Generates code for the given custom attributes and adds them to the given <see cref="CodeAttributeDeclarationCollection"/> /// </summary> /// <param name="proxyGenerator">Root client proxy generator</param> /// <param name="referencingType">The referencing type</param> /// <param name="attributes">Collection of attributes to generate</param> /// <param name="outputCollection">The collection to which the generated attributes will be added</param> /// <param name="comments">Collection of comments that should be updated if errors are discovered.</param> /// <param name="forcePropagation">Indicates whether or not to force attribute propagation.</param> public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, IEnumerable<Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments, bool forcePropagation) { GenerateCustomAttributes( proxyGenerator, referencingType, null, attributes, outputCollection, comments, Resource.ClientCodeGen_Attribute_FailedToGenerate, forcePropagation); }
/// <summary> /// Initializes a new instance of the <see cref="ComplexTypeProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code. Cannot be null.</param> /// <param name="complexType">The complex type. Cannot be null.</param> /// <param name="domainServiceDescription"><see cref="DomainServiceDescription"/> that exposes this complex type.</param> /// <param name="typeMapping">A dictionary of <see cref="DomainService"/> and related complex types that maps to their corresponding client-side <see cref="CodeTypeReference"/> representations.</param> public ComplexTypeProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, Type complexType, DomainServiceDescription domainServiceDescription, IDictionary<Type, CodeTypeDeclaration> typeMapping) : base(proxyGenerator, complexType, typeMapping) { this._domainServiceDescription = domainServiceDescription; }
/// <summary> /// Generates code for the given custom attributes and adds them to the given <see cref="CodeAttributeDeclarationCollection"/> /// </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 to generate</param> /// <param name="outputCollection">The collection to which the generated attributes will be added</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> public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, Func<AttributeBuilderException, string> getLogWarningMessage, IEnumerable<Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments, string customCommentHeader, bool forcePropagation) { IEnumerable<CodeAttributeDeclaration> cads = GenerateCustomAttributes(proxyGenerator, referencingType, getLogWarningMessage, attributes, comments, customCommentHeader, forcePropagation); foreach (var cad in cads) { outputCollection.Add(cad); } }
/// <summary> /// Initializes a new instance of the <see cref="DomainServiceProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">The client proxy generator against which this will generate code. Cannot be null.</param> /// <param name="domainServiceDescription">The domain service description to use as source metadata</param> /// <param name="typeMapping">A dictionary of <see cref="DomainService"/> and related entity types that maps to their corresponding client-side <see cref="CodeTypeReference"/> representations.</param> public DomainServiceProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, DomainServiceDescription domainServiceDescription, IDictionary<Type, CodeTypeDeclaration> typeMapping) : base(proxyGenerator) { this._domainServiceDescription = domainServiceDescription; this._typeMapping = typeMapping; }