/// <summary> /// Migrates the web.config to a settings file in the blazor server project. /// </summary> /// <param name="webFormProjectData">Data from the web forms project already loaded and provided in a list.</param> /// <param name="webFormProject">The web forms project that we are migrating data from.</param> /// <param name="blazorServerProject">The blazor server project this is being migrated to.</param> public async Task MigrateConfig(IReadOnlyList <VsModel> webFormProjectData, VsProject webFormProject, VsProject blazorServerProject) { try { //Informing the dialog the migration step has started. await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.Config, MigrationStatusEnum.Running); //Get the web.config file from the source project; if (!(webFormProjectData.FirstOrDefault(c => c.ModelType == VisualStudioModelType.Document & c.Name.ToLower().Equals("web.config")) is VsDocument configDocument)) { //No web.config was found. //Sending a status update to the dialog await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.Config, MessageTypeEnum.Error, $"No web.config file was found in the web forms project {webFormProject.Name}. Cannot migrate the configuration."); //Informing the dialog the migration step has failed. await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.Config, MigrationStatusEnum.Running); return; } else { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(await configDocument.GetDocumentContentAsStringAsync()); var jsonConverted = JsonConvert.SerializeXmlNode(xmlDoc); //Add converted web.config to a file in the target solution. this will be named webconfig.json var thing = await blazorServerProject.AddDocumentAsync("webconfig.json", jsonConverted); //Sending a status update to the dialog await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.Config, MessageTypeEnum.Information, $"The web.config file has been moved to the root directory of {blazorServerProject.Name} and converted to 'webconfig.json'."); await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.Config, MessageTypeEnum.Information, $"** Please review the webconfig.json file and make sure that it meets the needs of the converted Blazor app."); } //Completed the migration step informing the dialog. await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.Config, MigrationStatusEnum.Passed); }
/// <summary> /// Clones the bundleconfig.json into the blazer server project. /// </summary> /// <param name="webFormProjectData">Pre cached project data about the web forms project.</param> /// <param name="webFormProject">The web forms project that we are migrating data from.</param> /// <param name="blazorServerProject">The blazor server project this is being migrated to.</param> public async Task MigrateBundling(IReadOnlyList <VsModel> webFormProjectData, VsProject webFormProject, VsProject blazorServerProject) { try { //Letting the dialog know the migration step has started. await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.Bundling, MigrationStatusEnum.Running); //Finding the bundlconfig.json file. if (webFormProjectData.FirstOrDefault(p => p.Name.ToLower().Equals("bundleconfig.json")) is VsDocument bundleConfig) { //Found the config file. loading its content. var bundleText = await bundleConfig.GetDocumentContentAsStringAsync(); //Creating the bundleconfig.json in the blazor server project, and injecting the content. var thing = await blazorServerProject.AddDocumentAsync("bundleconfig.json", bundleText); //Sending a status to the dialog await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.Bundling, MessageTypeEnum.Information, $"The bundleconfig.json file has been copied to the root directory of {blazorServerProject.Name}."); } else { //No bundle configuration was found. No additional actions need to be taken. //Sending a status to the dialog await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.Bundling, MessageTypeEnum.Information, $"There was no 'bundleconfig.json' file found in the root of the source project {webFormProject.Name}. No files were copied."); } //Completed the migration step informing the dialog. await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.Bundling, MigrationStatusEnum.Passed); } catch (Exception unhandledError) { //Dumping the exception that occured directly into the status so the user can see what happened. await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.Bundling, MessageTypeEnum.Error, $"The following unhandled error occured. '{unhandledError.Message}'"); //Updating the status that the step failed await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.Bundling, MigrationStatusEnum.Failed); } }
/// <summary> /// Migrates logic class files over to the blazor server project. /// </summary> /// <param name="webFormProjectData">List of pre cached models for from the web form project.</param> /// <param name="webFormProject">The web forms project that we are migrating data from.</param> /// <param name="blazorServerProject">The blazor server project this is being migrated to.</param> public async Task MigrateLogic(IReadOnlyList <VsModel> webFormProjectData, VsProject webFormProject, VsProject blazorServerProject) { try { //Informing the dialog the migration step has started. await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AppLogic, MigrationStatusEnum.Running); var childFiles = webFormProjectData.GetSourceCodeDocumentsAsync(true); //we don't want any known aspx/ascx files hitching a ride. just plain vanilla *.cs files should qualify. var logicFiles = childFiles.Where(p => (!p.Name.ToLower().Contains("aspx.") && !p.Name.ToLower().Contains("ascx.") && !p.Name.ToLower().Contains("asax."))).ToList(); //put logic files (using the file system into the target under the project root) foreach (VsCSharpSource sourceDocument in logicFiles) { //look for specific files that are native to a WebForm app and skip them. ** TODO: move this to a config setting maybe? if (sourceDocument.Name.ToLower().Contains("bundleconfig")) { continue; } if (sourceDocument.Name.ToLower().Contains("assemblyinfo")) { continue; } if (sourceDocument.Name.ToLower().Contains("startup")) { continue; } if (sourceDocument.Name.ToLower().Contains(".master")) { continue; } var logicDocument = await sourceDocument.LoadDocumentModelAsync(); var parentFolders = await logicDocument.GetParentFolders(); var source = sourceDocument.SourceCode; var docText = await logicDocument.GetDocumentContentAsStringAsync(); if (parentFolders.Count >= 1) { parentFolders.Reverse(); //The folders are returned in leaf-to-trunk so need to reverse the order for the next step. VsProjectFolder createdFolder = null; //deal with source folder hierarchy for (int i = 0; i < parentFolders.Count; i++) { if (i > 0) { createdFolder = await createdFolder.CheckAddFolder(parentFolders[i].Name); } else { createdFolder = await blazorServerProject.CheckAddFolder(parentFolders[i].Name); } } //copy the file. We only really care about the most leaf/edge subfolder so its safe to use the creatdFolder variable here. docText = docText.Replace(source.Classes.First().Namespace, $"{blazorServerProject.Name}.{createdFolder.Name}"); var targetFolderFiles = await createdFolder.GetChildrenAsync(false); if (!targetFolderFiles.Any(c => c.Name.ToLower().Equals(logicDocument.Name.ToLower()))) { await createdFolder.AddDocumentAsync(logicDocument.Name, docText); //Updating the dialog with a status await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Information, $"Copied logic file: {logicDocument.Name} to project {blazorServerProject.Name} location: {Path.Combine(createdFolder.Path, logicDocument.Name)}"); } else { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Warning, $"Logic file: {logicDocument.Name} already exists in target folder location: {Path.Combine(createdFolder.Path, logicDocument.Name)} and was skipped."); } } else { var projFiles = await blazorServerProject.GetChildrenAsync(false); if (!projFiles.Any(c => c.Name.ToLower().Equals(logicDocument.Name.ToLower()))) { docText = docText.Replace(source.Classes.First().Namespace, $"{blazorServerProject.Name}"); var thing = await blazorServerProject.AddDocumentAsync(logicDocument.Name, docText); //Updating the dialog with a status await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Information, $"Copied static file: {logicDocument.Name} to project {blazorServerProject.Name} location: {Path.Combine(blazorServerProject.Path, logicDocument.Name)}"); } else { //Updating the dialog with a status await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Warning, $"Static file: {logicDocument.Name} already exists in project {blazorServerProject.Name} location: {Path.Combine(blazorServerProject.Path, logicDocument.Name)} and was skipped."); } } } //Completed the migration step informing the dialog. await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AppLogic, MigrationStatusEnum.Passed); } catch (Exception unhandledError) { //Dumping the exception that occured directly into the status so the user can see what happened. await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Error, $"The following unhandled error occured. '{unhandledError.Message}'"); //Updating the status that the step failed await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AppLogic, MigrationStatusEnum.Failed); } }
/// <summary> /// Searches the root of a project for the dependency injection registration class. If it is not found will generate a new service registration class. /// </summary> /// <param name="source">The source project to load the registration class from.</param> /// <param name="className">The name of the class that is used for service registration. This will be set to the constant <see cref="NetConstants.DefaultDependencyInjectionClassName"/> this can be overwritten with a custom class name.</param> /// <param name="automatedRegistrationMethodName">The name of the automatic transient class registration method. This will be set to the constant <see cref="NetConstants.DefaultAutomaticTransientClassRegistrationMethodName"/> this can be overwritten with a custom method name. </param> /// <returns>The source code model for the registration class.</returns> public static async Task <CsSource> GetRegistrationClassAsync(VsProject source, string className = NetConstants.DefaultDependencyInjectionClassName, string automatedRegistrationMethodName = NetConstants.DefaultAutomaticTransientClassRegistrationMethodName) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (!source.IsLoaded) { throw new CodeFactoryException("Project model was not loaded cannot load or create the registration class."); } if (string.IsNullOrEmpty(className)) { throw new ArgumentNullException(nameof(className)); } var projectFiles = await source.GetChildrenAsync(false, true); CsSource registrationSource = null; try { //Searching the projects root directory code files for the registration class if (projectFiles.Any()) { registrationSource = projectFiles.Where(f => f.ModelType == VisualStudioModelType.CSharpSource) .Cast <VsCSharpSource>().FirstOrDefault(s => s.SourceCode.Classes.Any(c => c.Name == className)) ?.SourceCode; } //If no registration source code file was found then create a new registration class from scratch. if (registrationSource == null) { var registrationClassCode = BuildNewRegistrationClass(source, className, automatedRegistrationMethodName); if (registrationClassCode == null) { throw new CodeFactoryException("Could not generate the dependency injection registration source code"); } string registrationClassFileName = $"{className}.cs"; var document = await source.AddDocumentAsync(registrationClassFileName, registrationClassCode); registrationSource = await document.GetCSharpSourceModelAsync(); } if (registrationSource == null) { throw new CodeFactoryException("Cannot load the source code for the registration class."); } } catch (CodeFactoryException) { throw; } catch (Exception unhandledException) { throw new CodeFactoryException("An error occured while loading the registration class source code, cannot continue.", unhandledException); } return(registrationSource); }