public void SharedCodeService_Ctors()
        {
            string projectPath = null;
            string outputPath  = null;

            TestHelper.GetProjectPaths("STS5", out projectPath, out outputPath);
            string clientProjectPath = CodeGenHelper.ClientClassLibProjectPath(projectPath);

            ConsoleLogger logger = new ConsoleLogger();

            using (SharedCodeService sts = CodeGenHelper.CreateSharedCodeService(clientProjectPath, logger))
            {
                ConstructorInfo ctor = typeof(TestValidator).GetConstructor(new Type[] { typeof(string) });
                Assert.IsNotNull("Failed to find string ctor on TestValidator");
                CodeMemberShareKind shareKind = sts.GetMethodShareKind(typeof(TestValidator).AssemblyQualifiedName, ctor.Name, new string[] { typeof(string).AssemblyQualifiedName });
                Assert.AreEqual(CodeMemberShareKind.SharedByReference, shareKind, "Expected TestValidator ctor to be shared by reference");
                TestHelper.AssertNoErrorsOrWarnings(logger);
            }
        }
        public void SharedCodeService_Properties()
        {
            string projectPath = null;
            string outputPath  = null;

            TestHelper.GetProjectPaths("STS2", out projectPath, out outputPath);
            string clientProjectPath = CodeGenHelper.ClientClassLibProjectPath(projectPath);

            ConsoleLogger logger = new ConsoleLogger();

            using (SharedCodeService sts = CodeGenHelper.CreateSharedCodeService(clientProjectPath, logger))
            {
                CodeMemberShareKind shareKind = sts.GetPropertyShareKind(typeof(TestEntity).AssemblyQualifiedName, "ServerAndClientValue");
                Assert.AreEqual(CodeMemberShareKind.SharedByReference, shareKind, "Expected TestEntity.ServerAndClientValue property to be shared by reference.");

                shareKind = sts.GetPropertyShareKind(typeof(TestEntity).AssemblyQualifiedName, "TheValue");
                Assert.AreEqual(CodeMemberShareKind.NotShared, shareKind, "Expected TestEntity.TheValue property not to be shared in source.");
            }
        }
Ejemplo n.º 3
0
        public void SharedCodeService_Types()
        {
            string projectPath = null;
            string outputPath  = null;

            TestHelper.GetProjectPaths("", out projectPath, out outputPath);
            string clientProjectPath = CodeGenHelper.ClientClassLibProjectPath(projectPath);

            ConsoleLogger logger = new ConsoleLogger();

            using (SharedCodeService sts = CodeGenHelper.CreateSharedCodeService(clientProjectPath, logger))
            {
                // TestEntity is shared because it is linked
                CodeMemberShareKind shareKind = sts.GetTypeShareKind(typeof(TestEntity).AssemblyQualifiedName);
                Assert.AreEqual(CodeMemberShareKind.SharedByReference, shareKind, "Expected TestEntity type to be shared by reference");

                // TestValidator is shared because it is linked
                shareKind = sts.GetTypeShareKind(typeof(TestValidator).AssemblyQualifiedName);
                Assert.AreEqual(CodeMemberShareKind.SharedByReference, shareKind, "Expected TestValidator type to be shared by reference");

                // SharedClass is shared because it is linked
                shareKind = sts.GetTypeShareKind(typeof(SharedClass).AssemblyQualifiedName);
                Assert.IsTrue(shareKind == CodeMemberShareKind.SharedBySource, "Expected SharedClass type to be shared in source");

                // DomainService exists only in the server and is not shared
                shareKind = sts.GetTypeShareKind(typeof(DomainService).AssemblyQualifiedName);
                Assert.IsTrue(shareKind == CodeMemberShareKind.NotShared, "Expected DomainService type not to be shared");

                // TestValidatorServer exists only on the server and is not shared
                shareKind = sts.GetTypeShareKind(typeof(TestValidatorServer).AssemblyQualifiedName);
                Assert.IsTrue(shareKind == CodeMemberShareKind.NotShared, "Expected TestValidatorServer type not to be shared");

                // CodelessType exists on both server and client, but lacks all user code necessary
                // to determine whether it is shared.  Because it compiles into both projects, it should
                // be considered shared by finding the type in both assemblies
                shareKind = sts.GetTypeShareKind(typeof(CodelessType).AssemblyQualifiedName);
                Assert.IsTrue(shareKind == CodeMemberShareKind.SharedByReference, "Expected CodelessType type to be shared in assembly");
            }
        }
        /// <summary>
        /// Generates all of the properties for the type.
        /// </summary>
        private void GenerateProperties()
        {
            IEnumerable <PropertyDescriptor> properties = TypeDescriptor.GetProperties(this.Type)
                                                          .Cast <PropertyDescriptor>()
                                                          .OrderBy(p => p.Name);

            foreach (PropertyDescriptor pd in properties)
            {
                if (!this.ShouldDeclareProperty(pd))
                {
                    continue;
                }

                // Generate a property getter/setter pair for every property whose type
                // we support. Non supported property types will be skipped.
                if (this.CanGenerateProperty(pd))
                {
                    // Ensure the property is not virtual, abstract or new
                    // If there is a violation, we log the error and keep
                    // running to accumulate all such errors.  This function
                    // may return an "okay" for non-error case polymorphics.
                    if (!this.CanGeneratePropertyIfPolymorphic(pd))
                    {
                        continue;
                    }

                    if (!this.GenerateNonSerializableProperty(pd))
                    {
                        Type        propType             = CodeGenUtilities.TranslateType(pd.PropertyType);
                        List <Type> typesToCodeGen       = new List <Type>();
                        bool        isTypeSafeToGenerate = true;

                        // Create a list containing the types we will require on the client
                        if (TypeUtility.IsPredefinedDictionaryType(propType))
                        {
                            typesToCodeGen.AddRange(CodeGenUtilities.GetDictionaryGenericArgumentTypes(propType));
                        }
                        else
                        {
                            typesToCodeGen.Add(TypeUtility.GetElementType(propType));
                        }

                        // We consider all predefined types as legal to code-gen *except* those
                        // that would generate a compile error on the client due to missing reference.
                        // We treat "don't know" and "false" as grounds for a warning.
                        // Note that we do this *after* TranslateType so that types like System.Data.Linq.Binary
                        // which cannot exist on the client anyway has been translated
                        foreach (Type type in typesToCodeGen)
                        {
                            // Enum (and nullable<enum>) types may require generation on client
                            Type nonNullableType = TypeUtility.GetNonNullableType(type);


                            if (nonNullableType.IsEnum)
                            {
                                // Register use of this enum type, which could cause deferred generation
                                this.ClientProxyGenerator.RegisterUseOfEnumType(nonNullableType);
                            }
                            // If this is not an enum or nullable<enum> and we're not generating the complex type, determine whether this
                            // property type is visible to the client.  If it is not, log a warning.
                            else if (!this.ComplexTypes.Contains(type))
                            {
                                // "Don't know" counts as "no"
                                CodeMemberShareKind enumShareKind = this.ClientProxyGenerator.GetTypeShareKind(nonNullableType);
                                if ((enumShareKind & CodeMemberShareKind.Shared) == 0)
                                {
                                    this.ClientProxyGenerator.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_PropertyType_Not_Shared, pd.Name, this.Type.FullName, type.FullName, this.ClientProxyGenerator.ClientProjectName));
                                    isTypeSafeToGenerate = false; // Flag error but continue to allow accumulation of additional errors.
                                }
                            }
                        }

                        if (isTypeSafeToGenerate)
                        {
                            // Generate OnMethodXxxChanging/Changed partial methods.

                            // Note: the parameter type reference needs to handle the possibility the
                            // property type is defined in the project's root namespace and that VB prepends
                            // that namespace.  The utility helper gives us the right type reference.
                            CodeTypeReference parameterTypeRef =
                                CodeGenUtilities.GetTypeReference(propType, this.ClientProxyGenerator, this.ProxyClass);

                            this.NotificationMethodGen.AddMethodFor(pd.Name + "Changing", new CodeParameterDeclarationExpression(parameterTypeRef, "value"), null);
                            this.NotificationMethodGen.AddMethodFor(pd.Name + "Changed", null);

                            this.GenerateProperty(pd);
                        }
                    }
                }
                else
                {
                    this.OnPropertySkipped(pd);
                }
            }
        }
Ejemplo n.º 5
0
 public SharedCodeDescription(CodeMemberShareKind shareKind, int[] sharedFileIds)
 {
     this._shareKind     = shareKind;
     this._sharedFileIds = sharedFileIds;
 }
        private static void ValidateAttributeDeclarationRequirements(AttributeDeclaration attributeDeclaration, ClientCodeGenerator textTemplateClientCodeGenerator)
        {
            // Verify the attribute itself is shared.
            CodeMemberShareKind shareKind = textTemplateClientCodeGenerator.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,
                        textTemplateClientCodeGenerator.ClientProjectName));
            }
            else if (shareKind == CodeMemberShareKind.NotShared)
            {
                attributeDeclaration.Errors.Add(
                    string.Format(
                        CultureInfo.CurrentCulture,
                        Resource.ClientCodeGen_Attribute_RequiresShared,
                        attributeDeclaration.AttributeType,
                        textTemplateClientCodeGenerator.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 = textTemplateClientCodeGenerator.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,
                            textTemplateClientCodeGenerator.ClientProjectName));
                }
                else if (shareKind == CodeMemberShareKind.NotShared)
                {
                    attributeDeclaration.Errors.Add(
                        string.Format(
                            CultureInfo.CurrentCulture,
                            Resource.ClientCodeGen_Attribute_RequiresShared_Type,
                            attributeDeclaration.AttributeType,
                            type,
                            textTemplateClientCodeGenerator.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 = textTemplateClientCodeGenerator.GetMethodShareKind(method);
                if (shareKind == CodeMemberShareKind.NotShared)
                {
                    attributeDeclaration.Errors.Add(
                        string.Format(
                            CultureInfo.CurrentCulture,
                            Resource.ClientCodeGen_Attribute_RequiresShared_Method,
                            attributeDeclaration.AttributeType,
                            method.Name,
                            method.DeclaringType,
                            textTemplateClientCodeGenerator.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 = textTemplateClientCodeGenerator.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,
                            textTemplateClientCodeGenerator.ClientProjectName));
                }
            }
        }
        /// <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);
        }
        private string GenerateProxyClass()
        {
            string generatedCode = string.Empty;

            // Analyze the assemblies to extract all the DomainServiceDescriptions
            ICollection <DomainServiceDescription> allDescriptions = this._domainServiceDescriptions;
            List <Type> generatedEntityTypes  = new List <Type>();
            List <Type> generatedComplexTypes = new List <Type>();
            Dictionary <Type, CodeTypeDeclaration> typeMapping = new Dictionary <Type, CodeTypeDeclaration>();

            // Used to queue CodeProcessor invocations
            Queue <CodeProcessorWorkItem> codeProcessorQueue = new Queue <CodeProcessorWorkItem>();

            // Before we begin codegen, we want to register type names with our codegen
            // utilities so that we can avoid type name conflicts later.
            this.PreprocessProxyTypes();

            // Generate a new domain service proxy class for each domain service we found.
            // OrderBy type name of DomainService to give code-gen predictability
            foreach (DomainServiceDescription dsd in allDescriptions.OrderBy(d => d.DomainServiceType.Name))
            {
                // If we detect the client already has the DomainContext we would have
                // generated, skip it.  This condition arises when the client has references
                // to class libraries as well as a code-gen link to the server which has
                // references to the server-side equivalent libraries.  Without this check, we would
                // re-generate the same DomainContext that already lives in the class library.
                CodeMemberShareKind domainContextShareKind = this.GetDomainContextTypeMemberShareKind(dsd);
                if ((domainContextShareKind & CodeMemberShareKind.Shared) != 0)
                {
                    this.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.Shared_DomainContext_Skipped, dsd.DomainServiceType.Name));
                    continue;
                }

                // Log information level message to help users see progress and debug code-gen issues
                this.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Generating_DomainService, dsd.DomainServiceType.FullName));

                new DomainServiceProxyGenerator(this, dsd, typeMapping).Generate();

                // Generate all entities.
                this.GenerateDataContractTypes(dsd.EntityTypes, generatedEntityTypes, Resource.ClientCodeGen_EntityTypesCannotBeShared_Reference, t =>
                {
                    new EntityProxyGenerator(this, t, allDescriptions, typeMapping).Generate();
                });

                // Generate all complex types.
                this.GenerateDataContractTypes(dsd.ComplexTypes, generatedComplexTypes, Resource.ClientCodeGen_ComplexTypesCannotBeShared_Reference, t =>
                {
                    new ComplexTypeProxyGenerator(this, t, dsd, typeMapping).Generate();
                });

                DomainIdentifierAttribute domainIdentifier = dsd.Attributes.OfType <DomainIdentifierAttribute>().SingleOrDefault();
                if (domainIdentifier != null)
                {
                    Type codeProcessorType = domainIdentifier.CodeProcessor;
                    if (codeProcessorType != null)
                    {
                        // Create a limited type-mapping dictionary that contains references to
                        // the DomainService and related entity type CodeTypeDeclarations.
                        Dictionary <Type, CodeTypeDeclaration> scopedTypeMapping =
                            typeMapping.Where(kvp => kvp.Key == dsd.DomainServiceType || dsd.EntityTypes.Contains(kvp.Key))
                            .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

                        // Register CodeProc
                        codeProcessorQueue.Enqueue(
                            new CodeProcessorWorkItem(codeProcessorType, dsd, scopedTypeMapping));
                    }
                }
            }

            // Generate an application-level WebContext class
            if (this.ClientProxyCodeGenerationOptions.IsApplicationContextGenerationEnabled)
            {
                new WebContextGenerator(this).Generate();
            }
            // If there are no descriptions, we do not generate any client proxies
            // We don't consider this an error, since this task might be invoked before the user has created any.
            else if (allDescriptions.Count == 0)
            {
                return(generatedCode);
            }

            // Generate any enum types we have decided we need to generate
            this.GenerateAllEnumTypes();

            // Fix up CodeDOM graph before invoking CodeProcessors
            this.FixUpCompileUnit(this.CompileUnit);

            // Invoke CodeProcessors after we've completed our CodeDOM graph
            while (codeProcessorQueue.Count > 0)
            {
                // Allow CodeProcessors to do post processing work
                CodeProcessorWorkItem workItem = codeProcessorQueue.Dequeue();
                this.InvokeDomainServiceCodeProcessor(workItem);
            }

            // Write the entire "file" to a single string to permit us to redirect it
            // to a file, a TextBuffer, etc
            if (!this.CodeGenerationHost.HasLoggedErrors)
            {
                using (TextWriter t = new StringWriter(CultureInfo.InvariantCulture))
                {
                    this.Provider.GenerateCodeFromCompileUnit(this.CompileUnit, t, this._options);
                    generatedCode = this.FixupVBOptionStatements(t.ToString());
                }
            }

            return(generatedCode);
        }
 public SharedCodeDescription(CodeMemberShareKind shareKind, int[] sharedFileIds)
 {
     this._shareKind = shareKind;
     this._sharedFileIds = sharedFileIds;
 }