与 Visual Studio 交互而不依赖 SDK?
我有一些 Workflow Foundation 4 ActivityDesigners,我想与 Visual Studio IDE 交互(它们是 WF4 的事实与问题无关)。
这些设计器在程序集中定义,我们将其称为 herpaderp.dll。最终,这个 dll 将被传递到服务器,其活动和其他代码将在其中永远幸福地生活。
但是,在此之前,我希望我的设计人员能够检查当前的解决方案,以便为使用 herpaderp.dll 中定义的活动的人们提供更好的设计时体验。类似于“让我们使用解决方案中存储的示例数据来测试此活动配置;这是我在方便的组合框中找到的示例数据 - 请选择一个”。
现在,这很简单。这是天真的实现:
var dteo = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(DTE));
var dte = dteo as DTE;
if(dte == null) return; // not in Visual Studio or other wierdness, bail
var samples = dte.GetSampleDatum(); // super awesome extension method
嘿,效果很好!但有一个小问题... herpaderp.dll 现在必须引用以下程序集:
- Microsoft.VisualStudio.Shell.10.0.dll
- envdte.dll
这些程序集是 SDK 的一部分。必须将它们打包并将它们传递到服务器对我来说绝对没有意义。这就像在我的大肠上添加另一个阑尾。
如何打破这些依赖性,同时保留设计师内部与 Visual Studio 交互的能力?
据我所知,我有三个可能的答案,但我对其中没有一个特别满意。
- 使用 IoC 在运行时绑定到程序集,该程序集将为我执行交互。该程序集可以引用 SDK 程序集,同时隐藏在 herpaderp 中定义的简单接口后面。不幸的是,这增加了一个不同的依赖关系,它只在设计时重要,而在服务器上毫无用处。
- 在运行时使用程序集限定名称加载依赖项并隐藏在
dynamic
后面。这冒犯了我的类型安全育种。另外,我不确定我是否能100%逃脱惩罚。 - 在运行时通过某种服务位置与 Visual Studio 包进行交互。我的解决方案提供了 Visual Studio 扩展,因此我不必担心编写它或强迫人们使用它。但为了与之交互,我必须使用某种蹩脚的服务定位器 BS 模式废话,我非常鄙视。服务定位器。呃。此外,它还需要某种跨进程(或者至少是同一进程中的跨AppDomain)通信。
我相信选项 3 是我最好的选择。我还缺少其他解决方案吗?我讨厌我的三个答案之一是不是错了?
I've got some Workflow Foundation 4 ActivityDesigners that I'd like to have interact with the Visual Studio IDE (the fact that they are WF4 doesn't relate to the question).
These designers are defined in an assembly, lets call it herpaderp.dll. Eventually, this dll will be delivered to servers where its Activities and other code will live happily forever after.
But, before this time, I would like for my designers to be able to examine the current solution in order to provide a better design time experience for people using the Activities defined in herpaderp.dll. Something along the lines of "let's test this Activity configuration using sample data that is stored in the solution; here's the sample data I've found in a convenient combo box--please select one".
Now, this is pretty simple to do. Here's the naiive implementation:
var dteo = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(DTE));
var dte = dteo as DTE;
if(dte == null) return; // not in Visual Studio or other wierdness, bail
var samples = dte.GetSampleDatum(); // super awesome extension method
Hey, that worked great! But there's a slight problem... herpaderp.dll now must reference the following assemblies:
- Microsoft.VisualStudio.Shell.10.0.dll
- envdte.dll
These assemblies are part of the SDK. Having to package them up and deliver them to the server makes absolutely no sense to me. Its like adding another appendix to my large intestine.
How can I break these dependencies while retaining the ability to interact with Visual Studio from within my designers?
As I see it, I have three possible answers, none of which I'm particularly happy about.
- Use IoC to bind to an assembly at runtime which will perform the interaction for me. The assembly can reference the SDK assemblies while hiding behind a simple interface defined in herpaderp. Unfortunately, that adds a different dependency which only matters at design time and will be useless on the server.
- Load the dependencies at runtime using their assembly qualified names and hide behind
dynamic
. This offends my type-safe breeding. Plus, I'm not sure if I can even get away with it 100%. - Interract with a Visual Studio Package at runtime via some kind of service location. My solution delivers a Visual Studio extension, so I don't have to worry about writing it or forcing people to use it. But in order to interract with it, I'd have to use some kind of lame Service Locator BS pattern crap which I despise with a passion. Service locator. Feh. In addition, it will also require some kind of cross-process (or, at a minimum, cross-AppDomain in the same process) communication.
Option 3 is my best bet, I believe. Is there another solution I'm missing? Am I just wrong in hating one of my three answers?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我的选择是使用 WCF 创建一个进程范围的(通过命名约定)命名管道来进行来回通信。
这效果非常好。我可以在活动程序集中的内部定义服务和客户端,并在包端实现服务。唯一需要注意的是,默认情况下,服务实现将在 UI 线程上处理调用,因此在从设计界面/客户端进行调用时我无法阻止 UI 线程。通过一些明智的扩展方法魔法,我可以毫不费力地将客户端调用卸载到 ThreadPool 上,通过当前的 SynchronizationContext 编组结果和异常。
我无需付出太多努力就能实现这一点。不幸的是,我最近意识到真正的问题是我应该做的是在不同的程序集中交付我的设计器,而该程序集仅通过设计器安装程序交付。然后,活动将被传递到生产服务器,从而消除任何依赖性担忧。这需要一些我一无所知的惯例和流程的内部知识。这MSDN 博客文章详细介绍了我希望在提出这个问题之前就知道的内容。
The choice I've gone with is to use WCF to create a process-scoped (via a naming convention) named pipe to communicate back and forth.
This works pretty well. I can define the service and the client side within my internals in my Activity assembly and implement the service within the Package side. The only caveat is that, by default, the service implementation will be processing calls on the UI thread, so I cannot block the UI thread when making calls from the design surface/client side. With a little judicious extension method wizardry, I can offload the client calls onto the ThreadPool with little effort, marshalling results and exceptions back across via the current SynchronizationContext.
I was able to implement this without too much effort. Unfortunately, I have recently realized that the true issue is that I should have done was deliver my designers in a different assembly, which is delivered only via the designer installer. The Activities would then be delivered to the production server, thus breaking any dependency worries. This requires some inside knowledge of conventions and process which I had no clue about. This MSDN blog post details what I wish I had known before I asked this question.