11.6.1 概述
ASP.NET 2.0的Web部件框架构的实现已经支持Web部件之间的连接。一个连接包含两个Web部件;:一个是提供者,负责向其他Web部件发布数据,;另一个是消费者,负责检索、和使用数据。听起来虽然简单,但是这种性能支持Web部件对的开发,Web部件对同时使用时可提供强壮的解决方案。假设一个灵活的报告报表解决方案需要用到Web部件。一个一种方法是创建一个显示Web部件,用以显示可用的预生成报告报表的列表。选择列表中的一个报告报表时,另一个Web部件就会在页面的其他地方显示该报表告。另一个另一种方法是创建一个Web部件,该Web部件允许用户从后端企业系统中选择和过滤数据。数据被过滤和管理时,连接的Web部件就会将数据作为图片呈现,向用户提供包含原始数据和形象化可视化数据的视图。
11.6.2 Web部件连接的类型
在WSS v3中,连接有两种类型:静态和动态。在WSS v3中,当连接中的一个或两个Web部件不驻在Web部件区域内时,可用静态连接建立Web部件间的连接。但是,在ASP.NET 2.0中,静态连接只是ASPX页面标记中定义声明的那部分。静态连接的局限性在于它不能通过Web浏览器接口来建立。相反,静态连接必须通过使用SPWebPartManager.AddConnectin方法的对象模型或使用微软的SPD建立。
动态连接要求连接内的两个Web部件都驻入位于Web部件区域内。这是最为通用的连接类型,可通过Web浏览器接口建立。
11.6.3 Web部件连接的实现——连接点
ASP.NET 2.0连接是根据“拉”模型建立的,而不是根据“推”模型或订阅模型。如果两个Web部件间事先建立了连接,那么消费者部件装载时,会向连已连接的提供者部件请求数据。要使Web部件间能互相通信,它们必须使用同一种语言。这通过实现同一接口的提供对象和消费对象来实现。要使两个Web部件参与到一个连接中,则提供者和消费者都必须至少有一个连接点。连接点包含连接到其他Web部件所必需的所有消息,包括控件ID、控件可识别的数据类型等。给向Web部件添加连接点时,只需用ConnectionProvider属性修改提供者的特定方法、用ConnectionConsumer属性修改消费者的特定方法即可。用ConnectionProvider属性修改的方法必须返回一个接口,用ConnectionConsumer属性修改的方法必须接收该接口的一个参数。用必要的属性完成实现和修改后,Web部件框架酒会构就会在连接的通信过程中使用已注册的callback回调方法。下面的代码片断片段给出了ConnectionProvider属性和ConnectionConsumer属性的使用方法:
[ConnectionProvider("Connection Provider Name")]
public Ifoo GetProviderInterface()
{
return foo;
}
ConnetionConsumer attribute:
[connectionConsumer("Connection Consumer Name")]
public void SetConsumerInterface(Ifoo foo)
{}
初始化连接过程时,Web部件框架构会调用提供者部件上的已注册callback的回调方法,在上述代码中是GetProviderInterface。该方法返回IFoo类型的对象。框架构会获取该对象,并调用消费者部件上的已注册callback的回调方法(本例中是指SetConsumerInterface),以传递提供者的callback回调方法返回的对象。
注意,ConnectionProvider和ConnectionComsumerConnectionConsumer的构造函数都只有一个参数。这是连接的显示名称,也是抽象类ConnectionPoint中定义的唯一必需的属性。其他要注意的是抽象类ConnectionPoint中的一些可用属性,如表11-3中所示。
表11-3 ConnectionPoint的属性
属 性 名 |
描 述 |
ControlType |
返回与连接点关联的服务器控件类型。一般为WebPart类型 |
DisplayName |
将Web部件的名称作为字符串返回。创建连接点时,用户界面中要用到该字符串 |
ID |
识别连接点的唯一字符串。如果在Web部件中只指定一个连接点,那么Web部件 |
InterfaceType |
返回连接点所使用的接口类型 |
WSS v2开发者可能会记得ICellConsumer/ICellProvider和IRowConsumer/IRowProvider接口。目前,已使用Obsolete属性修改了这两个方法,而且WSS v3也不提倡该这两个接口的使用;。因此,可以使用前面提到的两个接口创建自定义的接口,如果可能的话,也可以使用.NET Web部件框架构包含的接口:IWebPartField、IWebPartRow和IWebPartTable,以在接口未知的Web部件间传递数据。
创建两个对接的Web部件
要了解对接的Web部件的工作方式,就要创建参与同一连接的提供者部件和消费者部件。在这个例子中,创建了一个总统提供者President Provider部件,它包含一个列表,在该列表中,用户可选择前五任美国总统中的一个。当用户选择了一个总统时,页面就会刷新,President Provider总统消费者部件就会显示所选总统的全名。
1. 创建Web部件连接所使用的接口
首先,创建一个提供者和消费者都能识别的接口。
(1) 创建VS.NET类库“Web Part Techniques.Second”。
(2) 在VS.NET中创建一个新接口IPresident.cs,其代码如代码清单11-19所示。:
代码清单11-19 接口IPresident.cs
using System;
namespace WebPartTechniques.Second
{
public interface IPresident
{
int PresidentNumber { get;}
}
}
2. 创建对接的提供者部件
两个Web部件都要用到接口IPresident.cs。创建提供者部件,一旦建立连接,该部件就向消费者部件提供数据。
(31) 在VS.NET中创建一个新类PresidentProvider.cs,其代码如代码清单11-20所示(注意实现IPresident接口的Web部件):
代码清单11-20 PresidentProviderPart类
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
namespace WebPartTechniques.Second
{
public class PresidentProviderPart :
System.Web.UI.WebControls.WebParts.WebPart,
ProviderWebParts.IPresident
{
DropDownList presidentPicker = null;
}
}
(42) 在PresidentProvider类中添加私有的下拉列表。
(53) 添加代码清单11-21中的代码,以重载重写CreateChildControls方法,该方法将初始化Web部件,并在Web部件中添加按钮和总统选项选择器:
代码清单11-21 PresidentProviderPart的CreateChildControls方法
protected override void CreateChildControls()
{
base.CreateChildControls();
try
{
//Instantiate, Initialize,
// wIreup (in this case, require a post back on change)
// and Insert the control into the Web Part class
//Note: The ListItem value is provided to the consumer of
// the Web Part connection
presidentPicker = new DropDownList();
presidentPicker.Items.Add(new ListItem("Pick a President", "0"));
presidentPicker.Items.Add(new ListItem("Washington", "1"));
presidentPicker.Items.Add(new ListItem("Adams", "2"));
presidentPicker.Items.Add(new ListItem("Jefferson", "3"));
presidentPicker.Items.Add(new ListItem("Madison", "4"));
presidentPicker.Items.Add(new ListItem("Monroe", "5"));
presidentPicker.AutoPostBack = true;
this.Controls.Add(presidentPicker);
}
catch (Exception ex)
{
this.Controls.Clear();
Literal msg = new Literal();
msg.Text = ex.Message;
this.Controls.Add(msg);
}
}
实现PresidentProvider类中IPresident的必要部分。在这个示例子中,添加一个只读属性。
(64) 在PresidentProvider类中添加代码清单11-22中的只读属性:
代码清单11-22 PresidentNumber属性
public int PresidentNumber
{
get { return int.Parse(presidentPicker.SelectedValue); }
}
创建提供者Web部件的最后一步是创建提供者连接点。为此,要创建返回IPresident接口的实现实例,并用ConnectionProviderAttribute修改该方法。
(75) 添加代码清单11-23中的代码,使得Web部件作为提供者参与连接:
代码清单11-23 提供者连接点
[System.Web.UI.WebControls.WebParts.ConnectionProvider(
"President Number")]
public IPresident GetPresidentProvider()
{
return this;
}
经过Web部件签名、并创建.webpart文件后,该Web部件的创建就完成了。只要将总统提供者President Provider部件部署到WSS v3站点,就可以将其添加到Web页面上的任意Web部件区域了。该Web部件的视图如图11-11所示。
图11-11 President Provider总统提供者部件
3. 创建对接的消费者部件
现在来创建消费者Web部件:。
(1) 用代码清单11-24中的代码在VS.NET中创建新的公共类PresidentConsumer.cs。
代码清单11-24 PresidentConsumerPart类
using System.Web.UI.WebControls;
namespace WebPartTechniques.Second
{
public class PresidentConsumerPart :
System.Web.UI.WebControls.WebParts.WebPart,
ProviderWebParts.IPresident
{
int m_selectedPresidentIndex = 0;
ProviderWebParts.IPresident m_presidentProvider = null;
Label lbl = null;
}
}
(2) 在PresidentConsumer类中添加一些私有字段:
在添加Web部件特定的代码之前,要创建一个帮助方法(helper method),以根据President Provider总统提供者部件传递的编号来确定总统。这可以使用简单的switch语句实现。
(3) 将代码清单11-25中的代码添加到PresidentConsumer类中:
代码清单11-25 PresidentConsumerPart的GetPresidentByIndex方法
private string GetPresidentByIndex(int index)
{
switch (index)
{
case 1:
return "George Washington";
case 2:
return "John Adams";
case 3:
return "Thomas Jefferson";
case 4:
return "James Madison";
case 5:
return "James Monroe";
default:
return "No president selected";
}
}
(4) 将代码清单11-26中的代码添加到PresidentConsumerPart类中,以重载重写原有的RenderContents方法:
代码清单11-26 PresidentConsumerPart的RenderContents方法
protected override void CreateChildControls()
{
base.CreateChildControls();
try
{
lbl = new Label();
lbl.Text = "No president selected";
this.Controls.Add(lbl);
}
catch (Exception ex)
{
this.Controls.Clear();
Literal msg = new Literal();
msg.Text = ex.Message;
this.Controls.Add(msg);
}
}
protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
{
//This must be done late in the Page Life Cycle
if (this.m_selectedPresidentIndex > 0)
{
lbl.Text = "President #" +
this.m_selectedPresidentIndex +
": " +
GetPresidentByIndex(this.m_selectedPresidentIndex) +
" was selected.";
}
base.RenderContents(writer);
}
重载重写连接建立后要调用的OnPreRender方法。该方法会通过PresidentNumber属性(定义于IPresident接口,实现于President Provider总统提供者部件)检索所选的总统编号。如果连接没有建立,就不会执行任何操作。
(5) 将代码清单11-27中的代码添加到PresidentConsumer类中。
代码清单11-27 PresidentConsumerPart的OnPreRender重载重写方法
protected override void OnPreRender(EventArgs e)
{
if (this.m_presidentProvider != null)
{
this.m_selectedPresidentIndex =
this.m_presidentProvider.PresidentNumber;
}
}
(6) 将代码清单11-28中的代码添加到PresidentConsumer类中,使Web部件作为消费者参与连接:
代码清单11-28 PresidentConsumerPart类的消费者连接点
[System.Web.UI.WebControls.WebParts.ConnectionConsumer(
"President Number")]
public void GetConnectedProviderInterface(
ProviderWebParts.IPresident connectionProvider)
{
m_presidentProvider = connectionProvider;
}
保存更改,构建编译项目。完成部署Web部件的其他必要任务,并将其添加到WSS v3站点,之后,将该Web部件添加到同一页面(即添加President Provider总统提供者部件的页面)的任意Web部件区域。该Web部件的视图如图11-12所示。
图11-12 President Consumer总统消费者部件
4. 连接提供者部件和消费者部件
将President Provider总统提供者部件和消费者President Consumer部件添加到Web页面后,所剩的工作就是连接这两个部件。选择【Site Actions】菜单中的【Edit Page】选项。单击标题栏的编辑菜单或Web部件时,【Connections】菜单项就会发亮高亮显示。提供者部件【Connections】菜单的子菜单项(如图11-13所示)和消费者部件【Connections】菜单的子菜单项(如图11-14所示)有点不同。提供者使用“Send…To”方式,而消费者使用“Get..From”方式。
图11-13 President Provider总统提供者部件的【Connections】菜单
图11-14 President Consumer总统消费者部件的【Connections】菜单
单击Web部件标题栏的【Edit】菜单就可建立两个Web部件间的连接。单击【Site Actions】菜单下方的【Exit Edit Mode】。从President Provider总统提供者部件的选择列表中选择一个编号,并单击【Show president】按钮。
页面会自动刷新,并在President Display Consumer总统显示消费者部件中显示所选总统的编号和姓名,如图11-15所示。
图11-15 证实Web部件连接的President Provider总统提供者部件和President Consumer总统消费者部件
如实例所示,除了要构建一个典型的Web部件外,不需要大量的编码工作,就可添加提供者和消费者连接能力,创建一个Web部件连接。
11.6.4 Web部件连接的实现——转换器
提供者部件和消费者部件是否必须返回和识别同一个接口类型呢?如果两个Web部件不能识别相同的接口,它们就不兼容,也就不能实现连接。但这并不是尽头;,Web部件框架构包含了一个机制,该机制支持用户连接识别不同接口的两个Web部件。要建立这种Web部件间的连接,就必须实现一个自定义的转换器,以将提供者部件的数据转换成消费者部件可接收的形式。ASP.NET 2.0包含了两个转换器:RowToFieldTransformer和RowToParameterTransformer,它们是用接口IWebPartField和IWebPartRow实现的。如果Web部件使用的是自定义接口,则只需继承基类WebPartTransformer并实现Transform方法,就可创建一个自定义转换器。Transform方法只接收一个对象作为参数(不管提供者部件连接点返回什么),并返回调用消费者部件连接点所需的对象。
11.6.5 ASP.NET 2.0和WSS v3的Web部件连接之间的区别
ASP.NET团队采用了修正的Web部件框架构,该框架构最早在WSS v2中引入,WSS的最新版本WSS v3则建立在ASP.NET 2.0的基础上,继承了新的实现。虽然WSS v3融合了.NET Web部件的实现,但两个环境还是存在些许差异。推荐.NET Web部件在WSS v3中使用.NET Web部件,因为这样可以通过继承WSS v3的WebPart类(取代ASP.NET 2.0的WebPart类),来克服.NET Web部件的一些局限性。
Web部件的ASP.NET 2.0实现引入了与单个Web部件相关联的消费者连接数量的限制。在WSS v2中,Web部件有无数的提供者和消费者连接。然而在ASP.NET 2.0中,虽然Web部件仍然有无数的提供者连接,但却只由一个消费者连接。如果需要无数的消费者连接,Web部件就要继承WSS v3的WebPart类,并设置UnlimitedConnections属性。这借鉴了WSS v2的连接行为。
两个环境的另一个区别是,ASP.NET 2.0不支持不同页面上Web部件间的连接。跨页连接是WSS v2的特性之一,但.NET Web部件的实现中没有采用该特性。但是然而,出于向后兼容性的原因,WSS v3的WebPart类支持不同页面上Web部件间的连接。
WSS v3 Web部件实现和WSS v2实现的最大改变在于安全策略。在WSS v2中,连接两个Web部件时,WSS站点要以CAS设置方式运行Web应用程序(该Web应用程序必须定义了WebPartPermission,而且要将Connections属性的值设为true)。虽然WSS v3仍然支持这种方式,但在WSS站点的CAS策略中已没必要包含该方法。原因很简单:底层体系结构发生了变化。在.NET Web部件实现中,连接绑定接口归WebPartManager(或WSS v3中的SPWebPartManager)所有。WebPartManager不需要为每个ASP.NET 2.0站点在CAS策略文件中定义这种许可。因此,由于WSS v3不再拥有连接绑定接口,也是通过ASP.NET 2.0的 WebPartManager来控制,所以,建立或处理连接时,就不能声明该许可。和与WSS v2相比看起来,WSS v3的站点主人主持者可能会丧失一些控制权,但仍然保留了其他的控制机制,作为站点管理员,仍能通过虚拟的服务器级设置来阻止新的Web部件连接的创建。