Ejemplo n.º 1
0
        /// <summary>
        /// Default constructor accepting the current <see cref="ClientCodeGenerationOptions"/> context.
        /// </summary>
        /// <param name="options">The current <see cref="ClientCodeGenerationOptions"/> options.</param>
        public ClientProxyFixupCodeDomVisitor(ClientCodeGenerationOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            this._options  = options;
            this._isCSharp = (this._options.Language == "C#");
        }
        /// <summary>
        /// Generates client proxy source code using the specified <paramref name="codeGeneratorName"/> in the context
        /// of the specified <paramref name="host"/>.
        /// </summary>
        /// <param name="host">The host for code generation.</param>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="assembliesToLoad">The set of server assemblies to use for analysis and composition.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The generated source code or <c>null</c> if none was generated.</returns>
        internal string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable <string> assembliesToLoad, string codeGeneratorName)
        {
            Debug.Assert(host != null, "host cannot be null");
            Debug.Assert(options != null, "options cannot be null");
            Debug.Assert(assembliesToLoad != null, "assembliesToLoad cannot be null");

            ILogger logger = host as ILogger;
            DomainServiceCatalog catalog = new DomainServiceCatalog(assembliesToLoad, logger);

            return(this.GenerateCode(host, options, catalog, assembliesToLoad, codeGeneratorName));
        }
        /// <summary>
        /// Creates an <see cref="AppDomain"/> configured for Silverlight code generation.
        /// </summary>
        /// <param name="options">The code generation options.</param>
        internal static void ConfigureAppDomain(ClientCodeGenerationOptions options)
        {
            FrameworkManifest frameworkManifest;
            if (options.ClientProjectTargetPlatform == TargetPlatform.Silverlight)
                frameworkManifest = GetSilverlightFrameworkManifest(options.ClientFrameworkPath);
            else
                frameworkManifest = GetFrameworkManifest(options.ClientFrameworkPath);

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += AppDomainUtilities.ResolveFrameworkAssemblyVersioning;
            AppDomain.CurrentDomain.SetData(FrameworkManifestKey, frameworkManifest);
        }
        /// <summary>
        /// Generates client proxy source code using the specified <paramref name="codeGeneratorName"/> in the context
        /// of the specified <paramref name="host"/>.
        /// </summary>
        /// <param name="host">The host for code generation.</param>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="domainServiceTypes">The set of <see cref="OpenRiaServices.DomainServices.Server.DomainService"/> types for which to generate code.</param>
        /// <param name="compositionAssemblies">The optional set of assemblies to use to create the MEF composition container.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The generated source code or <c>null</c> if none was generated.</returns>
        internal string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable <Type> domainServiceTypes, IEnumerable <string> compositionAssemblies, string codeGeneratorName)
        {
            Debug.Assert(host != null, "host cannot be null");
            Debug.Assert(options != null, "options cannot be null");
            Debug.Assert(domainServiceTypes != null, "domainServiceTypes cannot be null");

            ILogger logger = host as ILogger;
            DomainServiceCatalog catalog = new DomainServiceCatalog(domainServiceTypes, logger);

            return(this.GenerateCode(host, options, catalog, compositionAssemblies, codeGeneratorName));
        }
        /// <summary>
        /// Validates whether the given <see cref="ClientCodeGenerationOptions"/> options are correct.
        /// </summary>
        /// <param name="clientProxyCodeGenerationOptions">Options to validate</param>
        private static void ValidateOptions(ClientCodeGenerationOptions clientProxyCodeGenerationOptions)
        {
            // A null is not acceptable
            if (clientProxyCodeGenerationOptions == null)
            {
                throw new ArgumentNullException(nameof(clientProxyCodeGenerationOptions));
            }

            // The language property may not be null.
            if (String.IsNullOrEmpty(clientProxyCodeGenerationOptions.Language))
            {
                throw new ArgumentException(Resource.Null_Language_Property, nameof(clientProxyCodeGenerationOptions));
            }
        }
        public string GenerateCode(ICodeGenerationHost host, IEnumerable<DomainServiceDescription> descriptions, ClientCodeGenerationOptions options)
        {
            try
            {
                // Initialize all instance state
                this.Initialize(host, descriptions, options);

                // Generate the code
                return this.GenerateProxyClass();
            }
            finally
            {
                // Dispose and release all instance state
                this.Cleanup();
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Creates an <see cref="AppDomain"/> configured for Silverlight code generation.
        /// </summary>
        /// <param name="options">The code generation options.</param>
        internal static void ConfigureAppDomain(ClientCodeGenerationOptions options)
        {
            FrameworkManifest frameworkManifest;

            if (options.ClientProjectTargetPlatform == TargetPlatform.Silverlight)
            {
                frameworkManifest = GetSilverlightFrameworkManifest(options.ClientFrameworkPath);
            }
            else
            {
                frameworkManifest = GetFrameworkManifest(options.ClientFrameworkPath);
            }

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += AppDomainUtilities.ResolveFrameworkAssemblyVersioning;
            AppDomain.CurrentDomain.SetData(FrameworkManifestKey, frameworkManifest);
        }
        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);
        }
        internal void Initialize(ICodeGenerationHost host, IEnumerable<DomainServiceDescription> descriptions, ClientCodeGenerationOptions options)
        {
            if (host == null)
            {
                throw new ArgumentNullException("host");
            }

            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            if (descriptions == null)
            {
                throw new ArgumentNullException("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);
        }
        private void Cleanup()
        {
            // Dispose and release all instance variables
            CodeDomProvider provider = this._provider;

            this._provider = null;
            if (provider != null)
            {
                provider.Dispose();
            }
            this._compileUnit               = null;
            this._namespaces                = null;
            this._enumTypesToGenerate       = null;
            this._domainServiceDescriptions = null;
            this._host    = null;
            this._options = null;
            this._clientProxyCodeGenerationOptions = null;
        }
        /// <summary>
        /// Generates client proxy source code using the specified <paramref name="codeGeneratorName"/> in the context
        /// of the specified <paramref name="host"/>.
        /// </summary>
        /// <param name="host">The host for code generation.</param>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="catalog">The catalog containing the <see cref="OpenRiaServices.DomainServices.Server.DomainService"/> types.</param>
        /// <param name="compositionAssemblies">The optional set of assemblies to use to create the MEF composition container.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The generated source code or <c>null</c> if none was generated.</returns>
        private string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, DomainServiceCatalog catalog, IEnumerable <string> compositionAssemblies, string codeGeneratorName)
        {
            Debug.Assert(host != null, "host cannot be null");
            Debug.Assert(options != null, "options cannot be null");
            Debug.Assert(catalog != null, "catalog cannot be null");

            IEnumerable <DomainServiceDescription> domainServiceDescriptions = catalog.DomainServiceDescriptions;
            IDomainServiceClientCodeGenerator      proxyGenerator            = this.FindCodeGenerator(host, options, compositionAssemblies, codeGeneratorName);
            string generatedCode = null;

            if (proxyGenerator != null)
            {
                try
                {
                    generatedCode = proxyGenerator.GenerateCode(host, domainServiceDescriptions, options);
                }
                catch (Exception ex)
                {
                    // Fatal exceptions are never swallowed or processed
                    if (ex.IsFatal())
                    {
                        throw;
                    }

                    // Any exception from the code generator is caught and reported, otherwise it will
                    // hit the MSBuild backstop and report failure of the custom build task.
                    // It is acceptable to report this exception and "ignore" it because we
                    // are running in a separate AppDomain which will be torn down immediately
                    // after our return.
                    host.LogError(string.Format(CultureInfo.CurrentCulture,
                                                Resource.CodeGenerator_Threw_Exception,
                                                string.IsNullOrEmpty(codeGeneratorName) ? proxyGenerator.GetType().FullName : codeGeneratorName,
                                                options.ClientProjectPath,
                                                ex.Message));
                }
            }

            return(generatedCode);
        }
        /// <summary>
        /// Generates client proxy source code using the generator specified by <paramref name="codeGeneratorName"/>.
        /// </summary>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="parameters">The parameters required to create the <see cref="ISharedCodeService"/>.</param>
        /// <param name="loggingService">The service to use for logging.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The generated source code or <c>null</c> if none was generated.</returns>
        internal string GenerateCode(ClientCodeGenerationOptions options, SharedCodeServiceParameters parameters, ILoggingService loggingService, string codeGeneratorName)
        {
            Debug.Assert(options != null, "options cannot be null");
            Debug.Assert(parameters != null, "parameters cannot be null");
            Debug.Assert(loggingService != null, "loggingService cannot be null");

            try
            {
                AppDomainUtilities.ConfigureAppDomain(options);
                LoadOpenRiaServicesServerAssembly(parameters, loggingService);

                using (SharedCodeService sharedCodeService = new SharedCodeService(parameters, loggingService))
                {
                    CodeGenerationHost host = new CodeGenerationHost(loggingService, sharedCodeService);
                    return this.GenerateCode(host, options, parameters.ServerAssemblies, codeGeneratorName);
                }
            }
            catch (Exception ex)
            {
                // Fatal exceptions are never swallowed or processed
                if (ex.IsFatal())
                {
                    throw;
                }

                // Any exception from the code generator is caught and reported, otherwise it will
                // hit the MSBuild backstop and report failure of the custom build task.
                // It is acceptable to report this exception and "ignore" it because we
                // are running in a separate AppDomain which will be torn down immediately
                // after our return.
                loggingService.LogError(string.Format(CultureInfo.CurrentCulture,
                                                Resource.ClientCodeGenDispatecher_Threw_Exception_Before_Generate,
                                                ex.Message));
                loggingService.LogException(ex);
                return null;
            }
        }
        /// <summary>
        /// Generates client proxy source code using the generator specified by <paramref name="codeGeneratorName"/>.
        /// </summary>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="parameters">The parameters required to create the <see cref="ISharedCodeService"/>.</param>
        /// <param name="loggingService">The service to use for logging.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The generated source code or <c>null</c> if none was generated.</returns>
        internal string GenerateCode(ClientCodeGenerationOptions options, SharedCodeServiceParameters parameters, ILoggingService loggingService, string codeGeneratorName)
        {
            Debug.Assert(options != null, "options cannot be null");
            Debug.Assert(parameters != null, "parameters cannot be null");
            Debug.Assert(loggingService != null, "loggingService cannot be null");

            try
            {
                AppDomainUtilities.ConfigureAppDomain(options);
                LoadOpenRiaServicesServerAssembly(parameters, loggingService);

                using (SharedCodeService sharedCodeService = new SharedCodeService(parameters, loggingService))
                {
                    CodeGenerationHost host = new CodeGenerationHost(loggingService, sharedCodeService);
                    return(this.GenerateCode(host, options, parameters.ServerAssemblies, codeGeneratorName));
                }
            }
            catch (Exception ex)
            {
                // Fatal exceptions are never swallowed or processed
                if (ex.IsFatal())
                {
                    throw;
                }

                // Any exception from the code generator is caught and reported, otherwise it will
                // hit the MSBuild backstop and report failure of the custom build task.
                // It is acceptable to report this exception and "ignore" it because we
                // are running in a separate AppDomain which will be torn down immediately
                // after our return.
                loggingService.LogError(string.Format(CultureInfo.CurrentCulture,
                                                      Resource.ClientCodeGenDispatecher_Threw_Exception_Before_Generate,
                                                      ex.Message));
                loggingService.LogException(ex);
                return(null);
            }
        }
        /// <summary>
        /// Generates client proxy source code using the specified <paramref name="codeGeneratorName"/> in the context
        /// of the specified <paramref name="host"/>.
        /// </summary>
        /// <param name="host">The host for code generation.</param>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="catalog">The catalog containing the <see cref="OpenRiaServices.DomainServices.Server.DomainService"/> types.</param>
        /// <param name="compositionAssemblies">The optional set of assemblies to use to create the MEF composition container.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The generated source code or <c>null</c> if none was generated.</returns>
        private string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, DomainServiceCatalog catalog, IEnumerable<string> compositionAssemblies, string codeGeneratorName)
        {
            Debug.Assert(host != null, "host cannot be null");
            Debug.Assert(options != null, "options cannot be null");
            Debug.Assert(catalog != null, "catalog cannot be null");

            IEnumerable<DomainServiceDescription> domainServiceDescriptions = catalog.DomainServiceDescriptions;
            IDomainServiceClientCodeGenerator proxyGenerator = this.FindCodeGenerator(host, options, compositionAssemblies, codeGeneratorName);
            string generatedCode = null;

            if (proxyGenerator != null)
            {
                try
                {
                    generatedCode = proxyGenerator.GenerateCode(host, domainServiceDescriptions, options);
                }
                catch (Exception ex)
                {
                    // Fatal exceptions are never swallowed or processed
                    if (ex.IsFatal())
                    {
                        throw;
                    }

                    // Any exception from the code generator is caught and reported, otherwise it will
                    // hit the MSBuild backstop and report failure of the custom build task.
                    // It is acceptable to report this exception and "ignore" it because we
                    // are running in a separate AppDomain which will be torn down immediately
                    // after our return.
                    host.LogError(string.Format(CultureInfo.CurrentCulture, 
                                                    Resource.CodeGenerator_Threw_Exception, 
                                                    string.IsNullOrEmpty(codeGeneratorName) ? proxyGenerator.GetType().FullName : codeGeneratorName,
                                                    options.ClientProjectPath,
                                                    ex.Message));
                }
            }

            return generatedCode;
        }
Ejemplo n.º 15
0
 // Locates the code generator registered to work with the language for the given options
 internal static IDomainServiceClientCodeGenerator CreateCodeGenerator(ICodeGenerationHost host, ClientCodeGenerationOptions options)
 {
     using (ClientCodeGenerationDispatcher dispatcher = new ClientCodeGenerationDispatcher())
     {
         IDomainServiceClientCodeGenerator generator = dispatcher.FindCodeGenerator(host, options, /*compositionAssemblies*/ null, /*codeGeneratorName*/ null);
         return generator;
     }
 }
        /// <summary>
        /// Generates the client proxies.
        /// </summary>
        /// <remarks>
        /// This method validates the presence of the necessary input server assemblies and
        /// then invokes the code generation logic to create a file containing the client
        /// proxies for the discovered server's Business Objects.  If the file already exists
        /// and is newer than the inputs, this method does nothing.
        /// <para>In all success paths, the client proxy file will be added to the list of generated
        /// files so the custom targets file can add them to the @Compile collection.</para>
        /// </remarks>
        internal void GenerateClientProxies()
        {
            IEnumerable<string> assemblies = this.GetServerAssemblies();
            IEnumerable<string> references = this.GetReferenceAssemblies();

            // We will load all input and reference assemblies
            IEnumerable<string> assembliesToLoad = assemblies.Concat(references);

            // It is a failure if any of the reference assemblies are missing
            if (!this.EnsureAssembliesExist(references))
            {
                return;
            }

            // Obtain the name of the output assembly from the server project
            // (it is currently a collection to be consistent with MSBuild item collections).
            // If there is no output assembly, log a warning.
            // We consider this non-fatal because an Intellisense build can trivially
            // encounter this immediately after creating a new Open Ria Services application
            string assemblyFile = assemblies.FirstOrDefault();
            if (string.IsNullOrEmpty(assemblyFile) || !File.Exists(assemblyFile))
            {
                string serverProjectFile = Path.GetFileName(this.ServerProjectPath);
                this.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_No_Input_Assemblies, serverProjectFile));
                return;
            }

            // Make it an absolute path and append the language-specific extension
            string generatedFileName = Path.Combine(this.GeneratedCodePath, this.GenerateProxyFileName(assemblyFile));

            // We maintain cached lists of references we used in prior builds.
            // Determine whether our current inputs are different from the last build that generated code.
            bool serverReferencesChanged = this.HaveReferencesChanged(this.ServerReferenceListPath(), assembliesToLoad, this.ServerProjectDirectory);
            bool clientReferencesChanged = this.HaveReferencesChanged(this.ClientReferenceListPath(), this.ClientReferenceAssembliesNormalized, this.ClientProjectDirectory);

            // Any change in the assembly references for either client or server are grounds to re-gen code.
            // Developer note -- we use the fact that the inputs have changed to trigger the full code-gen pass.
            bool needToGenerate = serverReferencesChanged || clientReferencesChanged;

            // Also trigger code-gen if the generated file is absent or empty.
            // Technically, the reference-change test is enough, but experience has shown users
            // manually delete the GeneratedCode folder and expect the next build to recreate it.
            // Therefore, its absence always triggers a code-gen pass, even though this has the
            // negative perf impact of causing a full code gen pass everytime until errors have been
            // resolved.
            if (!needToGenerate)
            {
                FileInfo fileInfo = new FileInfo(generatedFileName);
                bool fileExists = fileInfo.Exists;
                needToGenerate = (!fileExists || (fileInfo.Length == 0));

                // If we determine the generated
                // file has been touched since we last analyzed our server references, it is an indication
                // the user modified the generated file.  So force a code gen and
                // force a rewrite of the server reference file to short circuit this same code next build.
                if (!needToGenerate && fileExists && File.Exists(this.ServerReferenceListPath()))
                {
                    if (File.GetLastWriteTime(generatedFileName) > File.GetLastWriteTime(this.ServerReferenceListPath()))
                    {
                        needToGenerate = true;
                        serverReferencesChanged = true;
                    }
                }
            }

            // If we need to generate the file, do that now
            if (needToGenerate)
            {
                // Warn the user if the server assembly has no PDB
                this.WarnIfNoPdb(assemblyFile);

                string generatedFileContent = string.Empty;

                // We override the default parameter to ask for ForceDebug, otherwise the PDB is not copied.
                ClientBuildManagerParameter cbmParameter = new ClientBuildManagerParameter()
                {
                    PrecompilationFlags = PrecompilationFlags.ForceDebug,
                };

                string sourceDir = this.ServerProjectDirectory;
                string targetDir = null;

                using (ClientBuildManager cbm = new ClientBuildManager(/* appVDir */ "/", sourceDir, targetDir, cbmParameter))
                {
                    // Capture the list of assemblies to load into an array to marshal across AppDomains
                    string[] assembliesToLoadArray = assembliesToLoad.ToArray();

                    // Create the list of options we will pass to the generator.
                    // This instance is serializable and can cross AppDomains
                    ClientCodeGenerationOptions options = new ClientCodeGenerationOptions()
                    {
                        Language = this.Language,
                        ClientFrameworkPath = this.ClientFrameworkPath,
                        ClientRootNamespace = this.ClientProjectRootNamespace,
                        ServerRootNamespace = this.ServerProjectRootNameSpace,
                        ClientProjectPath = this.ClientProjectPath,
                        ServerProjectPath = this.ServerProjectPath,
                        IsApplicationContextGenerationEnabled = this.IsClientApplicationAsBool,
                        UseFullTypeNames = this.UseFullTypeNamesAsBool,
                        ClientProjectTargetPlatform = this.ClientTargetPlatform,
                    };

                    // The other AppDomain gets a logger that will log back to this AppDomain
                    CrossAppDomainLogger logger = new CrossAppDomainLogger((ILoggingService)this);

                    // Compose the parameters we will pass to the other AppDomain to create the SharedCodeService
                    SharedCodeServiceParameters sharedCodeServiceParameters = this.CreateSharedCodeServiceParameters(assembliesToLoadArray);

                    // Surface a HttpRuntime initialization error that would otherwise manifest as a NullReferenceException
                    // This can occur when the build environment is configured incorrectly
                    if (System.Web.Hosting.HostingEnvironment.InitializationException != null)
                    {
                        throw new InvalidOperationException(
                            Resource.HttpRuntimeInitializationError,
                            System.Web.Hosting.HostingEnvironment.InitializationException);
                    }

                    // Create the "dispatcher" in the 2nd AppDomain.
                    // This object will find and invoke the appropriate code generator
                    using (ClientCodeGenerationDispatcher dispatcher = (ClientCodeGenerationDispatcher)cbm.CreateObject(typeof(ClientCodeGenerationDispatcher), false))
                    {
                        // Transfer control to the dispatcher in the 2nd AppDomain to locate and invoke
                        // the appropriate code generator.
                        generatedFileContent = dispatcher.GenerateCode(options, sharedCodeServiceParameters, logger, this.CodeGeneratorName);
                    }
                }

                // Tell the user where we are writing the generated code
                if (!string.IsNullOrEmpty(generatedFileContent))
                {
                    this.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.Writing_Generated_Code, generatedFileName));
                }

                // If VS is hosting us, write to its TextBuffer, else simply write to disk
                // If the file is empty, delete it.
                this.WriteOrDeleteFileToVS(generatedFileName, generatedFileContent, /*forceWriteToFile*/ false);
            }
            else
            {
                // Log a message telling user we are skipping code gen because the inputs are older than the generated code
                this.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Skipping_CodeGen, generatedFileName));
            }

            // We unconditionally declare the file was generated if it exists
            // on disk after this method finishes, even if it was not modified.
            // This permits the targets file to add it to the @COMPILE collection.
            // This also prevents adding it to the list if it was deleted above
            if (File.Exists(generatedFileName))
            {
                this.AddGeneratedFile(generatedFileName);
            }

            // Write out reference lists if they have changed
            if (serverReferencesChanged)
            {
                this.WriteReferenceList(this.ServerReferenceListPath(), assembliesToLoad, this.ServerProjectDirectory);
            }

            if (clientReferencesChanged)
            {
                this.WriteReferenceList(this.ClientReferenceListPath(), this.ClientReferenceAssembliesNormalized, this.ClientProjectDirectory);
            }

            return;
        }
        /// <summary>
        /// Locates and returns the <see cref="IDomainServiceClientCodeGenerator"/> to use to generate client proxies
        /// for the specified <paramref name="options"/>.
        /// </summary>
        /// <param name="host">The host for code generation.</param>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="compositionAssemblies">The optional set of assemblies to use to create the MEF composition container.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The code generator to use, or <c>null</c> if a matching one could not be found.</returns>
        internal IDomainServiceClientCodeGenerator FindCodeGenerator(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable<string> compositionAssemblies, string codeGeneratorName)
        {
            Debug.Assert(host != null, "host cannot be null");
            Debug.Assert(options != null, "options cannot be null");

            if (string.IsNullOrEmpty(options.Language))
            {
                throw new ArgumentException(Resource.Null_Language_Property, "options");
            }

            IDomainServiceClientCodeGenerator generator = null;

            // Try to load the code generator directly if given an assembly qualified name.
            // We insist on at least one comma in the name to know this is an assembly qualified name.
            // Otherwise, we might succeed in loading a dotted name that happens to be in our assembly,
            // such as the default CodeDom generator.
            if (!string.IsNullOrEmpty(codeGeneratorName) && codeGeneratorName.Contains(','))
            {
                Type codeGeneratorType = Type.GetType(codeGeneratorName, /*throwOnError*/ false);
                if (codeGeneratorType != null)
                {
                    if (!typeof(IDomainServiceClientCodeGenerator).IsAssignableFrom(codeGeneratorType))
                    {
                        // If generator is of the incorrect type, we will still allow the MEF approach below
                        // to find a better one.   This path could be exercised by inadvertantly using a name
                        // that happened to load some random type that was not a code generator.
                        host.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.Code_Generator_Incorrect_Type, codeGeneratorName));
                    }
                    else
                    {
                        try
                        {
                            generator = Activator.CreateInstance(codeGeneratorType) as IDomainServiceClientCodeGenerator;
                        }
                        catch (Exception e)
                        {
                            // The open catch of Exception is acceptable because we unconditionally report
                            // the error and are running in a separate AppDomain.
                            if (e.IsFatal())
                            {
                                throw;
                            }
                            host.LogError(string.Format(CultureInfo.CurrentCulture, Resource.Code_Generator_Instantiation_Error, codeGeneratorName,  e.Message));
                        }
                    }
                }
            }

            if (generator == null)
            {
                // Create the MEF composition container (once only) from the assemblies we are analyzing
                this.CreateCompositionContainer(compositionAssemblies, host as ILogger);

                // The following property is filled by MEF by the line above.
                if (this.DomainServiceClientCodeGenerators != null && this.DomainServiceClientCodeGenerators.Any())
                {
                    // Select only those registered for the required language
                    IEnumerable<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>> allImportsForLanguage =
                        this.DomainServiceClientCodeGenerators.Where(i => string.Equals(options.Language, i.Metadata.Language, StringComparison.OrdinalIgnoreCase));

                    Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata> lazyImport = null;

                    // If client specified a specific generator, use that one.
                    // If it cannot be found, log an error to explain the problem.
                    // If multiple with that name are found, log an error and explain the problem.
                    // We consider this an error because the user has explicitly named a generator,
                    // meaning they would not expect the default to be used.
                    if (!string.IsNullOrEmpty(codeGeneratorName))
                    {
                        IEnumerable<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>> allImportsForLanguageAndName = allImportsForLanguage.Where(i => string.Equals(i.Metadata.GeneratorName, codeGeneratorName, StringComparison.OrdinalIgnoreCase));

                        int numberOfMatchingGenerators = allImportsForLanguageAndName.Count();

                        // No generator with that name was found.  Log an error and explain how to register one.
                        if (numberOfMatchingGenerators == 0)
                        {
                            host.LogError(string.Format(CultureInfo.CurrentCulture,
                                                        Resource.Code_Generator_Not_Found, 
                                                        codeGeneratorName, 
                                                        options.Language, 
                                                        options.ServerProjectPath, 
                                                        options.ClientProjectPath,
                                                        CodeDomClientCodeGenerator.GeneratorName));
                        }
                        else if (numberOfMatchingGenerators == 1)
                        {
                            // Exactly one was found -- take it
                            lazyImport = allImportsForLanguageAndName.First();
                        }
                        else
                        {
                            // Multiple with that name were found.  Explain how to remove some of them or
                            // explicitly name one.
                            StringBuilder sb = new StringBuilder();
                            foreach (var import in allImportsForLanguageAndName.OrderBy(i => i.Value.GetType().FullName))
                            {
                                sb.AppendLine("    " + import.Value.GetType().FullName);
                            }
                            host.LogError(string.Format(CultureInfo.CurrentCulture, 
                                                        Resource.Multiple_Named_Code_Generators, 
                                                        codeGeneratorName,
                                                        options.Language, 
                                                        sb.ToString(),
                                                        options.ServerProjectPath, 
                                                        options.ClientProjectPath,
                                                        allImportsForLanguageAndName.First().Value.GetType().AssemblyQualifiedName));
                        }
                    }
                    else
                    {
                        // We are here if no generator name was specified.
                        // If only one import matched the language, we have it.
                        // This is the most common path to discovery of our own CodeDom generator
                        // but will work equally well when it replaced.
                        if (allImportsForLanguage.Count() == 1)
                        {
                            lazyImport = allImportsForLanguage.First();
                        }
                        else
                        {
                            // Multiple custom generators exist, but a specific generator name was not provided.
                            // Look for any custom generators other than our default CodeDom one.
                            // If we find there is only one custom generator registered, we use that one rather than the default
                            IEnumerable<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>> customGeneratorImports =
                                allImportsForLanguage.Where(i => !string.Equals(CodeDomClientCodeGenerator.GeneratorName, i.Metadata.GeneratorName, StringComparison.OrdinalIgnoreCase));

                            int generatorCount = customGeneratorImports.Count();

                            // Exactly 1 custom generator that is not the default -- take it
                            if (generatorCount == 1)
                            {
                                lazyImport = customGeneratorImports.First();
                                host.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.Using_Custom_Code_Generator, lazyImport.Metadata.GeneratorName));
                            }
                            else if (generatorCount != 0)
                            {
                                // Multiple generators are available but we have insufficient information
                                // to choose one.  Log an warning and use the default
                                StringBuilder sb = new StringBuilder();

                                // Sort for unit test predictability
                                IEnumerable<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>> orderedCustomGenerators = customGeneratorImports.OrderBy(i => i.Metadata.GeneratorName);
                                foreach (var import in orderedCustomGenerators)
                                {
                                    sb.AppendLine("    " + import.Metadata.GeneratorName);
                                }

                                host.LogWarning(string.Format(CultureInfo.CurrentCulture, 
                                                                Resource.Multiple_Custom_Code_Generators_Using_Default, 
                                                                options.Language, sb.ToString(), 
                                                                options.ClientProjectPath, 
                                                                orderedCustomGenerators.First().Metadata.GeneratorName,
                                                                CodeDomClientCodeGenerator.GeneratorName));

                                // Pick the default.  There should be one, but if not, the calling methods will detect and report a problem.
                                lazyImport = allImportsForLanguage.FirstOrDefault(i => string.Equals(CodeDomClientCodeGenerator.GeneratorName, i.Metadata.GeneratorName, StringComparison.OrdinalIgnoreCase));
                            }
                        }
                    }

                    generator = lazyImport == null ? null : lazyImport.Value;
                }
            }

            return generator;
        }
        /// <summary>
        /// Validates whether the given <see cref="ClientCodeGenerationOptions"/> options are correct.
        /// </summary>
        /// <param name="clientProxyCodeGenerationOptions">Options to validate</param>
        private static void ValidateOptions(ClientCodeGenerationOptions clientProxyCodeGenerationOptions)
        {
            // A null is not acceptable
            if (clientProxyCodeGenerationOptions == null)
            {
                throw new ArgumentNullException("clientProxyCodeGenerationOptions");
            }

            // The language property may not be null.
            if (String.IsNullOrEmpty(clientProxyCodeGenerationOptions.Language))
            {
                throw new ArgumentException(Resource.Null_Language_Property, "clientProxyCodeGenerationOptions");
            }
        }
 private void Cleanup()
 {
     // Dispose and release all instance variables
     CodeDomProvider provider = this._provider;
     this._provider = null;
     if (provider != null)
     {
         provider.Dispose();
     }
     this._compileUnit = null;
     this._namespaces = null;
     this._enumTypesToGenerate = null;
     this._domainServiceDescriptions = null;
     this._host = null;
     this._options = null;
     this._clientProxyCodeGenerationOptions = null;
 }
 public string GenerateCode(ICodeGenerationHost codeGenerationHost, IEnumerable<DomainServiceDescription> domainServiceDescriptions, ClientCodeGenerationOptions options)
 {
     throw new NotImplementedException();
 }
        public string GenerateCode(ICodeGenerationHost host, IEnumerable <DomainServiceDescription> descriptions, ClientCodeGenerationOptions options)
        {
            try
            {
                // Initialize all instance state
                this.Initialize(host, descriptions, options);

                // Generate the code
                return(this.GenerateProxyClass());
            }
            finally
            {
                // Dispose and release all instance state
                this.Cleanup();
            }
        }
        /// <summary>
        /// This method is part of the <see cref="IDomainServiceClientCodeGenerator" /> interface. The RIA Services Code Generation process uses this method as the entry point into the code generator.
        /// </summary>
        /// <param name="codeGenerationHost">The code generation host for this instance.</param>
        /// <param name="domainServiceDescriptions">The list of all the DomainServiceDescription objects.</param>
        /// <param name="options">The code generation objects.</param>
        /// <returns>The generated code.</returns>
        public string GenerateCode(ICodeGenerationHost codeGenerationHost, IEnumerable<DomainServiceDescription> domainServiceDescriptions, ClientCodeGenerationOptions options)
        {
            this._codeGenerationHost = codeGenerationHost;
            this._domainServiceDescriptions = domainServiceDescriptions;
            this._options = options;

            this._enumTypesToGenerate = new HashSet<Type>();

            if (this.EntityGenerator == null)
            {
                this.CodeGenerationHost.LogError(string.Format(CultureInfo.CurrentCulture, TextTemplateResource.EntityGeneratorNotFound));
            }
            if (this.ComplexObjectGenerator == null)
            {
                this.CodeGenerationHost.LogError(string.Format(CultureInfo.CurrentCulture, TextTemplateResource.ComplexObjectGeneratorNotFound));
            }
            if (this.DomainContextGenerator == null)
            {
                this.CodeGenerationHost.LogError(string.Format(CultureInfo.CurrentCulture, TextTemplateResource.DomainContextGeneratorNotFound));
            }
            if (this.WebContextGenerator == null)
            {
                this.CodeGenerationHost.LogError(string.Format(CultureInfo.CurrentCulture, TextTemplateResource.WebContextGeneratorNotFound));
            }
            if (this.EnumGenerator == null)
            {
                this.CodeGenerationHost.LogError(string.Format(CultureInfo.CurrentCulture, TextTemplateResource.EnumGeneratorNotFound));
            }

            if (!this.CodeGenerationHost.HasLoggedErrors)
            {
                return this.GenerateCode();
            }
            return null;
        }
Ejemplo n.º 23
0
 internal static ClientCodeGenerationOptions CreateMockCodeGenContext(string language, bool useFullTypeNames)
 {
     ClientCodeGenerationOptions options = new ClientCodeGenerationOptions()
     {
         Language = language,
         ClientProjectPath = "MockProject.csproj",
         UseFullTypeNames = useFullTypeNames
     };
     return options;
 }
Ejemplo n.º 24
0
        /// <summary>
        /// Validates code gen for a specific language by comparing it against a file containing the expected output.
        /// </summary>
        /// <param name="codeGenOptions">The options specifying the type of validation to perform</param>
        /// <returns>A command that updates the comparison file</returns>
        internal static string ValidateLanguageCodeGen(CodeGenValidationOptions codeGenOptions)
        {
            Assert.IsFalse(string.IsNullOrEmpty(codeGenOptions.Language));

            string outDataDir = TestHelper.GetOutputTestDataDir(codeGenOptions.RelativeDeployDir);
            string extension = TestHelper.ExtensionFromLanguage(codeGenOptions.Language);
            string diffMessage = string.Empty;

            // Compose the abs path to where the test file got deployed by MSTest
            string referenceFileName = TestHelper.GetTestFileName(codeGenOptions.RelativeTestDir, codeGenOptions.BaseReferenceFileName + extension);

            Assert.IsTrue(File.Exists(referenceFileName), "Cannot find reference file " + referenceFileName);
            string generatedCode = string.Empty;

            ClientCodeGenerationOptions options = new ClientCodeGenerationOptions()
            {
                Language = codeGenOptions.Language,
                ClientRootNamespace = codeGenOptions.RootNamespace,
                ClientProjectPath = "MockProject.proj",
                IsApplicationContextGenerationEnabled = codeGenOptions.GenerateApplicationContexts,
                UseFullTypeNames = codeGenOptions.UseFullTypeNames,
                ClientProjectTargetPlatform =  TargetPlatform.Silverlight
            };

            MockCodeGenerationHost host = TestHelper.CreateMockCodeGenerationHost(codeGenOptions.Logger, codeGenOptions.SharedCodeService);
            ILogger logger = host as ILogger;
            DomainServiceCatalog catalog = new DomainServiceCatalog(codeGenOptions.DomainServiceTypes, logger);
            IDomainServiceClientCodeGenerator generator;
            using (ClientCodeGenerationDispatcher dispatcher = new ClientCodeGenerationDispatcher())
            {
                generator = dispatcher.FindCodeGenerator(host, options, /*compositionAssemblies*/ null, /*codeGeneratorName*/ null);
            }
            Assert.IsNotNull(generator, "Failed to find a code generator");
            generatedCode = generator.GenerateCode(host, catalog.DomainServiceDescriptions, options);

            ConsoleLogger consoleLogger = logger as ConsoleLogger;
            string errors = consoleLogger == null ? "" : consoleLogger.Errors;
            Assert.IsTrue(generatedCode.Length > 0, "No code was generated: " + errors);

            // Dump the generated code into a file for comparison
            bool isCSharp = options.Language.Equals("C#", StringComparison.InvariantCultureIgnoreCase);
            string generatedFileName = Path.Combine(outDataDir, Path.GetFileName(referenceFileName) + ".testgen");
            File.WriteAllText(generatedFileName, generatedCode);

            // TODO: (ron M3) Solve inability to get right MSBuild after checkin
            // First see if we compile
            List<string> referenceAssemblies = CompilerHelper.GetSilverlightClientAssemblies(codeGenOptions.RelativeDeployDir);
            List<string> files = new List<string>();

            files.Add(generatedFileName);

            // Unconditionally force generation of Xml doc comments to catch errors
            string documentationFile = Path.GetTempFileName();

            try
            {
                if (isCSharp)
                {
                    files.AddRange(codeGenOptions.SharedFiles.Where(sharedFile => Path.GetExtension(sharedFile).Equals(".cs")));
                    CompilerHelper.CompileCSharpSource(files, referenceAssemblies, documentationFile);
                }
                else
                {
                    files.AddRange(codeGenOptions.SharedFiles.Where(sharedFile => Path.GetExtension(sharedFile).Equals(".vb")));
                    CompilerHelper.CompileVisualBasicSource(files, referenceAssemblies, options.ClientRootNamespace, documentationFile);
                }
            }
            finally
            {
                File.Delete(documentationFile);
            }

            // Do the diff
            if (codeGenOptions.FailOnDiff)
            {
                TestHelper.ValidateFilesEqual(codeGenOptions.RelativeTestDir, codeGenOptions.RelativeDeployDir, generatedFileName, referenceFileName, codeGenOptions.Language);
            }
            else
            {
                TestHelper.FilesMatch(codeGenOptions.RelativeTestDir, codeGenOptions.RelativeDeployDir, generatedFileName, referenceFileName, codeGenOptions.Language, out diffMessage);
            }

            return diffMessage;
        }
        /// <summary>
        /// Generates client proxy source code using the specified <paramref name="codeGeneratorName"/> in the context
        /// of the specified <paramref name="host"/>.
        /// </summary>
        /// <param name="host">The host for code generation.</param>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="domainServiceTypes">The set of <see cref="OpenRiaServices.DomainServices.Server.DomainService"/> types for which to generate code.</param>
        /// <param name="compositionAssemblies">The optional set of assemblies to use to create the MEF composition container.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The generated source code or <c>null</c> if none was generated.</returns>
        internal string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable<Type> domainServiceTypes, IEnumerable<string> compositionAssemblies, string codeGeneratorName)
        {
            Debug.Assert(host != null, "host cannot be null");
            Debug.Assert(options != null, "options cannot be null");
            Debug.Assert(domainServiceTypes != null, "domainServiceTypes cannot be null");

            ILogger logger = host as ILogger;
            DomainServiceCatalog catalog = new DomainServiceCatalog(domainServiceTypes, logger);
            return this.GenerateCode(host, options, catalog, compositionAssemblies, codeGeneratorName);
        }
        /// <summary>
        /// Locates and returns the <see cref="IDomainServiceClientCodeGenerator"/> to use to generate client proxies
        /// for the specified <paramref name="options"/>.
        /// </summary>
        /// <param name="host">The host for code generation.</param>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="compositionAssemblies">The optional set of assemblies to use to create the MEF composition container.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The code generator to use, or <c>null</c> if a matching one could not be found.</returns>
        internal IDomainServiceClientCodeGenerator FindCodeGenerator(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable <string> compositionAssemblies, string codeGeneratorName)
        {
            Debug.Assert(host != null, "host cannot be null");
            Debug.Assert(options != null, "options cannot be null");

            if (string.IsNullOrEmpty(options.Language))
            {
                throw new ArgumentException(Resource.Null_Language_Property, nameof(options));
            }

            IDomainServiceClientCodeGenerator generator = null;

            // Try to load the code generator directly if given an assembly qualified name.
            // We insist on at least one comma in the name to know this is an assembly qualified name.
            // Otherwise, we might succeed in loading a dotted name that happens to be in our assembly,
            // such as the default CodeDom generator.
            if (!string.IsNullOrEmpty(codeGeneratorName) && codeGeneratorName.Contains(','))
            {
                Type codeGeneratorType = Type.GetType(codeGeneratorName, /*throwOnError*/ false);
                if (codeGeneratorType != null)
                {
                    if (!typeof(IDomainServiceClientCodeGenerator).IsAssignableFrom(codeGeneratorType))
                    {
                        // If generator is of the incorrect type, we will still allow the MEF approach below
                        // to find a better one.   This path could be exercised by inadvertantly using a name
                        // that happened to load some random type that was not a code generator.
                        host.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.Code_Generator_Incorrect_Type, codeGeneratorName));
                    }
                    else
                    {
                        try
                        {
                            generator = Activator.CreateInstance(codeGeneratorType) as IDomainServiceClientCodeGenerator;
                        }
                        catch (Exception e)
                        {
                            // The open catch of Exception is acceptable because we unconditionally report
                            // the error and are running in a separate AppDomain.
                            if (e.IsFatal())
                            {
                                throw;
                            }
                            host.LogError(string.Format(CultureInfo.CurrentCulture, Resource.Code_Generator_Instantiation_Error, codeGeneratorName, e.Message));
                        }
                    }
                }
            }

            if (generator == null)
            {
                // Create the MEF composition container (once only) from the assemblies we are analyzing
                this.CreateCompositionContainer(compositionAssemblies, host as ILogger);

                // The following property is filled by MEF by the line above.
                if (this.DomainServiceClientCodeGenerators != null && this.DomainServiceClientCodeGenerators.Any())
                {
                    // Select only those registered for the required language
                    IEnumerable <Lazy <IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata> > allImportsForLanguage =
                        this.DomainServiceClientCodeGenerators.Where(i => string.Equals(options.Language, i.Metadata.Language, StringComparison.OrdinalIgnoreCase));

                    Lazy <IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata> lazyImport = null;

                    // If client specified a specific generator, use that one.
                    // If it cannot be found, log an error to explain the problem.
                    // If multiple with that name are found, log an error and explain the problem.
                    // We consider this an error because the user has explicitly named a generator,
                    // meaning they would not expect the default to be used.
                    if (!string.IsNullOrEmpty(codeGeneratorName))
                    {
                        IEnumerable <Lazy <IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata> > allImportsForLanguageAndName = allImportsForLanguage.Where(i => string.Equals(i.Metadata.GeneratorName, codeGeneratorName, StringComparison.OrdinalIgnoreCase));

                        int numberOfMatchingGenerators = allImportsForLanguageAndName.Count();

                        // No generator with that name was found.  Log an error and explain how to register one.
                        if (numberOfMatchingGenerators == 0)
                        {
                            host.LogError(string.Format(CultureInfo.CurrentCulture,
                                                        Resource.Code_Generator_Not_Found,
                                                        codeGeneratorName,
                                                        options.Language,
                                                        options.ServerProjectPath,
                                                        options.ClientProjectPath,
                                                        CodeDomClientCodeGenerator.GeneratorName));
                        }
                        else if (numberOfMatchingGenerators == 1)
                        {
                            // Exactly one was found -- take it
                            lazyImport = allImportsForLanguageAndName.First();
                        }
                        else
                        {
                            // Multiple with that name were found.  Explain how to remove some of them or
                            // explicitly name one.
                            StringBuilder sb = new StringBuilder();
                            foreach (var import in allImportsForLanguageAndName.OrderBy(i => i.Value.GetType().FullName))
                            {
                                sb.AppendLine("    " + import.Value.GetType().FullName);
                            }
                            host.LogError(string.Format(CultureInfo.CurrentCulture,
                                                        Resource.Multiple_Named_Code_Generators,
                                                        codeGeneratorName,
                                                        options.Language,
                                                        sb.ToString(),
                                                        options.ServerProjectPath,
                                                        options.ClientProjectPath,
                                                        allImportsForLanguageAndName.First().Value.GetType().AssemblyQualifiedName));
                        }
                    }
                    else
                    {
                        // We are here if no generator name was specified.
                        // If only one import matched the language, we have it.
                        // This is the most common path to discovery of our own CodeDom generator
                        // but will work equally well when it replaced.
                        if (allImportsForLanguage.Count() == 1)
                        {
                            lazyImport = allImportsForLanguage.First();
                        }
                        else
                        {
                            // Multiple custom generators exist, but a specific generator name was not provided.
                            // Look for any custom generators other than our default CodeDom one.
                            // If we find there is only one custom generator registered, we use that one rather than the default
                            IEnumerable <Lazy <IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata> > customGeneratorImports =
                                allImportsForLanguage.Where(i => !string.Equals(CodeDomClientCodeGenerator.GeneratorName, i.Metadata.GeneratorName, StringComparison.OrdinalIgnoreCase));

                            int generatorCount = customGeneratorImports.Count();

                            // Exactly 1 custom generator that is not the default -- take it
                            if (generatorCount == 1)
                            {
                                lazyImport = customGeneratorImports.First();
                                host.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.Using_Custom_Code_Generator, lazyImport.Metadata.GeneratorName));
                            }
                            else if (generatorCount != 0)
                            {
                                // Multiple generators are available but we have insufficient information
                                // to choose one.  Log an warning and use the default
                                StringBuilder sb = new StringBuilder();

                                // Sort for unit test predictability
                                IEnumerable <Lazy <IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata> > orderedCustomGenerators = customGeneratorImports.OrderBy(i => i.Metadata.GeneratorName);
                                foreach (var import in orderedCustomGenerators)
                                {
                                    sb.AppendLine("    " + import.Metadata.GeneratorName);
                                }

                                host.LogWarning(string.Format(CultureInfo.CurrentCulture,
                                                              Resource.Multiple_Custom_Code_Generators_Using_Default,
                                                              options.Language, sb.ToString(),
                                                              options.ClientProjectPath,
                                                              orderedCustomGenerators.First().Metadata.GeneratorName,
                                                              CodeDomClientCodeGenerator.GeneratorName));

                                // Pick the default.  There should be one, but if not, the calling methods will detect and report a problem.
                                lazyImport = allImportsForLanguage.FirstOrDefault(i => string.Equals(CodeDomClientCodeGenerator.GeneratorName, i.Metadata.GeneratorName, StringComparison.OrdinalIgnoreCase));
                            }
                        }
                    }

                    generator = lazyImport == null ? null : lazyImport.Value;
                }
            }

            return(generator);
        }
Ejemplo n.º 27
0
 // Invokes the code generator discovered via the host and options
 internal static string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable<Type> domainServiceTypes)
 {
     IDomainServiceClientCodeGenerator generator = CreateCodeGenerator(host, options);
     DomainServiceCatalog catalog = new DomainServiceCatalog(domainServiceTypes, host as ILogger);
     return generator.GenerateCode(host, catalog.DomainServiceDescriptions, options);
 }
        /// <summary>
        /// Generates client proxy source code using the specified <paramref name="codeGeneratorName"/> in the context
        /// of the specified <paramref name="host"/>.
        /// </summary>
        /// <param name="host">The host for code generation.</param>
        /// <param name="options">The options to use for code generation.</param>
        /// <param name="assembliesToLoad">The set of server assemblies to use for analysis and composition.</param>
        /// <param name="codeGeneratorName">Optional generator name.  A <c>null</c> or empty value will select the default generator.</param>
        /// <returns>The generated source code or <c>null</c> if none was generated.</returns>
        internal string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable<string> assembliesToLoad, string codeGeneratorName)
        {
            Debug.Assert(host != null, "host cannot be null");
            Debug.Assert(options != null, "options cannot be null");
            Debug.Assert(assembliesToLoad != null, "assembliesToLoad cannot be null");

            ILogger logger = host as ILogger;
            DomainServiceCatalog catalog = new DomainServiceCatalog(assembliesToLoad, logger);
            return this.GenerateCode(host, options, catalog, assembliesToLoad, codeGeneratorName);
        }