/// <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="Entity"/> 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) { Type = type; _typeMapping = typeMapping; NotificationMethodGen = new NotificationMethodGenerator(proxyGenerator); }
/// <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> /// 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); }
private static CodeDomClientCodeGenerator CreateProxyGenerator(bool isCSharp) { MockCodeGenerationHost host = new MockCodeGenerationHost(); CodeDomClientCodeGenerator generator = isCSharp ? (CodeDomClientCodeGenerator) new CSharpCodeDomClientCodeGenerator() : (CodeDomClientCodeGenerator) new VisualBasicCodeDomClientCodeGenerator(); ClientCodeGenerationOptions options = new ClientCodeGenerationOptions() { Language = isCSharp ? "C#" : "VB", }; generator.Initialize(host, new DomainServiceDescription[] { DomainServiceDescription.GetDescription(typeof(MockOrder_DomainService)) }, options); return(generator); }
private static CodeDomClientCodeGenerator CreateProxyGenerator(bool isCSharp) { MockCodeGenerationHost host = new MockCodeGenerationHost(); CodeDomClientCodeGenerator generator = isCSharp ? (CodeDomClientCodeGenerator) new CSharpCodeDomClientCodeGenerator() : (CodeDomClientCodeGenerator) new VisualBasicCodeDomClientCodeGenerator(); ClientCodeGenerationOptions options = new ClientCodeGenerationOptions() { Language = isCSharp ? "C#" : "VB", }; var entityDescription = new EntityDescription(); generator.Initialize(host, new EntityDescription[] { entityDescription }, options); return(generator); }
/// <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> /// 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()); }
private string GenerateCode() { ClientCodeGenerationOptions options = new ClientCodeGenerationOptions() { Language = this._isCSharp ? "C#" : "VisualBasic", ClientProjectPath = "MockProject.proj", ClientRootNamespace = "TestRootNS", UseFullTypeNames = this._useFullTypeNames }; MockCodeGenerationHost host = TestHelper.CreateMockCodeGenerationHost(this.ConsoleLogger, this.MockSharedCodeService); CodeDomClientCodeGenerator generator = (this._isCSharp) ? (CodeDomClientCodeGenerator) new CSharpCodeDomClientCodeGenerator() : (CodeDomClientCodeGenerator) new VisualBasicCodeDomClientCodeGenerator(); this._domainServiceCatalog = new DomainServiceCatalog(this._domainServiceTypes, this.ConsoleLogger); string generatedCode = generator.GenerateCode(host, this._domainServiceCatalog.DomainServiceDescriptions, options); return(generatedCode); }
/// <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> /// Initializes a new instance of the <see cref="ProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">Our root client proxy generator holding the compilation context. Cannot be null.</param> protected ProxyGenerator(CodeDomClientCodeGenerator proxyGenerator) { _proxyGenerator = proxyGenerator; }
/// <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) { var emittedErrorCommentHeader = false; var 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> /// 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="ProxyGenerator"/> class. /// </summary> /// <param name="proxyGenerator">Our root client proxy generator holding the compilation context. Cannot be null.</param> protected ProxyGenerator(CodeDomClientCodeGenerator proxyGenerator) { this._proxyGenerator = proxyGenerator; }
/// <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 'ServerOnlyValidator'. This is not accessible in the client project. // // [CustomValidationAttribute(typeof(ServerOnlyValidator), "IsObjectValid")] var 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); }