/// <summary> /// Process a T4 template using Visual Studio's text templating service, given a path that could contain macros (i.e. "$(DevEnvDir)\..."). /// NOTE: Template paths that are not files or are UNC paths are not allowed /// </summary> /// <param name="templatePath">Template's file path which may contain project-based macros</param> /// <returns>The output of processing the template.</returns> protected string ProcessTemplate(string templatePath) { // Attempt to resolve the full template path if it contains any macros, using // the edmx path to get the project and using the project's defined macros. Project project = null; if (!String.IsNullOrEmpty(_edmxPath)) { project = VSHelpers.GetProjectForDocument(_edmxPath); } // Resolve and validate the template file path var errorMessages = new DatabaseGenerationEngine.PathValidationErrorMessages { NullFile = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTemplatePathNotSet, DisplayName), NonValid = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTemplatePathNotValid, DisplayName), ParseError = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ExceptionParsingTemplateFilePath, DisplayName), NonFile = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTemplatePathNonFile, DisplayName), NotInProject = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTemplateFileNotInProject, DisplayName), NonExistant = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_TemplateFileNotExists, DisplayName) }; var templateFileInfo = DatabaseGenerationEngine.ResolveAndValidatePath( project, templatePath, errorMessages); var resolvedTemplatePath = templateFileInfo.FullName; // The workflow will catch any IO exceptions and wrap them in a friendly message var templateContents = File.ReadAllText(resolvedTemplatePath); // Since we are leveraging the VS T4 Host, we will have to ask the environment how to resolve any other assemblies. // The VS T4 Host only resolves: (a) rooted paths (b) GAC'd dlls and (c) Referenced dlls in the project, if a hierarchy is provided // This is a little risky here, but we will use a strict regular expression to replace the assembly references with resolved paths templateContents = Regex.Replace( templateContents, _assemblyDirectiveRegex.ToString(), match => { Debug.Assert( match.Groups.Count == 2, "If we have a match, we should only ever have two groups, the last of which is the assembly path"); if (match.Groups.Count == 2) { var resolvedAssemblyPath = match.Groups[1].Value; // project can be null if we are running via tests. In this case, the custom macros will // be used resolvedAssemblyPath = VsUtils.ResolvePathWithMacro( project, resolvedAssemblyPath, new Dictionary <string, string> { { VsUtils.DevEnvDirMacroName, VsUtils.GetVisualStudioInstallDir() }, { ExtensibleFileManager.EFTOOLS_USER_MACRONAME, ExtensibleFileManager.UserEFToolsDir.FullName }, { ExtensibleFileManager.EFTOOLS_VS_MACRONAME, ExtensibleFileManager.VSEFToolsDir.FullName } }); return(String.Format(CultureInfo.InvariantCulture, AssemblyDirectiveFormat, resolvedAssemblyPath)); } return(match.Value); }); var textTemplatingService = Package.GetGlobalService(typeof(STextTemplating)) as ITextTemplating; Debug.Assert(textTemplatingService != null, "ITextTemplating could not be found from the IServiceProvider"); if (textTemplatingService == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTextTemplatingServiceNotFound, resolvedTemplatePath)); } // Process the template, keeping track of errors var templateCallback = new TemplateCallback(); var templateOutput = String.Empty; textTemplatingService.BeginErrorSession(); templateOutput = textTemplatingService.ProcessTemplate(resolvedTemplatePath, templateContents, templateCallback, null); if (textTemplatingService.EndErrorSession()) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, Resources.TemplateErrorsEncountered, resolvedTemplatePath, templateCallback.ErrorStringBuilder)); } return(templateOutput); }
/// <summary> /// Process a T4 template using Visual Studio's text templating service, given a path that could contain macros (i.e. "$(DevEnvDir)\..."). /// NOTE: Template paths that are not files or are UNC paths are not allowed /// </summary> /// <param name="templatePath">Template's file path which may contain project-based macros</param> /// <returns>The output of processing the template.</returns> protected string ProcessTemplate(string templatePath) { // Attempt to resolve the full template path if it contains any macros, using // the edmx path to get the project and using the project's defined macros. Project project = null; if (!String.IsNullOrEmpty(_edmxPath)) { project = VSHelpers.GetProjectForDocument(_edmxPath); } // Resolve and validate the template file path var errorMessages = new DatabaseGenerationEngine.PathValidationErrorMessages { NullFile = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTemplatePathNotSet, DisplayName), NonValid = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTemplatePathNotValid, DisplayName), ParseError = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ExceptionParsingTemplateFilePath, DisplayName), NonFile = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTemplatePathNonFile, DisplayName), NotInProject = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTemplateFileNotInProject, DisplayName), NonExistant = String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_TemplateFileNotExists, DisplayName) }; var templateFileInfo = DatabaseGenerationEngine.ResolveAndValidatePath( project, templatePath, errorMessages); var resolvedTemplatePath = templateFileInfo.FullName; // The workflow will catch any IO exceptions and wrap them in a friendly message var templateContents = File.ReadAllText(resolvedTemplatePath); // Since we are leveraging the VS T4 Host, we will have to ask the environment how to resolve any other assemblies. // The VS T4 Host only resolves: (a) rooted paths (b) GAC'd dlls and (c) Referenced dlls in the project, if a hierarchy is provided // This is a little risky here, but we will use a strict regular expression to replace the assembly references with resolved paths templateContents = Regex.Replace( templateContents, _assemblyDirectiveRegex.ToString(), match => { Debug.Assert( match.Groups.Count == 2, "If we have a match, we should only ever have two groups, the last of which is the assembly path"); if (match.Groups.Count == 2) { var resolvedAssemblyPath = match.Groups[1].Value; // project can be null if we are running via tests. In this case, the custom macros will // be used resolvedAssemblyPath = VsUtils.ResolvePathWithMacro( project, resolvedAssemblyPath, new Dictionary<string, string> { { VsUtils.DevEnvDirMacroName, VsUtils.GetVisualStudioInstallDir() }, { ExtensibleFileManager.EFTOOLS_USER_MACRONAME, ExtensibleFileManager.UserEFToolsDir.FullName }, { ExtensibleFileManager.EFTOOLS_VS_MACRONAME, ExtensibleFileManager.VSEFToolsDir.FullName } }); return String.Format(CultureInfo.InvariantCulture, AssemblyDirectiveFormat, resolvedAssemblyPath); } return match.Value; }); var textTemplatingService = Package.GetGlobalService(typeof(STextTemplating)) as ITextTemplating; Debug.Assert(textTemplatingService != null, "ITextTemplating could not be found from the IServiceProvider"); if (textTemplatingService == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, Resources.DatabaseCreation_ErrorTextTemplatingServiceNotFound, resolvedTemplatePath)); } // Process the template, keeping track of errors var templateCallback = new TemplateCallback(); var templateOutput = String.Empty; textTemplatingService.BeginErrorSession(); templateOutput = textTemplatingService.ProcessTemplate(resolvedTemplatePath, templateContents, templateCallback, null); if (textTemplatingService.EndErrorSession()) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, Resources.TemplateErrorsEncountered, resolvedTemplatePath, templateCallback.ErrorStringBuilder)); } return templateOutput; }