简体   繁体   English

来自服务器资源管理器的 T4 模板使用连接

[英]T4 Template Consume connection from Server Explorer

I am trying to build a item template that generates database models and a database context without storing the connection information in the source code.我正在尝试构建一个生成数据库模型和数据库上下文的项目模板,而不将连接信息存储在源代码中。

I have successfully interfaced a item template wizard with server explorer and can set a connection key in the settings.ttinclude.我已经成功地将项目模板向导与服务器资源管理器连接起来,并且可以在 settings.ttinclude 中设置连接密钥。

The problem is that I can not resolve the interface to the IVsDataExplorerConnectionManager from the DTE.问题是我无法从 DTE 解析到 IVsDataExplorerConnectionManager 的接口。

I believe I have barked up the wrong tree, because this is the way to get the server explorer in a VSIX project.我相信我找错了树,因为这是在 VSIX 项目中获取服务器资源管理器的方法。 I was hoping similar code would work for the T4 visual studio templating engine.我希望类似的代码适用于 T4 Visual Studio 模板引擎。

I have spent several hours looking to see if anyone else has already done something similar and I have found nothing.我花了几个小时寻找其他人是否已经做过类似的事情,但我一无所获。 any ideas on how I could consume a connection from the server explorer in a T4 template would be appreciated.任何关于如何在 T4 模板中使用来自服务器资源管理器的连接的想法都将不胜感激。

Update 7/23/2020 2020 年 7 月 23 日更新

I have since learned that the T4 ITextTemplatingEngineHost that is stock with the default Custom Tool does not support using Dependency Injection to retrieve the connection manager.从那以后,我了解到使用默认自定义工具的 T4 ITextTemplatingEngineHost 不支持使用依赖注入来检索连接管理器。 the solution is implement a templating file generator that will access the information that I am looking for.解决方案是实现一个模板文件生成器,它将访问我正在寻找的信息。 It is also not as simple as implementing a EngineHost Service.它也不像实现 EngineHost 服务那么简单。 Turns out the TextTemplatingService that is internal to visual studio may implement the interfaces required to support the text templating generator.原来 Visual Studio 内部的 TextTemplatingService 可以实现支持文本模板生成器所需的接口。 But, internally the service does not use the interfaces.但是,内部服务不使用接口。 Which makes the templating service very rigid and not as robust as I would like.这使得模板服务非常僵化并且不像我想要的那样健壮。 the solution in progress appears to build a new templating service that wraps visual studio service and override the TemplatedCodeGenerator and override ProcessTemplate and substitute the wrapped service.正在进行的解决方案似乎构建了一个新的模板服务,该服务包装了 Visual Studio 服务并覆盖了 TemplatedCodeGenerator 并覆盖了 ProcessTemplate 并替换了包装的服务。 I am by no means finished and I am dealing with some additional hurdles, which I may ask questions in other posts.我还没有完成,我正在处理一些额外的障碍,我可能会在其他帖子中提出问题。

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="$(DevEnvDir)PublicAssemblies\Microsoft.VisualStudio.Data.Services.dll" #>
<#@ assembly name="$(DevEnvDir)PublicAssemblies\Microsoft.VisualStudio.OLE.Interop.dll" #>
<#@ assembly name="$(DevEnvDir)PublicAssemblies\Microsoft.VisualStudio.Shell.15.0.dll" #>
<#@ assembly name="$(DevEnvDir)PublicAssemblies\Microsoft.VisualStudio.Shell.Interop.dll" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Configuration" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Data.Common" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Configuration" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.Shell" #>
<#@ import namespace="Interop = Microsoft.VisualStudio.OLE.Interop" #>
<#@ import namespace="Microsoft.VisualStudio.Data.Services" #>
<#+
public class Settings
{
    const string connectionKey = @"$connectionKey$";
    readonly Guid connectionExplorerGuid = Guid.Parse("8B6159D9-A634-4549-9EAC-8642744F1042");

    public static ITextTemplatingEngineHost Host { get; set; }

    public string[] ExcludeTables
    {
        get
        { 
            return new string[]{
                "sysdiagrams",
                "BuildVersion",
                "aspnet_Applications",
                "aspnet_Membership",
                "aspnet_Paths",
                "aspnet_PersonalizationAllUsers",
                "aspnet_PersonalizationPerUser",
                "aspnet_Profile",
                "aspnet_Roles",
                "aspnet_SchemaVersions",
                "aspnet_Users",
                "aspnet_UsersInRoles",
                "aspnet_WebEvent_Events"
                };
        }
    }

    public static IVsDataConnection Connection
    {
        get
        {
            if (Host is IServiceProvider service)
            {
                if (service.GetService(typeof(EnvDTE.DTE)) is Interop.IServiceProvider provider)
                {
                    if (PackageUtilities.QueryService<IVsDataExplorerConnectionManager>(provider) is IVsDataExplorerConnectionManager manager)
                    {
                        return manager.Connections[connectionKey].Connection;
                    }
                    throw new InvalidOperationException("Unable to resolve IVsDataExplorerConnectionManager!");
                }
                throw new InvalidOperationException("Unable to resolve DTE as Interop.IServiceProvider!");
            }
            throw new Exception("Host property returned unexpected value (null)");
        }
    }
}
#>

the test code that includes the above包含上述内容的测试代码

<#@ template hostspecific="true" language="C#" #>
<#@ include file="Settings.ttinclude" #>
<#
    Settings.Host = Host;
#>

using Microsoft.Extensions.DependencyInjection;
using SubSonic;
using System;

namespace $rootnamespace$
{
    public partial class $safeitemrootname$
        : SubSonicContext
    {
        private readonly IServiceCollection services = null;

        public $safeitemrootname$(IServiceCollection services)
        {
            this.services = services ?? throw new ArgumentNullException(nameof(services));
        }

        public string ConnectionString => "<#= Settings.Connection.DisplayConnectionString #>";

        #region ISubSonicSetCollection{TEntity} Collection Properties
        #endregion
    }
}

there is not an in the box solution to address the issue of getting the connection manager with visual studio as it is now.没有现成的解决方案来解决现在使用 Visual Studio 获取连接管理器的问题。 The solution resides in building a custom templating generator that will know how to communicate with the visual studio extensions.解决方案在于构建一个自定义模板生成器,该生成器将知道如何与 Visual Studio 扩展进行通信。 which is not an easy task on it's own.这本身并不是一件容易的事。

okay, I have developed the answer for this, but first here is the settings.ttinclude, as outputted from the item template /with wizard好的,我已经为此制定了答案,但首先是 settings.ttinclude,从项目模板/with 向导输出

<#@ template language="C#" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Configuration" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Data.Common" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Configuration" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="EnvDTE" #>
<#+
public class Settings
{
    const string connectionKey = @"home-prime\localdb#7fe04ab3.DbSubSonic.dbo";

    public static ITextTemplatingEngineHost Host { get; set; }

    public string[] ExcludeTables
    {
        get
        { 
            return new string[]{
                "sysdiagrams",
                "BuildVersion",
                "aspnet_Applications",
                "aspnet_Membership",
                "aspnet_Paths",
                "aspnet_PersonalizationAllUsers",
                "aspnet_PersonalizationPerUser",
                "aspnet_Profile",
                "aspnet_Roles",
                "aspnet_SchemaVersions",
                "aspnet_Users",
                "aspnet_UsersInRoles",
                "aspnet_WebEvent_Events"
                };
        }
    }

    public static IDataConnection Connection
    {
        get
        {
            if (Host is IServiceProvider service)
            {
                if (service.GetService(typeof(ISubSonicCoreService)) is ISubSonicCoreService subsonic)
                {
                    return subsonic.ConnectionManager[connectionKey];
                }
                throw new InvalidOperationException("Unable to resolve ISubSonicCoreService!");
            }
            throw new Exception("Host property returned unexpected value (null)");
        }
    }
}
#>

Second the t4 generated class file, this is a proof of concept and in the future the actual connection string will never surface in the generated code.其次,t4 生成的 class 文件,这是一个概念证明,将来实际的连接字符串将永远不会出现在生成的代码中。

using Microsoft.Extensions.DependencyInjection;
using SubSonic;
using System;

namespace TemplateIntegrationTest.DAL
{
    public partial class DataContext1
        : SubSonicContext
    {
        private readonly IServiceCollection services = null;

        public DataContext1(IServiceCollection services)
        {
            this.services = services ?? throw new ArgumentNullException(nameof(services));
        }

        public string ConnectionString => @"Data Source=(localdb)\MSSQLLocalDb;Initial Catalog=DbSubSonic;Integrated Security=True";

        #region ISubSonicSetCollection{TEntity} Collection Properties
        #endregion
    }
}

override the templating service ProcessTemplate method覆盖模板服务 ProcessTemplate 方法

public string ProcessTemplate(string inputFile, string content, ITextTemplatingCallback callback = null, object hierarchy = null)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            string result = "";

            if (this is ITextTemplatingComponents SubSonicComponents)
            {
                SubSonicComponents.Hierarchy = hierarchy;
                SubSonicComponents.Callback = callback;
                SubSonicComponents.InputFile = inputFile;

                SubSonicComponents.Host.SetFileExtension(SearchForLanguage(content, "C#") ? ".cs" : ".vb");

                result = SubSonicComponents.Engine.ProcessTemplate(content, SubSonicComponents.Host);

                // TextTemplatingService which is private and can not be replicated with out implementing from scratch.

                // SqmFacade is a DTE wrapper that can send additional commands to VS
                if (SearchForLanguage(content, "C#"))
                {
                    SqmFacade.T4PreprocessTemplateCS();
                }
                else if (SearchForLanguage(content, "VB"))
                {
                    SqmFacade.T4PreprocessTemplateVB();
                }
            }

            return result;
        }

To make this happen I had to do the following:为了实现这一点,我必须执行以下操作:

  • build a templating generator that uses a wrapped STextTemplating service in visual studio.在 Visual Studio 中构建一个使用包装的 STextTemplating 服务的模板生成器。
  • the wrapped service must override the ITextTemplatingEngineHost包装的服务必须覆盖 ITextTemplatingEngineHost
  • override the ProcessTemplate method, the vs service may implement the interface but it does it in a way the host cannot be overridden.覆盖 ProcessTemplate 方法,vs 服务可以实现接口,但它以主机无法覆盖的方式实现。
  • Last, placed my built dll libraries in the GAC.最后,将我构建的 dll 库放在 GAC 中。 apparently, there is still a bug with the T4 engine ignoring the fact that the codebase location is ignored and it looks for the libraries elsewhere and does not find them.显然,T4 引擎仍然存在一个错误,忽略了代码库位置被忽略的事实,它在其他地方查找库但没有找到它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM