public async Task <SyntaxTree> AddNewContext(NewDbContextTemplateModel dbContextTemplateModel) { if (dbContextTemplateModel == null) { throw new ArgumentNullException(nameof(dbContextTemplateModel)); } var templateName = "NewLocalDbContext.cshtml"; var templatePath = _filesLocator.GetFilePath(templateName, TemplateFolders); Contract.Assert(File.Exists(templatePath)); var templateContent = File.ReadAllText(templatePath); var templateResult = await _templatingService.RunTemplateAsync(templateContent, dbContextTemplateModel); if (templateResult.ProcessingException != null) { throw new InvalidOperationException(string.Format( MessageStrings.TemplateProcessingError, templatePath, templateResult.ProcessingException.Message)); } var newContextContent = templateResult.GeneratedText; var sourceText = SourceText.From(newContextContent); return(CSharpSyntaxTree.ParseText(sourceText)); }
public async Task <SyntaxTree> AddNewContext(NewDbContextTemplateModel dbContextTemplateModel) { if (dbContextTemplateModel == null) { throw new ArgumentNullException(nameof(dbContextTemplateModel)); } var templateName = "NewLocalDbContext.cshtml"; return(await AddNewContextItemsInternal(templateName, dbContextTemplateModel)); }
private async Task GenerateNewDbContextAndRegister(ModelType startupType, ModelType programType) { AssemblyAttributeGenerator assemblyAttributeGenerator = GetAssemblyAttributeGenerator(); _startupEditResult = new EditSyntaxTreeResult() { Edited = false }; ValidateEFSqlServerDependency(); // Create a new Context _logger.LogMessage(string.Format(MessageStrings.GeneratingDbContext, _dbContextFullTypeName)); var dbContextTemplateModel = new NewDbContextTemplateModel(_dbContextFullTypeName, _modelTypeSymbol, programType); _dbContextSyntaxTree = await _dbContextEditorServices.AddNewContext(dbContextTemplateModel); ContextProcessingStatus = ContextProcessingStatus.ContextAdded; if (startupType != null) { _startupEditResult = _dbContextEditorServices.EditStartupForNewContext(startupType, dbContextTemplateModel.DbContextTypeName, dbContextTemplateModel.DbContextNamespace, dataBaseName: dbContextTemplateModel.DbContextTypeName + "-" + Guid.NewGuid().ToString()); } if (!_startupEditResult.Edited) { ContextProcessingStatus = ContextProcessingStatus.ContextAddedButRequiresConfig; // The created context would anyway fail to fetch metadata with a crypic message // It's better to throw with a meaningful message throw new InvalidOperationException(string.Format("{0} {1}", MessageStrings.FailedToEditStartup, MessageStrings.EnsureStartupClassExists)); } _logger.LogMessage(MessageStrings.CompilingWithAddedDbContext); var projectCompilation = await _workspace.CurrentSolution.Projects .First(project => project.AssemblyName == _projectContext.AssemblyName) .GetCompilationAsync(); _reflectedTypesProvider = GetReflectedTypesProvider( projectCompilation, c => { c = c.AddSyntaxTrees(assemblyAttributeGenerator.GenerateAttributeSyntaxTree()); c = c.AddSyntaxTrees(_dbContextSyntaxTree); if (_startupEditResult.Edited) { c = c.ReplaceSyntaxTree(_startupEditResult.OldTree, _startupEditResult.NewTree); } return(c); }); var compilationErrors = _reflectedTypesProvider.GetCompilationErrors(); _dbContextError = string.Format( MessageStrings.DbContextCreationError, (compilationErrors == null ? string.Empty : string.Join(Environment.NewLine, compilationErrors))); _dbContextSyntaxTree = _dbContextSyntaxTree.WithFilePath(GetPathForNewContext(dbContextTemplateModel.DbContextTypeName, _areaName)); }
public async Task <ContextProcessingResult> GetModelMetadata(string dbContextFullTypeName, ModelType modelTypeSymbol, string areaName) { Type dbContextType; SyntaxTree dbContextSyntaxTree = null; EditSyntaxTreeResult startUpEditResult = new EditSyntaxTreeResult() { Edited = false }; ContextProcessingStatus state = ContextProcessingStatus.ContextAvailable; var dbContextSymbols = _modelTypesLocator.GetType(dbContextFullTypeName).ToList(); var startupType = _modelTypesLocator.GetType("Startup").FirstOrDefault(); var programType = _modelTypesLocator.GetType("Program").FirstOrDefault(); Type modelReflectionType = null; ReflectedTypesProvider reflectedTypesProvider = null; string dbContextError = string.Empty; AssemblyAttributeGenerator assemblyAttributeGenerator = GetAssemblyAttributeGenerator(); if (dbContextSymbols.Count == 0) { ValidateEFSqlServerDependency(); // Create a new Context _logger.LogMessage(string.Format(MessageStrings.GeneratingDbContext, dbContextFullTypeName)); var dbContextTemplateModel = new NewDbContextTemplateModel(dbContextFullTypeName, modelTypeSymbol, programType); dbContextSyntaxTree = await _dbContextEditorServices.AddNewContext(dbContextTemplateModel); state = ContextProcessingStatus.ContextAdded; // Edit startup class to register the context using DI if (startupType != null) { startUpEditResult = _dbContextEditorServices.EditStartupForNewContext(startupType, dbContextTemplateModel.DbContextTypeName, dbContextTemplateModel.DbContextNamespace, dataBaseName: dbContextTemplateModel.DbContextTypeName + "-" + Guid.NewGuid().ToString()); } if (!startUpEditResult.Edited) { state = ContextProcessingStatus.ContextAddedButRequiresConfig; // The created context would anyway fail to fetch metadata with a crypic message // It's better to throw with a meaningful message throw new InvalidOperationException(string.Format("{0} {1}", MessageStrings.FailedToEditStartup, MessageStrings.EnsureStartupClassExists)); } _logger.LogMessage(MessageStrings.CompilingWithAddedDbContext); var projectCompilation = await _workspace.CurrentSolution.Projects .First(project => project.AssemblyName == _projectContext.AssemblyName) .GetCompilationAsync(); reflectedTypesProvider = new ReflectedTypesProvider( projectCompilation, c => { c = c.AddSyntaxTrees(assemblyAttributeGenerator.GenerateAttributeSyntaxTree()); c = c.AddSyntaxTrees(dbContextSyntaxTree); if (startUpEditResult.Edited) { c = c.ReplaceSyntaxTree(startUpEditResult.OldTree, startUpEditResult.NewTree); } return(c); }, _projectContext, _loader, _logger); var compilationErrors = reflectedTypesProvider.GetCompilationErrors(); dbContextError = string.Format( MessageStrings.DbContextCreationError, (compilationErrors == null ? string.Empty : string.Join(Environment.NewLine, compilationErrors))); // Add file information dbContextSyntaxTree = dbContextSyntaxTree.WithFilePath(GetPathForNewContext(dbContextTemplateModel.DbContextTypeName, areaName)); } else { var addResult = _dbContextEditorServices.AddModelToContext(dbContextSymbols.First(), modelTypeSymbol); var projectCompilation = await _workspace.CurrentSolution.Projects .First(project => project.AssemblyName == _projectContext.AssemblyName) .GetCompilationAsync(); if (addResult.Edited) { state = ContextProcessingStatus.ContextEdited; dbContextSyntaxTree = addResult.NewTree; _logger.LogMessage(MessageStrings.CompilingWithModifiedDbContext); reflectedTypesProvider = new ReflectedTypesProvider( projectCompilation, c => { c = c.AddSyntaxTrees(assemblyAttributeGenerator.GenerateAttributeSyntaxTree()); var oldTree = c.SyntaxTrees.FirstOrDefault(t => t.FilePath == addResult.OldTree.FilePath); if (oldTree == null) { throw new InvalidOperationException(string.Format( MessageStrings.ModelTypeCouldNotBeAdded, modelTypeSymbol.FullName, dbContextFullTypeName)); } return(c.ReplaceSyntaxTree(oldTree, addResult.NewTree)); }, _projectContext, _loader, _logger); var compilationErrors = reflectedTypesProvider.GetCompilationErrors(); dbContextError = string.Format( MessageStrings.DbContextCreationError, (compilationErrors == null ? string.Empty : string.Join(Environment.NewLine, compilationErrors))); } else { _logger.LogMessage(MessageStrings.CompilingInMemory); reflectedTypesProvider = new ReflectedTypesProvider( projectCompilation, c => { c = c.AddSyntaxTrees(assemblyAttributeGenerator.GenerateAttributeSyntaxTree()); return(c); }, _projectContext, _loader, _logger); dbContextError = string.Format(MessageStrings.DbContextTypeNotFound, dbContextFullTypeName); } } dbContextType = reflectedTypesProvider.GetReflectedType( modelType: dbContextFullTypeName, lookInDependencies: true); if (dbContextType == null) { throw new InvalidOperationException(dbContextError); } modelReflectionType = reflectedTypesProvider.GetReflectedType( modelType: modelTypeSymbol.FullName, lookInDependencies: true); if (modelReflectionType == null) { throw new InvalidOperationException(string.Format(MessageStrings.ModelTypeNotFound, modelTypeSymbol.Name)); } var reflectedStartupType = reflectedTypesProvider.GetReflectedType( modelType: startupType.FullName, lookInDependencies: true); if (reflectedStartupType == null) { throw new InvalidOperationException(string.Format(MessageStrings.ModelTypeNotFound, reflectedStartupType.Name)); } _logger.LogMessage(string.Format(MessageStrings.GettingEFMetadata, modelTypeSymbol.Name)); var metadata = GetModelMetadata(dbContextType, modelReflectionType, reflectedStartupType); // Write the DbContext/Startup if getting the model metadata is successful if (dbContextSyntaxTree != null) { PersistSyntaxTree(dbContextSyntaxTree); if (state == ContextProcessingStatus.ContextAdded || state == ContextProcessingStatus.ContextAddedButRequiresConfig) { _logger.LogMessage(string.Format(MessageStrings.AddedDbContext, dbContextSyntaxTree.FilePath.Substring(_applicationInfo.ApplicationBasePath.Length))); if (state != ContextProcessingStatus.ContextAddedButRequiresConfig) { PersistSyntaxTree(startUpEditResult.NewTree); } else { _logger.LogMessage(MessageStrings.AdditionalSteps); } } } return(new ContextProcessingResult() { ContextProcessingStatus = state, ModelMetadata = metadata }); }
public async Task <ContextProcessingResult> GetModelMetadata(string dbContextFullTypeName, ModelType modelTypeSymbol) { Type dbContextType; SyntaxTree dbContextSyntaxTree = null; EditSyntaxTreeResult startUpEditResult = new EditSyntaxTreeResult() { Edited = false }; ContextProcessingStatus state = ContextProcessingStatus.ContextAvailable; var dbContextSymbols = _modelTypesLocator.GetType(dbContextFullTypeName).ToList(); var startupType = _modelTypesLocator.GetType("Startup").FirstOrDefault(); Type modelReflectionType = null; if (dbContextSymbols.Count == 0) { await ValidateEFSqlServerDependency(); // Create a new Context _logger.LogMessage("Generating a new DbContext class " + dbContextFullTypeName); var dbContextTemplateModel = new NewDbContextTemplateModel(dbContextFullTypeName, modelTypeSymbol); dbContextSyntaxTree = await _dbContextEditorServices.AddNewContext(dbContextTemplateModel); state = ContextProcessingStatus.ContextAdded; // Edit startup class to register the context using DI if (startupType != null) { startUpEditResult = _dbContextEditorServices.EditStartupForNewContext(startupType, dbContextTemplateModel.DbContextTypeName, dbContextTemplateModel.DbContextNamespace, dataBaseName: dbContextTemplateModel.DbContextTypeName + "-" + Guid.NewGuid().ToString()); } if (!startUpEditResult.Edited) { state = ContextProcessingStatus.ContextAddedButRequiresConfig; // The created context would anyway fail to fetch metadata with a crypic message // It's better to throw with a meaningful message throw new InvalidOperationException(string.Format("{0} {1}", MessageStrings.FailedToEditStartup, MessageStrings.EnsureStartupClassExists)); } _logger.LogMessage("Attempting to compile the application in memory with the added DbContext"); CompileAndGetDbContextAndModelTypes(dbContextFullTypeName, modelTypeSymbol.FullName, c => { c = c.AddSyntaxTrees(dbContextSyntaxTree); if (startUpEditResult.Edited) { c = c.ReplaceSyntaxTree(startUpEditResult.OldTree, startUpEditResult.NewTree); } return(c); }, out dbContextType, out modelReflectionType); // Add file information dbContextSyntaxTree = dbContextSyntaxTree.WithFilePath(GetPathForNewContext(dbContextTemplateModel.DbContextTypeName)); } else { var addResult = _dbContextEditorServices.AddModelToContext(dbContextSymbols.First(), modelTypeSymbol); if (addResult.Edited) { state = ContextProcessingStatus.ContextEdited; dbContextSyntaxTree = addResult.NewTree; _logger.LogMessage("Attempting to compile the application in memory with the modified DbContext"); CompileAndGetDbContextAndModelTypes(dbContextFullTypeName, modelTypeSymbol.FullName, c => { var oldTree = c.SyntaxTrees.FirstOrDefault(t => t.FilePath == addResult.OldTree.FilePath); Debug.Assert(oldTree != null); return(c.ReplaceSyntaxTree(oldTree, addResult.NewTree)); }, out dbContextType, out modelReflectionType); } else { _logger.LogMessage("Attempting to compile the application in memory"); CompileAndGetDbContextAndModelTypes(dbContextFullTypeName, modelTypeSymbol.FullName, c => { return(c); }, out dbContextType, out modelReflectionType); if (dbContextType == null) { throw new InvalidOperationException(string.Format(MessageStrings.DbContextTypeNotFound, dbContextFullTypeName)); } } } if (modelReflectionType == null) { throw new InvalidOperationException(string.Format(MessageStrings.ModelTypeNotFound, modelTypeSymbol.Name)); } _logger.LogMessage("Attempting to figure out the EntityFramework metadata for the model and DbContext: " + modelTypeSymbol.Name); var metadata = GetModelMetadata(dbContextType, modelReflectionType, startupType); // Write the DbContext/Startup if getting the model metadata is successful if (dbContextSyntaxTree != null) { PersistSyntaxTree(dbContextSyntaxTree); if (state == ContextProcessingStatus.ContextAdded || state == ContextProcessingStatus.ContextAddedButRequiresConfig) { _logger.LogMessage("Added DbContext : " + dbContextSyntaxTree.FilePath.Substring(_applicationInfo.ApplicationBasePath.Length)); if (state != ContextProcessingStatus.ContextAddedButRequiresConfig) { PersistSyntaxTree(startUpEditResult.NewTree); } else { _logger.LogMessage("However there may be additional steps required for the generted code to work properly, refer to documentation <forward_link>."); } } } return(new ContextProcessingResult() { ContextProcessingStatus = state, ModelMetadata = metadata }); }