/// <summary> /// Gets a list of all classes in the project that qualify for transient dependency injection registration. /// </summary> /// <param name="project">Target project to register.</param> /// <param name="rejectedClassNames">The class names that are not allowed for dependency injection.</param> /// <param name="rejectedBaseClasses">The base classes not allowed for dependency injection.</param> /// <param name="targetInterfaceTypeForRegistration">The interfaces of a target type to use for registration, this includes inheriting the target type.</param> /// <returns></returns> public static async Task <IEnumerable <DependencyInjectionRegistrationInformation> > GetTransientClassesAsync(VsProject project, IEnumerable <string> rejectedClassNames = null, IEnumerable <ModelLookupData> rejectedBaseClasses = null, IEnumerable <ModelLookupData> targetInterfaceTypeForRegistration = null) { var result = new List <DependencyInjectionRegistrationInformation>(); if (project == null) { return(result); } if (!project.HasChildren) { return(result); } var projectChildren = await project.GetChildrenAsync(true, true); var csSourceCodeDocuments = projectChildren .Where(m => m.ModelType == VisualStudioModelType.CSharpSource) .Cast <VsCSharpSource>(); foreach (var csSourceCodeDocument in csSourceCodeDocuments) { var sourceCode = csSourceCodeDocument.SourceCode; if (sourceCode == null) { continue; } if (!sourceCode.Classes.Any()) { continue; } var classes = sourceCode.Classes; foreach (var csClass in classes) { var registration = IsTransientClass(csClass, rejectedClassNames, rejectedBaseClasses, targetInterfaceTypeForRegistration); if (registration == null) { continue; } if (!result.Any(r => (r.ClassData?.Namespace == registration.ClassData?.Namespace && r.ClassData?.Name == registration.ClassData?.Name))) { result.Add(registration); } } //result.AddRange(classes.Select(csClass => IsTransientClass(csClass, rejectedClassNames, // rejectedBaseClasses, targetInterfaceTypeForRegistration)).Where(diInfo => diInfo != null)); } return(result); }
/// <summary> /// Extension method that searches a project for a C# class that exists in one of the projects documents. /// </summary> /// <param name="source">Source Project to search through</param> /// <param name="className">The name of the class to search for.</param> /// <param name="searchChildren">Flag that determines if the entire project should be searched or just the root of the project.</param> /// <returns>The first instance of the class or null.</returns> public static async Task <CsClass> FindClassAsync(this VsProject source, string className, bool searchChildren) { //Loading the visual studio models from the project and pre creating the source code files. var children = await source.GetChildrenAsync(searchChildren, true); //Extracting all the c# source code files from the returned models. var sourceCodeFiles = children.Where(p => p.ModelType.Equals(VisualStudioModelType.CSharpSource)).Cast <VsCSharpSource>(); //Getting the first code file that contains the class. Returning either null or the found class. return(sourceCodeFiles.FirstOrDefault(s => s.SourceCode.Classes.Any(c => c.Name.Equals(className))) ?.SourceCode.Classes.FirstOrDefault(c => c.Name.Equals(className))); }
/// <summary> /// Loads all the classes that exist in the project from each code file found within the project. That qualify for dependency injection. /// </summary> /// <param name="project">The source project to get the classes from</param> /// <returns>The class models for all classes that qualify for transient dependency injection. If no classes are found an empty enumeration will be returned.</returns> public static async Task <IEnumerable <CsClass> > LoadInstanceProjectClassesForRegistrationAsync(VsProject project) { var result = new List <CsClass>(); if (project == null) { return(result); } if (!project.HasChildren) { return(result); } try { var projectChildren = await project.GetChildrenAsync(true, true); var csSourceCodeDocuments = projectChildren .Where(m => m.ModelType == VisualStudioModelType.CSharpSource) .Cast <VsCSharpSource>(); foreach (var csSourceCodeDocument in csSourceCodeDocuments) { var sourceCode = csSourceCodeDocument.SourceCode; if (sourceCode == null) { continue; } if (!sourceCode.Classes.Any()) { continue; } var classes = sourceCode.Classes.Where(IsTransientClass).Where(c => result.All(r => $"{c.Namespace}.{c.Name}" != $"{r.Namespace}.{r.Name}")); if (classes.Any()) { result.AddRange(classes); } } } catch (Exception unhandledError) { _logger.Error($"The following unhandled error occured while loading the classes to be added to dependency injection.", unhandledError); } return(result); }
/// <summary> /// Returns a list of non-source code documents from VsProject that have a matching extension. /// </summary> /// <param name="source">The source visual studio project to search.</param> /// <param name="extension">The file extension to search for</param> /// <param name="searchChildren">Flag that determines if nested project folders should also be searched for files.</param> /// <param name="excludeKnownExternalFolders">Flag that determines if a content filter should be applied.</param> /// <returns>List of documents that meet the criteria.</returns> public static async Task <IReadOnlyList <VsDocument> > GetDocumentsWithExtensionAsync(this VsProject source, string extension, bool searchChildren, bool excludeKnownExternalFolders) { //If no source is found return an empty list. if (source == null) { return(ImmutableList <VsDocument> .Empty); } //If no file extension is provided return an empty list. if (string.IsNullOrEmpty(extension)) { return(ImmutableList <VsDocument> .Empty); } List <VsDocument> result = new List <VsDocument>(); //Making sure we start with a period for the extension for searching purposes. if (!extension.StartsWith(".")) { extension = $".{extension}"; } //Calling the CodeFactory project system api to get the children of the project. var children = await source.GetChildrenAsync(searchChildren); //Filtering out to just var sourceFiles = children.Where(p => p.ModelType.Equals(VisualStudioModelType.Document)) .Cast <VsDocument>().Where(d => !d.IsSourceCode); return(sourceFiles.Where(s => { //If we are excluding external folders just check for the extension. if (!excludeKnownExternalFolders) { return s.Name.EndsWith(extension); } //Checking to make sure the file is not in the excluded list. var documentPath = s.Path; if (string.IsNullOrEmpty(documentPath)) { return false; } return !documentPath.ToLower().Contains("\\content\\") && s.Name.EndsWith(extension); }).ToImmutableList()); }
/// <summary> /// Extension method that searches C# source code files for a base class inheritance. /// </summary> /// <param name="source">The source visual studio project to search.</param> /// <param name="baseClassName">The name of the base class to search for.</param> /// <param name="searchChildren">Flag that determines if you search all child project folders under the project.</param> /// <returns>The target source code that meets the criteria or an empty list. </returns> public static async Task <IReadOnlyList <CsSource> > GetClassesThatInheritBaseAsync(this VsProject source, string baseClassName, bool searchChildren) { //If the project is not created return an empty list. if (source == null) { return(ImmutableList <CsSource> .Empty); } //Calling into the CodeFactory project system api to load all project items, will pre load the source code models. var children = await source.GetChildrenAsync(searchChildren, true); //Pulling out the list of all code files. var sourceCodeFiles = children.Where(p => p.ModelType.Equals(VisualStudioModelType.CSharpSource)).Cast <VsCSharpSource>(); //Returning the code files that implement the target base class. return(sourceCodeFiles.Select(codeFile => codeFile.SourceCode) .Where(sourceCode => sourceCode.Classes.Any(c => c.BaseClass.Name.Equals(baseClassName))) .ToImmutableList()); }
/// <summary> /// Used to check a project model for the existence of a folder at the root level of a given name. If the folder is /// missing - create it. /// </summary> /// <param name="source">The visual studio project that we are checking exists or creating.</param> /// <param name="folderName">The name of the folder to return.</param> /// <returns>The existing or created project folder.</returns> /// <exception cref="ArgumentNullException">Thrown if either provided parameter is not provided.</exception> public static async Task <VsProjectFolder> CheckAddFolder(this VsProject source, string folderName) { //Bounds checking to make sure all the data needed to get the folder returned is provided. if (source == null) { throw new ArgumentNullException(nameof(source)); } if (string.IsNullOrEmpty(folderName)) { throw new ArgumentNullException(nameof(folderName)); } //Calling the project system in CodeFactory and getting all the children in the root of the project. var projectFolders = await source.GetChildrenAsync(false); //Searching for the project folder, if it is not found will add the project folder to the root of the project. return(projectFolders.Where(m => m.ModelType == VisualStudioModelType.ProjectFolder) .Where(m => m.Name.Equals(folderName)) .Cast <VsProjectFolder>() .FirstOrDefault() ?? await source.AddProjectFolderAsync(folderName)); }
#pragma warning disable CS1998 #region Overrides of VsCommandBase<VsProject> /// <summary> /// Validation logic that will determine if this command should be enabled for execution. /// </summary> /// <param name="result">The target model data that will be used to determine if this command should be enabled.</param> /// <returns>Boolean flag that will tell code factory to enable this command or disable it.</returns> public override async Task <bool> EnableCommandAsync(VsProject result) { //Result that determines if the the command is enabled and visible in the context menu for execution. bool isEnabled = false; try { var children = await result.GetChildrenAsync(true); if (children.Any(p => p.ModelType.Equals(VisualStudioModelType.ProjectFolder) && p.Name.Equals("Pages"))) { isEnabled = true; } } catch (Exception unhandledError) { _logger.Error($"The following unhandled error occured while checking if the solution explorer project command {commandTitle} is enabled. ", unhandledError); isEnabled = false; } return(isEnabled); }
/// <summary> /// Gets target classes that implement a target interface. It will skip classes that implement Page or HttpApplication. /// </summary> /// <param name="source">The project to search for the classes in.</param> /// <param name="interfaceName">The name of the interface to search for.</param> /// <param name="searchChildren">Flag to determine if sub folder should be searched or just the root project folder.</param> /// <returns>Readonly list of the found source code files with the target classes in them. or an empty list.</returns> public static async Task <IReadOnlyList <CsSource> > GetClassesThatImplementInterfaceAsync(this VsProject source, string interfaceName, bool searchChildren) { //Bounds check will return an empty list if no project was provided. if (source == null) { return(ImmutableList <CsSource> .Empty); } //Calls into the CodeFactory project system and gets the children of the supplied project. Will load all code files that support C# as CSharpSource files. var children = await source.GetChildrenAsync(searchChildren, true); //Extracting all the C# code files from the returned project data. var codeFiles = children.Where(p => p.ModelType.Equals(VisualStudioModelType.CSharpSource)) .Cast <VsCSharpSource>(); //Collection all the code files that meet the criteria and returning the source code models for each. return(codeFiles.Where(s => s.SourceCode.Classes.Any(c => (!c.BaseClass.Name.Equals("Page") && !c.BaseClass.Name.Equals("HttpApplication")) && c.InheritedInterfaces.Any(x => x.Name.Equals(interfaceName)))) .Select(s => s.SourceCode) .ToImmutableList()); }
/// <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> /// Extension method that loads all <see cref="VsProjectFolder"/> , <see cref="VsDocument"/>, and <see cref="VsCSharpSource"/> /// </summary> public static async Task <IReadOnlyList <VsModel> > LoadAllProjectData(this VsProject source, bool loadSourceCode = true) { return(source != null ? await source.GetChildrenAsync(true, loadSourceCode) : ImmutableList <VsModel> .Empty); }
/// <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); }
public async Task MigrateAspxFiles(IReadOnlyList <VsModel> webFormProjectData, VsProject webFormProject, VsProject blazorServerProject) { try { //Informing the dialog the migration step has started. await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AspxPages, MigrationStatusEnum.Running); //Getting all the aspx & ascx files in the project. var aspxFiles = webFormProjectData.Where(p => p.ModelType == VisualStudioModelType.Document && (p.Name.EndsWith(".aspx") || p.Name.EndsWith(".ascx"))).Cast <VsDocument>(); if (!aspxFiles.Any()) { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Warning, "No Aspx files were found in the web forms project. This step is finished."); await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AspxPages, MigrationStatusEnum.Passed); return; } //Migrate over the site.master layout pages. var layoutFiles = webFormProjectData.Where(p => p.Name.ToLower().Contains(".master")); var success = await ConvertLayoutFiles(layoutFiles, blazorServerProject); //Calling into the CodeFactory project system and getting all the direct children of the project. var blazorRootModels = await blazorServerProject.GetChildrenAsync(false); //Getting the pages folder from the blazor project. var blazorPagesFolder = blazorRootModels.FirstOrDefault(m => m.ModelType == VisualStudioModelType.ProjectFolder & m.Name.ToLower().Equals("pages")) as VsProjectFolder; //If the pages folder was not found fail this step and return. if (blazorPagesFolder == null) { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Error, "No pages folder was found in the blazor project, cannot continue the aspx file conversion."); await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AspxPages, MigrationStatusEnum.Failed); return; } //Call the CodeFactory project system to get all the current children of the pages project folder. var pagesFolderModels = await blazorPagesFolder.GetChildrenAsync(true); //Filtering out everything but documents. var pages = pagesFolderModels.Where(m => m.ModelType == VisualStudioModelType.Document) .Cast <VsDocument>(); int collect = 0; //Processing each aspx file. foreach (VsDocument aspxFile in aspxFiles) { collect++; //Getting the formatted names that will be used in migrating the ASPX file and its code behind to the blazor project. string targetFileNameNoExtension = Path.GetFileNameWithoutExtension(aspxFile.Path); string aspxCodeBehindFileName = $"{targetFileNameNoExtension}.aspx.cs"; string ascxCodeBehindFileName = $"{targetFileNameNoExtension}.axcs.cs"; string razorPageFileName = $"{targetFileNameNoExtension}.razor"; string razorPageCodeBehindFileName = $"{targetFileNameNoExtension}.razor.cs"; //Searching for an existing razor page. We will delete razor pages and recreate them. var currentRazorPage = pages.FirstOrDefault(p => p.Path.ToLower().EndsWith(razorPageFileName.ToLower())); if (currentRazorPage != null) { //Razor page was found removing the razor page. bool removedPage = await currentRazorPage.DeleteAsync(); if (removedPage) { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Information, $"Removed the razor page {razorPageFileName}"); var currentRazorPageCodeBehind = pages.FirstOrDefault(p => p.Path.ToLower().EndsWith(razorPageCodeBehindFileName.ToLower())); if (currentRazorPageCodeBehind != null) { if (File.Exists(currentRazorPageCodeBehind.Path)) { bool removedCodeBehind = await currentRazorPageCodeBehind.DeleteAsync(); if (removedCodeBehind) { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Information, $"Removed the razor page code behind file {razorPageCodeBehindFileName}"); } else { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Error, $"Could not remove the razor page code behind file {razorPageCodeBehindFileName}.The target ASPX file will not be migrated."); continue; } } } } else { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Error, $"Could not remove the razor page {razorPageFileName}.The target ASPX file will not be migrated."); continue; } } VsCSharpSource CodeBehindSource = null; if (aspxFile.Path.Contains("ascx")) { //Getting the code behind file that supports the current aspx page. CodeBehindSource = webFormProjectData .Where(m => m.ModelType == VisualStudioModelType.CSharpSource).Cast <VsCSharpSource>() .FirstOrDefault(s => s.SourceCode.SourceDocument.ToLower().EndsWith(ascxCodeBehindFileName.ToLower())) as VsCSharpSource; } //Getting the code behind file that supports the current aspx page. CodeBehindSource = webFormProjectData .Where(m => m.ModelType == VisualStudioModelType.CSharpSource).Cast <VsCSharpSource>() .FirstOrDefault(s => s.SourceCode.SourceDocument.ToLower().EndsWith(aspxCodeBehindFileName.ToLower())) as VsCSharpSource; //Converting the aspx page and the code behind file if it was found. await ConvertAspxPage(aspxFile, blazorServerProject, blazorPagesFolder, CodeBehindSource); if (collect == 4) { GC.Collect(); collect = 0; } } //Completed the migration step informing the dialog. await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AspxPages, 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.AspxPages, MessageTypeEnum.Error, $"The following unhandled error occured. '{unhandledError.Message}'"); //Updating the status that the step failed await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AspxPages, MigrationStatusEnum.Failed); } }
public async Task MigrateSingleASPXFile(VsDocument aspxSourcefile, VsProject blazorServerProject) { try { //VsCSharpSource aspxCodeBehindFile //Getting the formatted names that will be used in migrating the ASPX file and its code behind to the blazor project. string targetFileNameNoExtension = Path.GetFileNameWithoutExtension(aspxSourcefile.Path); string aspxCodeBehindFileName = $"{targetFileNameNoExtension}.aspx.cs"; string razorPageFileName = $"{targetFileNameNoExtension}.razor"; string razorPageCodeBehindFileName = $"{targetFileNameNoExtension}.razor.cs"; //Calling into the CodeFactory project system and getting all the direct children of the project. var blazorRootModels = await blazorServerProject.GetChildrenAsync(false); //Getting the pages folder from the blazor project. var blazorPagesFolder = blazorRootModels.FirstOrDefault(m => m.ModelType == VisualStudioModelType.ProjectFolder & m.Name.ToLower().Equals("pages")) as VsProjectFolder; //If the pages folder was not found fail this step and return. if (blazorPagesFolder == null) { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Error, "No pages folder was found in the blazor project, cannot continue the aspx file conversion."); return; } //Call the CodeFactory project system to get all the current children of the pages project folder. var pagesFolderModels = await blazorPagesFolder.GetChildrenAsync(true); //Filtering out everything but documents. var pages = pagesFolderModels.Where(m => m.ModelType == VisualStudioModelType.Document) .Cast <VsDocument>(); //Searching for an existing razor page. We will delete razor pages and recreate them. var currentRazorPage = pages.FirstOrDefault(p => p.Path.ToLower().EndsWith(razorPageFileName.ToLower())); if (currentRazorPage != null) { //Razor page was found removing the razor page. bool removedPage = await currentRazorPage.DeleteAsync(); if (removedPage) { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Information, $"Removed the razor page {razorPageFileName}"); var currentRazorPageCodeBehind = pages.FirstOrDefault(p => p.Path.ToLower().EndsWith(razorPageCodeBehindFileName.ToLower())); if (currentRazorPageCodeBehind != null) { if (File.Exists(currentRazorPageCodeBehind.Path)) { bool removedCodeBehind = await currentRazorPageCodeBehind.DeleteAsync(); if (removedCodeBehind) { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Information, $"Removed the razor page code behind file {razorPageCodeBehindFileName}"); } else { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Error, $"Could not remove the razor page code behind file {razorPageCodeBehindFileName}.The target ASPX file will not be migrated."); return; } } } } else { await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Error, $"Could not remove the razor page {razorPageFileName}.The target ASPX file will not be migrated."); return; } } var aspxChildren = await aspxSourcefile.GetChildrenAsync(true); var codeBehindFile = aspxChildren .Where(c => (c.IsSourceCode == true) && (c.SourceType == SourceCodeType.CSharp)).FirstOrDefault(); //Converting the aspx page and the code behind file if it was found. await ConvertAspxPage(aspxSourcefile, blazorServerProject, blazorPagesFolder, null, codeBehindFile); await _statusTracking.UpdateMigrationFinishedAsync(); } catch (Exception unhandledError) { //Dumping the exception that occured directly into the status so the user can see what happened. await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Error, $"The following unhandled error occured. '{unhandledError.Message}'"); } }