【问题标题】:How to make connection strings available in a T4 template?如何使连接字符串在 T4 模板中可用?
【发布时间】:2014-08-23 09:12:22
【问题描述】:

我编写了一个 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 的程序集,它会告诉我它已经插入。

另外,有没有办法让 DbContext 可以使用 ConfigurationManager,这样他就可以在后台读取他想要的内容,而无需我将连接字符串传递给他?


已解决,再次感谢:

<#@ 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;

【问题讨论】:

    标签: c# entity-framework t4


    【解决方案1】:

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

    <#@ 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;
    

    【讨论】:

    • "hostspecific" 选项是使用 this.Host: 所必需的
    • Ty,你的评论对我帮助很大。
    • 我没有任何可用的 App.Config,所以这没什么帮助
    • 我得到以下信息:当我尝试这个时:Compiling transformation: 'ConfigurationElement.this[ConfigurationProperty]' is inaccessible due to its protection 并且在网上任何地方都找不到好的解决方案。
    • @mattnificent 固定大小写与AppSettings by stackoverflow.com/a/29424434/2346621 refconfig.AppSettings.Settings["key_name"].Value。用ConnectionStrings 尝试同样的事情。也可以试试ConfigurationUserLevel.PerUserRoaming
    【解决方案2】:

    Injecting Your Web.Config Connection String Into Your T4 Template 为您提供了一个从 web.config 和 app.config 读取连接字符串的 T4 模板。我基于它创建了我的自定义版本,这为我节省了很多时间。

    <#@ 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;}
     }
    }
    #>
    

    【讨论】:

    【解决方案3】:

    如果有人需要解析 Web.config 文件的绝对路径(因为例如,一个 .tt 文件包含在不同的相对路径中的多个其他 .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;
    }
    #>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-22
      • 1970-01-01
      • 1970-01-01
      • 2011-06-09
      • 1970-01-01
      • 2016-11-01
      • 1970-01-01
      相关资源
      最近更新 更多