Joe*_*ish 10 sql-server visual-studio-extensions ssms-18
我想创建一个从执行计划窗口打开的 SQL Server Management Studio v18 扩展。我相信这在技术上是可行的,因为已经有第三方工具可以做到这一点:
到目前为止,我已经能够使用此处的指南在 SSMS v18 中创建基本扩展。我还可以通过引用文档中的 ID 来移动按钮的位置。但是,我不知道如何修改 .vsct 文件以便将按钮移到执行计划窗口内。
如何创建从执行计划窗口打开的 SSMS 扩展?
我想到了。
在<Symbols>
*.vsct 文件的元素中添加
<GuidSymbol name="foo1" value="{33F13AC3-80BB-4ECB-85BC-225435603A5E}">
<IDSymbol name="foo2" value="0x0080"/>
</GuidSymbol>
Run Code Online (Sandbox Code Playgroud)
然后改变
<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
Run Code Online (Sandbox Code Playgroud)
到
<Parent guid="foo1" id="foo2"/>
Run Code Online (Sandbox Code Playgroud)
如此处所述。
(如果渴望在菜单中更高的位置看到您的扩展,您可能还需要将priority
父<Group
元素设置为 )0x0001
我确定魔法 Guid 的机制最初非常费力,并且在这个答案的编辑历史中,但一个不太费力的方法是设置注册表项
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\SQL Server Management Studio\18.0_IsoShell\General]
"EnableVSIPLogging"=dword:00000001
Run Code Online (Sandbox Code Playgroud)
然后打开SSMS,获取执行计划+ Ctrl+Shift右键弹出如下消息框(十进制128为0x80
)。
对于我来说,如何在菜单单击事件中执行任何有用的操作并不是很明显,因此我认为添加一个示例会提供有用的信息。
作为 POC,我尝试对子树成本高于阈值和/或底层执行计划运算符遇到一些潜在问题条件的节点进行着色。
为此,我将Execute
模板生成的代码中的方法中的代码更改为
private void Execute(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread();
Dictionary<string, Color> coloringRules = new Dictionary<string, Color>
{
[@".//ns:Intrinsic[@FunctionName = ""GetRangeThroughConvert""]"] = Color.Yellow,
[@".//ns:Intrinsic[@FunctionName = ""GetRangeWithMismatchedTypes""]"] = Color.MediumPurple
};
ProofOfConcept.ColorInterestingNodes(coloringRules, costThreshold: 0.0032831);
}
Run Code Online (Sandbox Code Playgroud)
这依赖于以下嵌套类
private class ProofOfConcept
{
private const string ShowPlanControlTypeFullName = "Microsoft.SqlServer.Management.UI.VSIntegration.Editors.ShowPlan.ShowPlanControl";
private const string GraphControlTypeFullName = "Microsoft.SqlServer.Management.SqlMgmt.ShowPlan.GraphControl";
private const string ShowPlanNamespaceUri = "http://schemas.microsoft.com/sqlserver/2004/07/showplan";
private static readonly XNamespace ns = ShowPlanNamespaceUri;
[DllImport("user32.dll")]
public static extern IntPtr GetFocus();
public static void ColorInterestingNodes(Dictionary<string, Color> coloringRules, double costThreshold)
{
IntPtr focus = GetFocus();
Control activeControl = Control.FromChildHandle(focus);
Control rootControl = FindRootControl(activeControl);
List <Control> graphControls = new List<Control>();
FindAllDescendantControlsOfType(rootControl, graphControls, GraphControlTypeFullName);
XElement[] qpElements = GetShowPlanXMLQueryPlans(rootControl);
//TODO: More robust method of matching up the query plan XML elements with the display elements.
//e.g. "Use database;" statement will show a graph in "estimated" plan but not "actual" - and not have a QueryPlan element in the XML
if (graphControls.Count != qpElements.Count())
{
MessageBox.Show("Mismatch between graph control count (" + graphControls.Count + ") and query plan count (" + qpElements.Count() + "). Exiting");
return;
}
for (var index = 0; index < graphControls.Count; index++)
{
Control graphControl = graphControls[index];
XElement qpElement = qpElements[index];
Dictionary<int, Color> nodeBackgroundColors = GetNodeBackgroundColors(qpElement, coloringRules);
foreach (dynamic item in ((dynamic)graphControl).Nodes)
{
var nodeId = item.NodeOriginal["NodeId"] ?? -1;
if (item.NodeOriginal.Cost >= costThreshold)
{
item.TextColor = Color.Red;
item.BackgroundColor = Color.White;
item.UseBackgroundColor = true;
}
if (nodeBackgroundColors.TryGetValue(nodeId, out Color color))
{
item.BackgroundColor = color;
item.UseBackgroundColor = true;
}
}
graphControl.Refresh();
}
}
private static Dictionary<int, Color> GetNodeBackgroundColors(XElement queryPlan, Dictionary<string, Color> coloringRules)
{
var returnValue = new Dictionary<int, Color>();
NameTable nt = new NameTable();
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nt);
namespaceManager.AddNamespace("ns", "http://schemas.microsoft.com/sqlserver/2004/07/showplan");
foreach (var coloringRule in coloringRules)
{
var foundElements = queryPlan.XPathSelectElements(coloringRule.Key, namespaceManager);
foreach (var foundNode in foundElements)
{
var nodeId = foundNode.AncestorsAndSelf(ns + "RelOp").FirstOrDefault()?.Attribute("NodeId")?.Value;
if (nodeId != null)
{
returnValue[int.Parse(nodeId)] = coloringRule.Value;
}
}
}
return returnValue;
}
private static XElement[] GetShowPlanXMLQueryPlans(Control rootControl)
{
List<Control> showPlanControls = new List<Control>();
FindAllDescendantControlsOfType(rootControl, showPlanControls, ShowPlanControlTypeFullName);
Assembly sqlEditorsAssembly = Assembly.Load("SQLEditors");
Type showPlanControlType = sqlEditorsAssembly.GetType(ShowPlanControlTypeFullName);
MethodInfo GetShowPlanXmlMethod = showPlanControlType.GetMethod("GetShowPlanXml", BindingFlags.Instance | BindingFlags.NonPublic);
string xplan = GetShowPlanXmlMethod.Invoke(showPlanControls[0], null) as string;
XDocument doc = XDocument.Parse(xplan);
return doc.Descendants(ns + "QueryPlan").ToArray();
}
private static Control FindRootControl(Control control)
{
while (control.Parent != null)
control = control.Parent;
return control;
}
private static void FindAllDescendantControlsOfType(Control control, List<Control> graphControls, string typeFullName)
{
if (control.GetType().FullName == typeFullName)
graphControls.Add(control);
foreach (Control child in control.Controls)
FindAllDescendantControlsOfType(child, graphControls, typeFullName);
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
806 次 |
最近记录: |