internal void Configure(ClientBuildManager client) { // Add a pending call to make sure our thread doesn't get killed AddPendingCall(); try { _virtualPathProvider = new ClientVirtualPathProvider(); HostingEnvironment.RegisterVirtualPathProviderInternal(_virtualPathProvider); _client = client; // Type description provider required for multitargeting compilation support in VS. if (_client.CBMTypeDescriptionProviderBridge != null) { TargetFrameworkUtil.CBMTypeDescriptionProviderBridge = _client.CBMTypeDescriptionProviderBridge; } // start watching for app domain unloading _onAppDomainUnload = new EventHandler(OnAppDomainUnload); Thread.GetDomain().DomainUnload += _onAppDomainUnload; _buildManager = BuildManager.TheBuildManager; // Listen to appdomain shutdown. HttpRuntime.AppDomainShutdown += new BuildManagerHostUnloadEventHandler(this.OnAppDomainShutdown); } finally { RemovePendingCall(); } }
private void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) { Thread.GetDomain().DomainUnload -= _onAppDomainUnload; if (_client != null) { _client.OnAppDomainUnloaded(HttpRuntime.ShutdownReason); _client = null; } }
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); }
static void Main(string[] args) { // Create the path to ASPNETPOCs project so the ASPX can be found. var currentDir = Directory.GetCurrentDirectory(); var appPhysicalSourceDir = Path.Combine(currentDir.Substring(0, currentDir.IndexOf("GetASPNETFileAsType")), "ASPNETPOCs"); Console.WriteLine("appPhysicalSourceDir: {0}", appPhysicalSourceDir); // The virtual path is arbitrary unless you want to precompile a specific app. var appVirtualDir = "/GetASPNETFileAsType"; ClientBuildManager manager = new ClientBuildManager(appVirtualDir, appPhysicalSourceDir); // "GetCompiledType" will return null if the Type's dependencies can not be resolved. // This means the ASPNETPOCs project needs to be references so the ASPX's code behind class can be resolved. var pageType = manager.GetCompiledType("~/StripWhiteSpace/StripWhiteSpacePage.aspx"); // Should return the Type that "~/StripWhiteSpace/StripWhiteSpacePage.aspx" was compiled to. Console.WriteLine("pageType: {0}", pageType); // Writes the location of the assembly the ASPX's class is in. Console.WriteLine("pageType.Assembly.Location: {0}", pageType.Assembly.Location); // Create an instance from the type. Since it is a Page, it will implement IHttpHandler. var handler = (IHttpHandler)Activator.CreateInstance(pageType); // This is where you can wire up any dependencies if you need to. // Setup the call context to process the page in. // The rendered page will be written to "Console.Out" in this case. // For unit testing you can use a StringWriter and then parse it with something like Html Agility Pack. var context = new HttpContext(new SimpleWorkerRequest(appVirtualDir, appPhysicalSourceDir, "StripWhiteSpace/StripWhiteSpacePage.aspx", "", Console.Out)); // The next line is necessary to avoid some null reference and parse exceptions. context.Request.Browser = new HttpBrowserCapabilities { Capabilities = new Hashtable { { "tables", "true" } } }; // By default the response is buffered. // So either turn buffering off or make sure you call Flush. // Flush is called below. //context.Response.BufferOutput = false; // The following enables trace information to be written with the page and raises the "TraceFinished" event. The default is false. /* context.Trace.IsEnabled = true; context.Trace.TraceFinished += (sender, e) => { foreach(TraceContextRecord r in e.TraceRecords) { Console.WriteLine("Category: {0}, IsWarning: {1}, Message: {2}, ErrorInfo:{3}{4}", r.Category, r.IsWarning, r.Message, r.ErrorInfo == null ? "" : "\r\n", r.ErrorInfo); } }; */ // Renders the page. handler.ProcessRequest(context); // Flush the response. You either need this line or "context.Response.BufferOutput = false;" above. context.Response.Flush(); Console.WriteLine(); }
public Type CompilePage(string path) { var c = new System.Web.Compilation.ClientBuildManager(@"/", WebApplicationRootFolder, TargetFolder, new System.Web.Compilation.ClientBuildManagerParameter() { PrecompilationFlags = System.Web.Compilation.PrecompilationFlags.ForceDebug }); var returnType = c.GetCompiledType(path); return returnType; }
internal void Configure(ClientBuildManager client) { this.AddPendingCall(); try { this._virtualPathProvider = new ClientVirtualPathProvider(); HostingEnvironment.RegisterVirtualPathProviderInternal(this._virtualPathProvider); this._client = client; if (this._client.CBMTypeDescriptionProviderBridge != null) { TargetFrameworkUtil.CBMTypeDescriptionProviderBridge = this._client.CBMTypeDescriptionProviderBridge; } this._onAppDomainUnload = new EventHandler(this.OnAppDomainUnload); Thread.GetDomain().DomainUnload += this._onAppDomainUnload; this._buildManager = BuildManager.TheBuildManager; HttpRuntime.AppDomainShutdown += new BuildManagerHostUnloadEventHandler(this.OnAppDomainShutdown); } finally { this.RemovePendingCall(); } }
/// <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; }
private void OnAppDomainUnload(object unusedObject, EventArgs unusedEventArgs) { Thread.GetDomain().DomainUnload -= this._onAppDomainUnload; if (this._client != null) { this._client.OnAppDomainUnloaded(HttpRuntime.ShutdownReason); this._client = null; } }
/// <summary> /// Override of IDisposable.Dispose to handle implementation details of dispose /// </summary> public void Dispose() { GC.SuppressFinalize(this); if (this._businessLogicModel != null) { try { this._businessLogicModel.Dispose(); } catch (System.Runtime.Remoting.RemotingException) { // This condition is unlikely but possible if the 2nd AppDomain // is torn down independently. There is nothing we can do here, // as it is not a user error. We catch and ignore solely to // prevent the wizard from showing a fatal error the user does // not understand. } this._businessLogicModel = null; } IDisposable disposable = this._clientBuildManager as IDisposable; if (disposable != null) { disposable.Dispose(); } this._clientBuildManager = null; }
public string GetXomlContent(string pageName, string pagePath) { var buildManager = new System.Web.Compilation.ClientBuildManager(AppVirtualDir, AppPhysicalTargetDir); var obj = new JObject(); obj["controls"] = new JArray(); //script using (var reader = new System.IO.StreamReader(pagePath, true)) { var content = reader.ReadToEnd(); System.IO.StringReader sr = new System.IO.StringReader(content); XmlDocument doc = new XmlDocument(); doc.Load(sr); XmlElement xeRoot = doc.DocumentElement; JObject main = new JObject(); main["type"] = "mainProperty"; //main["tabName"] JObject mainPropertoes = new JObject(); mainPropertoes["total"] = 14; mainPropertoes["rows"] = new JArray(); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "Description", xeRoot.Attributes["Description"].Value)); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "EEPAlias", xeRoot.Attributes["EEPAlias"].Value, "dbalias")); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "ExpTime", xeRoot.Attributes["ExpTime"].Value)); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "ExpTimeField", xeRoot.Attributes["ExpTimeField"].Value)); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "FormName", xeRoot.Attributes["FormName"].Value)); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "Keys", xeRoot.Attributes["Keys"].Value, "collection", "Srvtools.KeyItem", "KeyFields", "KeyName")); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "NotifySendMail", xeRoot.Attributes["NotifySendMail"].Value, "checkbox")); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "OrgKind", xeRoot.Attributes["OrgKind"].Value, "orgkind")); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "PresentFields", xeRoot.Attributes["PresentFields"].Value, "collection", "Srvtools.KeyItem", "PresentFields", "PresentField")); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "SkipForSameUser", xeRoot.Attributes["SkipForSameUser"].Value, "checkbox")); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "TableName", xeRoot.Attributes["TableName"].Value, "tablename")); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "TimeUnit", xeRoot.Attributes["TimeUnit"].Value)); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "UrgentTime", xeRoot.Attributes["UrgentTime"].Value)); (mainPropertoes["rows"] as JArray).Add(CreateFlowProperty("Infolight", "WebFormName", xeRoot.Attributes["WebFormName"].Value, "webformname")); main["properties"] = mainPropertoes; (obj["controls"] as JArray).Add(main); String postfix = "_node_flow_" + pageName; //{"type":"flowstart","id":"start_node_node_flow_f6","name":"start","left":262,"top":10,"height":26,"width":26,"movable":false,"editable":false,"row":0} JObject flowstart = new JObject(); flowstart["type"] = "flowstart"; flowstart["id"] = "start_node" + postfix; flowstart["name"] = "start"; flowstart["row"] = 0; (obj["controls"] as JArray).Add(flowstart); JObject currentObject = flowstart; currentObject = CreateFlow(obj, xeRoot.ChildNodes, currentObject, postfix); //{"type":"flowend","id":"end_node_node_flow_f6","name":"end","left":262,"top":130,"height":26,"width":26,"movable":false,"editable":false,"row":0}, JObject flowend = new JObject(); flowend["type"] = "flowend"; flowend["id"] = "end_node" + postfix; flowend["name"] = "end"; flowend["row"] = 0; (obj["controls"] as JArray).Add(flowend); (obj["controls"] as JArray).Add(CreateFlowLine(currentObject["id"].ToString(), flowend["id"].ToString(), "sl")); } var json = Newtonsoft.Json.JsonConvert.SerializeObject(obj); return json; }
/// <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); } } }
public ClientBuildManagerWrapper(string webApplicationName, string webApplicationPhysicalPath, string preCompileOutputpath, ClientBuildManagerParameter parameter) { _buildManager = new ClientBuildManager(webApplicationName, webApplicationPhysicalPath, preCompileOutputpath, parameter); }
public static int Main(string[] args) { _excludedVirtualPaths = new List <string>(); bool nologo = false; try { // Get the proper line length based on the Console window settings. // This can fail in some cases, so ignore errors, and stay with the default maxLineLength = Console.BufferWidth; } catch { } // Set the console app's UI culture (VSWhidbey 316444) SetThreadUICulture(); // First, check if there is a -nologo switch for (int i = 0; i < args.Length; i++) { string currentSwitch = args[i].ToLower(CultureInfo.InvariantCulture); if (currentSwitch == "-nologo" || currentSwitch == "/nologo") { nologo = true; } } if (!nologo) { Console.WriteLine(String.Format(CultureInfo.CurrentCulture, CompilerResources.brand_text, ThisAssembly.InformationalVersion)); Console.WriteLine(CompilerResources.header_text); Console.WriteLine(CompilerResources.copyright); Console.WriteLine(); } if (args.Length == 0) { Console.WriteLine(CompilerResources.short_usage_text); return(1); } if (!ValidateArgs(args)) { return(1); } try { // ClientBuildManager automatically detects vpath vs mbpath if (_sourceVirtualDir == null) { _sourceVirtualDir = _metabasePath; } ClientBuildManagerParameter parameter = new ClientBuildManagerParameter(); parameter.PrecompilationFlags = _precompilationFlags; parameter.StrongNameKeyFile = _keyFile; parameter.StrongNameKeyContainer = _keyContainer; parameter.ExcludedVirtualPaths.AddRange(_excludedVirtualPaths); _client = new ClientBuildManager(_sourceVirtualDir, _sourcePhysicalDir, _targetPhysicalDir, parameter); _client.PrecompileApplication(new CBMCallback()); // Return 0 when successful return(0); } catch (FileLoadException e) { // If the assembly fails to be loaded because of strong name verification, // return a better error message. if ((_precompilationFlags & PrecompilationFlags.DelaySign) != 0) { PropertyInfo pInfo = typeof(FileLoadException).GetProperty("HResult", BindingFlags.NonPublic | BindingFlags.Instance); MethodInfo methodInfo = pInfo.GetGetMethod(true /*nonPublic*/); uint hresult = (uint)(int)methodInfo.Invoke(e, null); if (hresult == 0x8013141A) { DumpErrors(new FileLoadException(String.Format(CultureInfo.CurrentCulture, CompilerResources.Strongname_failure, e.FileName), e.FileName, e)); return(1); } } DumpErrors(e); } catch (Exception e) { DumpErrors(e); } // Some exception happened, so return 1 return(1); }
public void Execute() { var c = new System.Web.Compilation.ClientBuildManager(@"/", WebApplicationRootFolder, TargetFolder); c.PrecompileApplication(); }
public string GetPageContent(string pageName, string pagePath) { var buildManager = new System.Web.Compilation.ClientBuildManager(AppVirtualDir, AppPhysicalTargetDir); try { var obj = new JObject(); //script using (var reader = new System.IO.StreamReader(pagePath, true)) { var content = reader.ReadToEnd(); var startIndex = content.IndexOf("<script>", StringComparison.OrdinalIgnoreCase); if (startIndex > 0) { startIndex += 8; var endIndex = content.IndexOf("</script>", startIndex, StringComparison.OrdinalIgnoreCase); if (endIndex > startIndex) { var script = content.Substring(startIndex, endIndex - startIndex); obj["script"] = script; content = content.Replace(script, string.Empty); reader.Close(); using (var writer = new System.IO.StreamWriter(pagePath, false, new UTF8Encoding(true))) { writer.Write(content); } } } } obj["controls"] = new JArray(); var pageType = buildManager.GetCompiledType(string.Format("{0}/{1}/{2}.aspx", AppVirtualDir, Folder, pageName)); var page = Activator.CreateInstance(pageType) as Page; var buildControlMethod = pageType.GetMethod("__BuildControlTree", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); buildControlMethod.Invoke(page, new object[] { page }); foreach (var control in page.Controls) { if (control.GetType().Namespace == Namespace) { if (FilteredControls.Contains(control.GetType().FullName)) { continue; } var objControl = GetItemProperties(control); (obj["controls"] as JArray).Add(objControl); } else if (control is System.Web.UI.HtmlControls.HtmlForm) { foreach (var childControl in (control as System.Web.UI.HtmlControls.HtmlForm).Controls) { if (childControl.GetType().Namespace == Namespace) { if (FilteredControls.Contains(childControl.GetType().FullName)) { continue; } var objControl = GetItemProperties(childControl); (obj["controls"] as JArray).Add(objControl); } } } } //code var code = new StringBuilder(); using (var reader = new System.IO.StreamReader(string.Format("{0}.cs", pagePath), true)) { var content = reader.ReadToEnd(); var medthods = pageType.BaseType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); foreach (var method in medthods) { if (FilteredMethods.Contains(method.Name)) { continue; } var match = Regex.Match(content, string.Format(@"{0}\s*\(.*\)", method.Name)); if (match.Success) { var startIndex = content.LastIndexOf("\n", match.Index); startIndex += 1; var bracket = 0; var quot = 0; for (int i = match.Index; i < content.Length; i++) { if (content[i] == '"') { quot++; } if (quot % 2 == 0) { if (content[i] == '{') { bracket++; } else if (content[i] == '}') { bracket--; if (bracket == 0) { code.AppendLine(content.Substring(startIndex, i + 1 - startIndex)); break; } } } } } } } obj["code"] = code.ToString(); ; var json = Newtonsoft.Json.JsonConvert.SerializeObject(obj); return json; } finally { buildManager.Unload(); } }