private static CommandHost CreateWorkerAppDomainWithHost(string virtualPath, string physicalPath, Type hostType)
 {
     var clientBuildManager = new ClientBuildManager(virtualPath, physicalPath);
     // Fix for http://Coevery.codeplex.com/workitem/17920
     // By forcing the CBM to build App_Code, etc, we ensure that the ASP.NET BuildManager
     // is in a state where it can safely (i.e. in a multi-threaded safe way) process
     // multiple concurrent calls to "GetCompiledAssembly".
     clientBuildManager.CompileApplicationDependencies();
     return (CommandHost)clientBuildManager.CreateObject(hostType, false);
 }
        /// <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>
        /// Validates the integrity of the <see cref="OpenRiaServices.DomainServices.Server.DomainService"/>s exposed by the target Web Application
        /// in a separate <see cref="AppDomain"/>
        /// </summary>
        private void ValidateDomainServices()
        {
            IEnumerable<string> assemblies =
                new[] { this.GetFileName(this.Assembly) } .Concat(
                this.ReferenceAssemblies.Select(i => this.GetFileName(i)));

            this.WarnIfAssembliesDontExist(assemblies);

            using (ClientBuildManager cbm = new ClientBuildManager(/* appVirtualDir */ "/", this.ProjectDirectory))
            {
                // 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);
                }

                using (DomainServiceValidator validator = (DomainServiceValidator)cbm.CreateObject(typeof(DomainServiceValidator), false))
                {
                    // Transfer control to Web Application AppDomain to invoke the validator
                    validator.Validate(assemblies.ToArray(), this.LoggingService);
                }
            }
        }