如何在T4模板中使用连接字符串?

12

我编写了一个T4模板,在其中实例化了一个EF上下文以读取一些数据。问题在于,上下文无法看到来自Web.config的连接字符串。

我该如何使Web.config中的连接字符串在模板中可用?

更多信息:

  • 模板是从头开始编写的
  • EF edmx位于另一个项目中
  • 我在模板中进行的第一个查询告诉我,它无法在模板所在的项目中找到所需的连接字符串

尝试了下面一些解决方案(谢谢),但我得到了这个:

Error 2 Compiling transformation: 'Microsoft.VisualStudio.TextTemplating12165CB53B43A726CBA54A29800255D257AAFD4D5F0DACE4DFE5872F2DEC7739EDF358F49C0444A912B851939903A79BC6180FCEB3FD0D1BF8C0093741DDDACA.GeneratedTextTransformation' does not contain a definition for 'Host' and no extension method 'Host' accepting a first argument of type 'Microsoft.VisualStudio.TextTemplating12165CB53B43A726CBA54A29800255D257AAFD4D5F0DACE4DFE5872F2DEC7739EDF358F49C0444A912B851939903A79BC6180FCEB3FD0D1BF8C0093741DDDACA.GeneratedTextTransformation' could be found (are you missing a using directive or an assembly reference?)

我有以下声明:

<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>

如果我包含Microsoft.VisualStudio.TextTemplating的程序集,它会告诉我它已经被插入了。

此外,是否有办法使ConfigurationManager对DbContext可用,以便他可以在幕后读取任何他想要的内容,而无需我传递连接字符串给他?


问题已解决,再次感谢:

<#@ template hostspecific="true" language="C#" debug="false" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Configuration" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ import namespace="System.Configuration" #>

var map = new ExeConfigurationFileMap();
map.ExeConfigFilename = this.Host.ResolvePath(@"..\Web.config");

var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
var connectionString = config.ConnectionStrings.ConnectionStrings["MyConnectionName"].ConnectionString;
3个回答

10

我以以下方式从App.config文件中的T4模板访问了一个连接字符串:

<#@ template debug="false" hostspecific="true" language="C#" #>

ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap(this.Host.ResolvePath(@"..\ProjName\App.config"));
configFileMap.ExeConfigFilename = this.Host.ResolvePath(@"..\ProjName\App.config");
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
string connectionString = config.ConnectionStrings.ConnectionStrings[0].ConnectionString;

3
为了使用这个,必须使用“hostspecific”选项。主机: - Corwin
1
谢谢,你的评论帮了我很多。 - Dan
我没有任何可用的App.Config文件,所以这并没有什么帮助。 - Jaroslav Daníček
1
当我尝试这样做时,我会得到以下错误信息:Compiling transformation: 'ConfigurationElement.this[ConfigurationProperty]' is inaccessible due to its protection,并且在网上找不到好的解决方案。 - Matthew Beck
在我调用ExeConfigurationFileMap的空构造函数之前,这个方法对我来说一直失败,代码如下:var configFileMap = new ExeConfigurationFileMap(); - qujck
1
@mattnificent 通过 https://dev59.com/Cmoy5IYBdhLWcg3wZdGr#29424434 refconfig.AppSettings.Settings["key_name"].Value 修复了与 AppSettings 相关的问题。尝试使用相同的方法解决 ConnectionStrings 的问题。另外,也可以尝试使用 ConfigurationUserLevel.PerUserRoaming - Brains

5

将Web.Config中的连接字符串注入到T4模板中为您提供了一个T4模板,该模板从web.config和app.config中读取连接字符串。我基于此创建了我的自定义版本,并节省了很多时间。

<#@ assembly name="System.Configuration" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="System.Core.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Configuration" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#+

/// <summary>
/// Provides strongly typed access to the hosting EnvDTE.Project and app.config/web.config
/// configuration file, if present.
///
/// Typical usage from T4 template:
/// <code>ConfigurationAccessor config = new ConfigurationAccessor((IServiceProvider)this.Host);</code>
///
/// </summary>
/// <author>Sky Sanders [sky.sanders@gmail.com, http://skysanders.net/subtext]</author>
/// <date>01-23-10</date>
/// <copyright>The contents of this file are a Public Domain Dedication.</copyright>
///
/// TODO: determine behaviour of ProjectItem.FileNames when referred to a linked file.
///
public class ConfigurationAccessor
{

 /// <summary>
 /// Typical usage from T4 template:
 /// <code>ConfigurationAccessor config = new ConfigurationAccessor((IServiceProvider)this.Host);</code>
 /// </summary>
 public ConfigurationAccessor(IServiceProvider host) : this(host, null)
 {  }

 /// <summary>
 /// Same as default constructor but it looks for a web.config/app.config in the passed config
 /// project location and not in the first startup project it finds. The configProjectLocation
 /// passed should be relative to the solution file.
 /// </summary>
 public ConfigurationAccessor(IServiceProvider host, string configProjectLocation)
 {
  // Get the instance of Visual Studio that is hosting the calling file
  EnvDTE.DTE env = (EnvDTE.DTE)host.GetService(typeof(EnvDTE.DTE));

  // Initialize configuration filename
  string configurationFilename=null;

  // Gets an array of currently selected projects. Since you are either in this file saving it or
  // right-clicking the item in solution explorer to invoke the context menu it stands to reason
  // that there is 1 ActiveSolutionProject and that it is the parent of this file....
  _project = (EnvDTE.Project)((Array)env.ActiveSolutionProjects).GetValue(0);

  // Try and find the configuration file in the active solution project
  configurationFilename = FindConfigurationFilename(_project);

  // If we didn't find the configuration file, check the startup project or passed config
  // project location in the constructor
  if (configurationFilename == null)
  {
   // We are going to get the first *STARTUP* project in the solution
   // Our startup projects should also have a valid web.config/app.config, however,
   // if for some reason we have more than one startup project in the solution, this
   // will just grab the first one it finds
   //
   // We can also supply a config project location to look for in the constructor. This solves
   // the problem in the case where we have multiple startup projects and this file is not
   // in the first, or the config file is not in either the startup project or the active solution
   // project.
   if (!string.IsNullOrEmpty(configProjectLocation))
   {
    _project = (EnvDTE.Project)env.Solution.Projects.Item(configProjectLocation);
   }
   else
   {
    foreach (String s in (Array)env.Solution.SolutionBuild.StartupProjects)
    {
     _project = (EnvDTE.Project)env.Solution.Projects.Item(s);
     break;
    }
   }

   // Try and find the configuration file in one of the projects we found
   configurationFilename = FindConfigurationFilename(_project);
  }

  // Return the configuration object if we have a configuration file name
  // If we do not have a configuration file name, throw an exception
  if(!string.IsNullOrEmpty(configurationFilename))
  {
   // found it, map it and expose salient members as properties
   ExeConfigurationFileMap configFile = null;
   configFile = new ExeConfigurationFileMap();
   configFile.ExeConfigFilename=configurationFilename;
   _configuration = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None);
   }
  else
  {
   throw new ArgumentException("Unable to find a configuration file (web.config/app.config). If the config file is located in a different project, you must mark that project as either the Startup Project or pass the project location of the config file relative to the solution file.");
  }
 }

 /// <summary>
 /// Finds a web.config/app.config file in the passed project and returns the file name.
 /// If none are found, returns null.
 /// </summary>
 private string FindConfigurationFilename(EnvDTE.Project project)
 {
  // examine each project item's filename looking for app.config or web.config
  foreach (EnvDTE.ProjectItem item in project.ProjectItems)
  {
   if (Regex.IsMatch(item.Name,"(app|web).config",RegexOptions.IgnoreCase))
   {
    // TODO: try this with linked files. is the filename pointing to the source?
    return item.get_FileNames(0);
   }
  }

  // not found, return null
  return null;
 }

 private EnvDTE.Project _project;
 private System.Configuration.Configuration _configuration;

 /// <summary>
 /// Provides access to the host project.
 /// </summary>
 /// <remarks>see http://msdn.microsoft.com/en-us/library/envdte.project.aspx</remarks>
 public EnvDTE.Project Project
 {
  get { return _project; }
 }

 /// <summary>
 /// Convenience getter for Project.Properties.
 /// Examples:
 /// <code>string thisAssemblyName = config.Properties.Item("AssemblyName").Value.ToString();</code>
 /// <code>string thisAssemblyName = config.Properties.Item("AssemblyName").Value.ToString();</code>
 /// </summary>
 /// <remarks>see http://msdn.microsoft.com/en-us/library/envdte.project_properties.aspx</remarks>
 public EnvDTE.Properties Properties
 {
  get { return _project.Properties;}
 }

 /// <summary>
 /// Provides access to the application/web configuration file.
 /// </summary>
 /// <remarks>see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.aspx</remarks>
 public System.Configuration.Configuration Configuration
 {
  get { return _configuration; }
 }

 /// <summary>
 /// Provides access to the appSettings section of the configuration file.
 /// Behavior differs from typical AppSettings usage in that the indexed
 /// item's .Value must be explicitly addressed.
 /// <code>string setting = config.AppSettings["MyAppSetting"].Value;</code>
 /// </summary>
 /// <remarks>see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.appsettings.aspx</remarks>
 public  KeyValueConfigurationCollection AppSettings
 {
  get { return _configuration.AppSettings.Settings;}
 }

 /// <summary>
 /// Provides access to the connectionStrings section of the configuration file.
 /// Behavior is as expected; items are accessed by string key or integer index.
 /// <code>string northwindProvider = config.ConnectionStrings["northwind"].ProviderName;</code>
 /// </summary>
 /// <remarks>see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.connectionstrings.aspx</remarks>
 public  ConnectionStringSettingsCollection ConnectionStrings
 {
  get { return _configuration.ConnectionStrings.ConnectionStrings;}
 }
}
#>

这个解决方案在项目嵌套在解决方案文件夹中时存在问题。我能够通过从这里获取的代码解决问题:http://www.wwwlicious.com/2011/03/29/envdte-getting-all-projects-html/ - Thick_propheT

1
如果有人需要解决 Web.config 文件的绝对路径(例如,一个 .tt 文件被包含并从不同的相对路径中使用):
<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="EnvDTE" #>
<#@ Assembly Name="System.Configuration" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Configuration" #>
<#+
string GetConnectionString()
{
    var map = new ExeConfigurationFileMap();
    var configPath = Path.Combine(
        Host.ResolveAssemblyReference("$(SolutionDir)"), "WwwFolderInSolution", "Web.config");

    map.ExeConfigFilename = configPath;

    var config = ConfigurationManager.
        OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

    return config.ConnectionStrings.ConnectionStrings["MyConnectionString"].ConnectionString;
}
#>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接