CLR

使用 CLR 存储过程的最短路径图形分析

James McCaffrey

下载代码示例

图分析变得越来越重要在软件应用程序中。 在这里一个图是一个集合的节点和边缘,没有如条形图的数据可视化。 本文介绍了如何执行使用 SQL CLR 存储过程的最短路径分析的演示。 这里介绍的技术也可以用于很多其他的数据访问编程任务。

最短路径图分析真正涉及到两个密切相关的问题。 首先是要确定结束节点的跃点数目从指定的图形开始节点的最短路径。 第二个问题是如果图连接有某种重量确定最短路径的长度。 例如,假设在一个图中的节点代表人民和节点之间的边表示电子邮件通信。 你可能会感兴趣最短的地方你隐式地假设每个跃点有 1 重量的两个人之间的跃点数。 这是类似于"六度的凯文 · 培根"游戏或一个研究员的鄂尔多斯号码。 如果每个图形边缘有重量 — — 例如,表示某种程度的关系的重要性 — — 你可能想要确定考虑重量的重要性,考虑到两人之间的最短路径。

但为什么使用 CLR 存储过程吗? 传统的最短路径算法假定问题图表示形式可以将完全存储在计算机内存,通常在一个矩阵或邻接列表中。 对于大图 — — 例如,图形表示的社交网络 — — 这种做法往往并不可行。 大图可以方便地存储在一个 SQL 数据库中。 为执行一个 SQL 数据库中存储的图的最短路径分析的一种方法是编写一个本机 SQL 语言存储过程。 MSDN 杂志文章,"基于图与 SQL 的最短数据路径分析"(msdn.microsoft.com/magazine/jj863138),解释了这种方法的详细信息。 然而,使用 CLR 存储的过程而不是纯粹的 SQL 方法可以提供显著更好的性能和更大的灵活性,为自定义。

看一看虚拟图形表示法在图 1。 图有八个节点。 每个箭头旁边的数字代表的重量。 222 节点与节点 444 之间的最短路径是 222-> 555-> 666-> 777-> 444,已加权的距离 1.0 + 1.0 + 1.0 + 2.0 = 5.0。 222-> 的通知 333-> 666-> 777-> 444 也是从 222 到 444 的最短路径。

Dummy Graph for Shortest-Path Analysis
图 1 虚拟图的最短路径分析

图 2 显示截图到 CLR 调用的存储过程命名为 csp_ShortestPath,用于确定节点 222 和 444 节点之间的最短路径。 在这种情况下的最短路径显示为以分号分隔的字符串按相反的顺序。 输出是在图像的底部。 图像的顶端部分包含创建关系图中的一个对应的 SQL 语句图 1

Calling a Shortest-Path CLR Stored Procedure
图 2 调用的最短路径 CLR 存储过程

本文假定您拥有先进的 C# 编程技巧和 SQL 的基本熟悉。 现在所有的 SQL 代码,以创建虚拟图和所有的 C# 代码来创建 CLR 存储过程,并还描述了几种方法来调用 CLR 存储过程。 这篇文章的所有代码都是可用在 archive.msdn.microsoft.com/mag201305Graph

创建数据库

若要创建虚拟基于 SQL 的关系图,我发起了 Microsoft SQL 服务器管理工作室 (SSMS),连接到 SQL Server 2008 数据库的实例。 通过 SQL Server 2005 及更高版本支持 CLR 存储过程。 第一,我创建了一个数据库命名为 dbShortPathWithCLR,使用下列命令:

    use master go if exists(select * from sys.sysdatabases where name='dbShortPathWithCLR')   drop database dbShortPathWithCLR go create database dbShortPathWithCLR go use dbShortPathWithCLR go

我强烈建议尝试虚拟数据库而不与生产数据库。 命令来创建一个表包含节点和边缘的数据是:

    create table tblGraph (   fromNode bigint not null,   toNode bigint not null,   edgeWeight real not null ) go

作为 SQL 类型 bigint,大致对应于 C# 类型长存储节点 Id。 边缘权重存储为类型真实的这是 SQL 类型 float(24),C# 类型双大致对应的同义词。 在许多情况下,你不会关注边缘重量和 edgeWeight 列,则可以省略。

中的 14 语句图 3 定义关系图。

图 3 定义关系图

    insert into tblGraph values(111,222,1.0) insert into tblGraph values(111,555,1.0) insert into tblGraph values(222,111,2.0) insert into tblGraph values(222,333,1.0) insert into tblGraph values(222,555,1.0) insert into tblGraph values(333,666,1.0) insert into tblGraph values(333,888,1.0) insert into tblGraph values(444,888,1.0) insert into tblGraph values(555,111,2.0) insert into tblGraph values(555,666,1.0) insert into tblGraph values(666,333,2.0) insert into tblGraph values(666,777,1.0) insert into tblGraph values(777,444,2.0) insert into tblGraph values(777,888,1.0) go

如果你比较中的语句图 3 中的图像与图 1 你会看到每个语句对应的节点之间的边。

下一步,该数据库被编写的最短路径分析:

    create nonclustered index idxTblGraphFromNode on tblGraph(fromNode) go create nonclustered index idxTblGraphToNode on tblGraph(toNode) go create nonclustered index idxTblGraphEdgeWeight on tblGraph(edgeWeight) go alter database dbShortPathWithCLR set trustworthy on  go select is_trustworthy_on from sys.databases   where name = 'dbShortPathWithCLR' go

第三个语句的 fromNode、 toNode 和 edgeWeight 的列上创建索引。 使用的大图表时,索引是几乎总是必要给予合理的性能。 下面两个语句更改数据库,所以值得信赖属性设置为"打开"。属性的默认值是"关闭"。我会解释为什么值得信赖的属性必须设置为"on"不久。

此时将创建虚拟基于 SQL 的关系图。 下一步是要使用 Visual Studio 创建 CLR 存储过程来执行最短路径分析。

创建 CLR 存储过程

若要创建 CLR 最短路径存储的过程,我发起了 Visual Studio 2010。 若要创建 CLR 存储过程您的开发环境必须包括 Microsoft.NET Framework 3.5 或更高版本。 从文件 |新 |项目菜单选择了数据库 |SQL Server 项目模板组,然后选择 Visual C# SQL CLR 数据库项目的选项,如中所示图 4。 我名为 CreateStoredProc 的项目。

A New CLR Project in Visual Studio 2010
图 4 新的 CLR 项目在 Visual Studio 2010 中

.NET Framework 3.5 新项目对话框的下拉列表控件中选定的通知。 到目标框架的版本必须与承载数据库的 SQL 服务器上框架的版本相匹配。 因为虚拟数据库是 SQL Server 2008 实例上,.NET Framework 3.5 靶子 如果虚拟数据库的 SQL Server 2005 实例上,我会一直针对.NET Framework 2.0。 CLR 存储过程文档是有点阴暗中描述的框架版本的开发环境中,在 SQL Server 上和 C# 存储的过程创建项目中的相关性。 您可能必须诉诸有点试验和错误。

后单击确定以创建 CLR 存储的过程创建项目,Visual Studio 会提示与目标数据库的名称和身份验证模型有关的信息的请求 (请参阅图 5)。 后单击确定的新建数据库引用对话框中,Visual Studio 将加载一个新的项目,但并不直接创建一个模板。 要生成的模板,请右键单击项目名称 — — 在本例中为 CreateStoredProc — — 并选择添加 |从上下文菜单中的新项目 (请参阅图 6)。

New Database Reference for the Project
图 5 为项目的的新数据库引用

New Item Stored Procedure
图 6 新项目存储过程

我所选的存储过程项目,并将它命名为 csp_ShortestPath.cs。 此名称,不带.cs 扩展名,将成为在目标数据库中的存储过程的名称。 作为一种样式,我一般预置 csp 与 CLR 存储过程的名称来区分它们从系统存储过程 (sp),扩展存储的过程 (xp) 和用户定义的存储的过程 (usp)。 单击添加按钮后, Visual Studio 生成一个名为 StoredProcedures 的分部类的一个轻量级模板模板:

using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; public partial class StoredProcedures {   [Microsoft.SqlServer.Server.SqlProcedure]   public static void csp_ShortestPath()   {     // Put your code here   } };

可怜的男人优先级队列

在这篇文章中提出的最短路径算法需要随机访问优先级队列的数据结构。 .NET 框架不提供一个内置优先队列,将完全满足需要的最短路径算法,因此您必须编写代码自己优先级队列。

优先队列是类似于普通的先出 (FIFO) 队列,有一些的区别。 优先级队列中的项目被假定除了数据字段具有某种形式的优先领域。 例如,一组客户技术支持在等待一个优先队列项目可能包括客户名称和客户一直在等待服务的时间长度。

优先级队列支持出列操作,总是删除具有最高优先级的项。 在这里,最高的含义取决于问题的背景情况和可以是一个最大值或最小值。 随机访问优先级队列支持修改的具有指定 ID 的项的优先级字段的操作

本文介绍了一个穷人优先队列 — — 一种获取完成工作,但有很大的改进余地。 在哪里在队列中的项目有两个字段的优先级队列上操作的最短路径算法:一个节点 ID (如 111 或 222) 和距离的字段。 距离领域由算法用于存储在任何给定的时间,在该算法的执行过程中的启动节点和项目节点之间的最佳 (最短) 已知的距离。 距离领域充当项目的优先级,并为距离较小的值代表更高的优先级。

因此,若要支持随机访问优先级队列,C# 存储的过程模板需要额外使用引用 System.Collections.Generic 的命名空间和两个额外的程序定义的类定义的语句放置低于 StoredProcedures 的分部类:

public class NodeDistance {   // Definition here } public class MyPriorityQueue {   // Definition here } Class NodeDistance is simple: public class NodeDistance {   public long nodeID;   public double distance;   public NodeDistance(long nodeID, double distance)   {     this.
nodeID = nodeID;     this.distance = distance;   } }

为简单起见使用公共的类字段的范围。 优先级队列是基本上 NodeDistance 列表中的项目:

public class MyPriorityQueue {   public List<NodeDistance> list;   public MyPriorityQueue()   {     this.list = new List<NodeDistance>();   }

再次,我使用公共范围内,为简单起见只。 出列操作中删除具有最小的距离值的 NodeDistance 项目:

public NodeDistance Dequeue() {   int i = IndexOfSmallestDist();   NodeDistance result = this.list[i];   this.list.RemoveAt(i);   return result; }

方法将出列调用的帮助器方法来查找具有最小的距离、 保存对该项目的引用,然后使用内置的 List.RemoveAt 方法,删除的项目的项的位置。

帮助器方法 IndexOfSmallestDist 定义为:

private int IndexOfSmallestDist() {   double smallDist = this.list[0].distance;   int smallIndex = 0;   for (int i = 0; i < list.Count; ++i) {     if (list[i].distance < smallDist) {       smallDist = list[i].distance;       smallIndex = i;     }   }   return smallIndex; }

该方法并通过基础列表集合的简单线性搜索。 请注意这种方法意味着出列有 o (n) 的性能。

入队操作定义如下:

public void Enqueue(NodeDistance nd) {   list.Add(nd); }

更改优先级运算定义如下:

public void ChangePriority(long nodeID, double newDist) {   int i = IndexOf(nodeID);   list[i].distance = newDist; }

方法 ChangePriority 调用帮助器方法定位的 NodeDistance 项目,鉴于该项目的 ID,位置的 IndexOf 和被定义为:

private int IndexOf(long nodeID) {   for (int i = 0; i < list.Count; ++i)     if (list[i].
nodeID == nodeID)       return i;   return -1; }

同样要注意因为 IndexOf 方法执行线性搜索时,其性能是 o (n)。

最短路径算法需要知道在任何给定时间的优先级队列中的项的数目:

public int Count() {   return this.list.Count; }

总结,可怜人的随机访问优先级队列在此处定义支持排队操作 ; Count 属性 ; 移除具有最小的距离 ; 的 NodeDistance 项出列操作 和修改指定项的距离 ChangePriority 操作。 操作出列和 ChangePriority 有 o (n) 的表现。

对于大的图,最短路径算法的性能是高度依赖于使用的优先队列的效率。 虽然 o (n) 性能通常是可以接受的它是可以实现更好的 O (log n) 表现的优先队列。 此类实现通常使用二进制堆数据结构,而是相当棘手。 我在我的视觉工作室杂志文章,"优先队列与 C# 中,"可用在描述高效的优先级队列 bit.ly/QWU1Hv

创建存储的过程

定义的自定义优先级队列,可以实施的最短路径算法。 方法签名是:

public static void csp_ShortestPath(System.Data.SqlTypes.SqlInt64   startNode, SqlInt64 endNode, SqlInt32 maxNodesToCheck,   out SqlString pathResult, out SqlDouble distResult) {

方法 csp_ShortestPath 接受三个输入的参数,并具有两个输出结果参数。 参数 startNode 持有的节点的节点 ID,以开始搜索。 还记得在定义中,SQL 表,列 fromNode 和 toNode 被定义为 SQL 类型 bigint,长时间对应于 C# 类型。 当定义 CLR 存储过程时,该过程的参数使用 System.Data.SqlTypes 命名空间中定义的类型模型。 这些类型通常是很容易就能映射到 SQL 类型和 C# 的类型。 在这里,类型 bigint 映射到个 SqlInt64。 输入的参数 endNode 也被声明为类型个 SqlInt64。

输入的参数 maxNodesToCheck 用来防止失控极大的关系图上的存储的过程。 在这里,maxNodesToCheck 被声明为类型 SqlInt32,对应于 C# 类型 int。

如果你是新到的 SQL 存储过程,你可能会惊讶来学习他们可以没有显式的返回值,也可以返回一个 int 值,但他们不能显式地返回的任何其他数据类型。 所以在 SQL 存储过程的位置的情况下必须返回两个或多个值或返回值不是 int 类型,采取的办法是使用 out 参数。 在这里,CLR 存储过程的最短路径作为字符串返回,如"444 ; 777 ; 666 ; 333 222,",并返回作为一个数字值,如 5.0 的最短路径的总距离。 所以 pathResult out 参数声明为类型 SqlString 和 distResult out 参数声明为类型 Sql­双,分别对应于 C# 的类型字符串和双精度型。

CLR 存储过程定义继续通过设置四个数据结构:

Dictionary<long, double> distance = new Dictionary<long, double>(); Dictionary<long, long> previous = new Dictionary<long, long>(); Dictionary<long, bool> beenAdded = new Dictionary<long, bool>(); MyPriorityQueue PQ = new MyPriorityQueue();

最短路径算法执行,在任何给定的点,该算法需要到所有其他节点从启动节点访问的当前最佳 (最短) 已知总距离。 名为"距离"的字典集合保存此信息。 字典的键是一个节点 ID 和字典的值是最短的已知总距离。 字典集合命名为"上一个"存储到的最短路径中的一个关键节点 ID 的前任节点 ID。 例如,在示例中所示图 2,结束节点是 444。 其最短路径中的立即前身是 777。 所以以前 [444] = 777。 本质上以前集合拥有的实际的最短路径已编码的方式。

名为"添加"为字典集合包含指示图的节点是否已添加到该算法的优先级队列的信息。 布尔值是一个虚拟的值,因为它不真的需要,确定节点是否在集合中。 你可能想要使用哈希表而不是字典集合。 命名"PQ"的自定义优先级队列是定义和这篇文章的前面部分中的说明。

存储的过程定义将继续:

SqlConnection conn = null; long startNodeAsLong = (long)startNode; long endNodeAsLong = (long)endNode; int maxNodesToCheckAsInt = (int)maxNodesToCheck;

SqlConnection 对象是与目标图数据库的单一连接。 所以,我可以检查其状态后,如果发生异常声明在这里。 虽然不是严格必要的写 CLR 存储的过程我更喜欢显式创建本地 C# 时键入对应的 SQL 变量类型参数的变量。

定义将继续:

distance[startNodeAsLong] = 0.0; previous[startNodeAsLong] = -1; PQ.Enqueue(new NodeDistance(startNodeAsLong, 0.0)); beenAdded[startNodeAsLong] = true;

这些代码行初始化启动节点。 因为从起始节点到本身的距离为零距离字典中的值设置为 0.0。 到启动节点的前任不存在因此,值为-1 用于指示这一点。 优先级队列初始化的启动节点中,并更新添加字典集合。

定义将继续:

try {   string connString = "Server=(local);Database=dbShortPathWithCLR;" +     "Trusted_Connection=True;MultipleActiveResultSets=True";   conn = new SqlConnection(connString);   conn.Open();   double alt = 0.0;  // 'Test distance'

写 CLR 存储过程时我更喜欢使用显式的 try catch 块而比更优雅使用语句。 设置连接字符串时你有两个选项。 在许多情况下,因为在 SQL 数据库中,在同一台计算机上运行该存储的过程你可以简单地连接字符串设置为"上下文连接 = true."在理论上的上下文连接将提供更好的性能比标准的连接。 但是,上下文连接有几个限制。 一个限制是它可以支持只有单个 SQL 数据读取器。 你不久就会看到,最短路径分析经常 (但不是总是) 需要两个 SQL 数据读取器。

因为此 CLR 存储过程需要两个读者,所以使用一个标准的连接字符串,包含一个子句,将 MultipleActiveResultSets 属性设置为 true。 此子句目前不支持 SQL 上下文连接。 因为存储的过程使用标准的连接,而不是上下文的连接,如中所示,Visual Studio 存储的过程创建项目数据库的访问权限级别都必须设置为外部、 图 7。 然而,为了将此属性设置 SQL 数据库必须具有其值得信赖的属性设置为"打开",作为所示后面在图 2

Setting Database Permission Level
图 7 设置数据库的权限级别

总之,创建图形数据库时,数据库可信赖属性设置为"on"。这允许 Visual Studio 项目要向外部设置的数据库权限级别属性。 这将允许使用标准的连接,而不是上下文连接使用的存储的过程定义。 这将允许连接的 MultipleActiveResultSets 属性设置为 true。 这允许在存储过程中的两个 SQL 数据读取器。

存储的过程定义将继续:

while (PQ.Count() > 0 && beenAdded.Count < maxNodesToCheckAsInt) {   NodeDistance u = PQ.Dequeue();   if (u.
nodeID == endNode) break;

这里所使用的算法是 Dijkstra 的最短路径,其中最著名的变体在计算机科学中。 虽然很短,该算法是很微妙的可以在很多方面修改。 算法的核心是一个循环,终止时的优先级队列为空。 在这里,被添加额外健全性检查,基于处理的关系图节点的总数。 里面的主循环,优先队列出列呼叫队列具有最佳 (最短) 已知总距离从启动节点中返回的节点。 如果只是删除了该节点从优先级队列是结束节点,然后最短路径已被发现并循环就会终止。

定义将继续:

SqlCommand cmd = new SqlCommand(   "select toNode from tblGraph where fromNode=" + u.
nodeID); cmd.Connection = conn; long v;  // ID of a neighbor to u SqlDataReader sdr = cmd.ExecuteReader(); while (sdr.Read() == true) {   v = long.Parse(sdr[0].ToString());   if (beenAdded.ContainsKey(v) == false) {     distance[v] = double.MaxValue;     previous[v] = -1;     PQ.Enqueue(new NodeDistance(v, double.MaxValue));     beenAdded[v] = true;   }

这些代码行到当前节点 u 所有的邻居。 请注意这需要第一个 SQL 数据读取器。 每个邻居节点 v 的检查,以查看它是否在该算法中的节点的首次出现。 如果是这样,以作为其节点 v NodeDistance 项目实例化并添加到优先级队列中。 当他们遇到到优先级队列添加节点,而不是替代设计是最初将在图形中的所有节点都添加到优先级队列。 然而,对于非常大的图这可能需要很大数量的机器内存,需要很长的时间。

内部读取-所有-邻居循环继续:

SqlCommand distCmd =   new SqlCommand("select edgeWeight from tblGraph where fromNode=" +   u.
nodeID + " and toNode=" + v); distCmd.Connection = conn; double d = Convert.ToDouble(distCmd.ExecuteScalar()); alt = distance[u.
nodeID] + d;

这段代码查询数据库以获取距离从当前节点到当前的邻居节点 v 你。 请注意第二次的数据读取器需要这么做。 第二次的数据读取器的存在,就必须对包括数据库可信赖属性和权限属性的 Visual Studio 项目的属性的多个更改。 如果您的最短路径分析使用未加权的图 — — 就是一个地方所有边缘权重被都假定为 1 — — 您可以简化消除第二个读卡器,而代以

alt = distance[u.
nodeID] + 1;

double d = Convert.ToDouble(distCmd.ExecuteScalar()); alt = distance[u.
nodeID] + d;

变量 alt 是从起始节点到当前的邻居节点 v 测试距离。 如果你仔细检查赋值语句,您将看到 alt 是最短的已知的距离从开始节点到节点的 u 再加上从节点的实际距离你到节点 v。 这从开始节点到节点 v 表示潜在的新更短的距离。

内部读取-所有-邻居循环和主要算法循环继续:

if (alt < distance[v])     {       distance[v] = alt;       previous[v] = u.
nodeID;       PQ.ChangePriority(v, alt);     }   }  // sdr Read loop   sdr.Close(); } // Main loop conn.Close();

如果从启动节点到 v 的测试距离小于最短的已知距离从起始节点到 v (存储在字典的距离),然后已发现了一个新的较短距离从一开始的第五,并相应地更新本地数据结构的距离,以前和优先级队列。

存储过程的逻辑现在确定如果主要算法循环终止,因为最短的路径是事实上找到,或终止,因为被发现没有开始节点和结束之间的路径:

pathResult = "NOTREACHABLE"; distResult = -1.0; if (distance.ContainsKey(endNodeAsLong) == true) {   double sp = distance[endNodeAsLong];   if (sp != double.MaxValue) {     pathResult = "";     long currNode = endNodeAsLong;     while (currNode != startNodeAsLong) {       pathResult += currNode.ToString() + ";";       currNode = previous[currNode];     }     pathResult += startNode.ToString();     distResult = sp;

记得真的有实现这种最短路径的两个结果:表示为以分号-最短路径­按相反的顺序,分隔字符串和最短路径的边缘之间的最短路径中的节点权重总和的衡量。 存储的过程使用"NOTREACHABLE"和-1.0 的默认值为结束节点不是从启动节点可到达的情况。 While 循环从以前的字典中提取的最短路径节点和结束要从节点启动节点使用字符串串联在一起缝他们。 如果你雄心勃勃你可以使用堆栈和构造从起始节点到结束节点的结果字符串。 还记得两个返回结果通过参数 pathResult 和 distResult。

存储的过程定义的检查错误的结论:

} // Try   catch(Exception ex)   {     pathResult = ex.Message;     distResult = -1.0;   }   finally   {     if (conn != null && conn.State == ConnectionState.Open)       conn.Close();   } }  // csp_ShortestPath()

如果发生了异常 — — 通常如果 SQL Server 运行内存极大的关系图或由于 SQL 连接超时不足 — — 代码尝试清理的 SQL 连接并返回结果有一定意义。

生成、 部署和调用存储的过程

确保您已经将数据库设置为后值得信赖属性设置为"on"和于外部数据库的权限级别从 Visual Studio 生成的菜单中选择生成 CreateStoredProc。 如果生成成功,请选择部署创建­话务报表从生成菜单来复制 CLR 存储过程从你的开发机器到 SQL 服务器承载基于 SQL 的关系图。

有几种方法来调用 CLR 存储过程。 Visual Studio 项目中包含您可以使用一个测试模板。 中所示,你可以直接从 SSMS 调用的存储的过程或者图 2。 例如,

    declare @startNode bigint declare @endNode bigint declare @maxNodesToCheck int declare @pathResult varchar(4000) declare @distResult float set @startNode = 222 set @endNode = 444 set @maxNodesToCheck = 100000 exec csp_ShortestPath @startNode, @endNode, @maxNodesToCheck,   @pathResult output, @distResult output select @pathResult select @distResult

另一个选项是调用 CLR 存储过程,从 C# 应用程序中使用 ADO.NET,沿的行内图 8

或者,如果你是一个真正的暴食者处罚的可以调用使用 LINQ 技术的 CLR 存储过程。

图 8 调用一个存储的过程从 C# 使用 ADO.NET 内

SqlConnection sc = null; string connString = "Server=" + dbServer + ";Database=" +   database + ";Trusted_Connection=True"; sc = new SqlConnection(connString); SqlCommand cmd = new SqlCommand("csp_ShortestPath", sc); cmd.CommandType = System.Data.CommandType.StoredProcedure;  // sp signature: (System.Data.SqlTypes.SqlInt64 startNode, SqlInt64 endNode,   SqlInt32 maxNodesToCheck, out SqlString path) cmd.CommandTimeout = commandTimeout;  // Seconds SqlParameter sqlStartNode = cmd.Parameters.Add("@startNode",   System.Data.SqlDbType.BigInt); sqlStartNode.Direction = ParameterDirection.Input; sqlStartNode.Value = sn; // ...
cmd.ExecuteNonQuery(); string result = (string)cmd.Parameters["@pathResult"].Value; distResult = (double)cmd.Parameters["@distResult"].Value;

以上只是最短路径

代码和解释这里提出应允许您处理使用 CLR 存储过程的最短路径图分析。 此外,您可能会发现设计范式有用一般。 而不是在数据从读取 SQL 客户端应用程序,然后做过滤和复杂的处理在客户端上,它使用服务器上的 CLR 存储过程提取、 筛选器和进程 — — 然后将结果传输到客户端。 我发现这种方法,是极为有益的几种情况下,与大型数据库和实时性能的要求。

**Dr。**James McCaffrey 为微软在华盛顿州雷德蒙德的研究工作 他曾在几个关键的 Microsoft 产品,包括互联网资源管理器和必应。他从加州大学欧文分校和南加利福尼亚大学的学位。他的个人博客是在 jamesmccaffrey.wordpress.com。他可以在达成 jammc@microsoft.com

衷心感谢以下技术专家对本文的审阅:达伦 Gehring (Microsoft)
达伦 Gehring 是测试经理,在华盛顿州雷德蒙德的微软研究院之前在微软研究院工作,他在 Microsoft SQL Server 产品组工作 10 多年。 在他的网页是 research.microsoft.com/people/darrenge/