Web Client Software Factory系列(4):数据绑定和ObjectContainerDataSource控件

概述

Web Client Software Factory系列(3):View-Presenter模式中提到,表示器包含了响应用户事件逻辑以及一些View的状态等,在Web Client Software Factory中包含了一个名为ObjectContainerDataSource的数据源控件,它为View-Presenter模式和数据绑定之间提供了桥梁,ObjectContainerDataSource可以简单的理解为用来包行对象的容器。它的处理过程如下图所示:

TerryLee_WCSF16

下面我们将通过一个完成的实力来演示如何使用View-Presenter模式和ObjectContainerDataSource控件进行数据绑定。按照上一篇所讲的,我们先在Product业务模块下添加一个NewProduct视图。

在视图中添加ObjectContainerDataSource控件

之前请先在工具箱中添加ObjectContainerDataSource控件,位于Microsoft.Practices.Web.UI.WebControls.dll下,拖拽ObjectContainerDataSource控件到NewProduct.aspx页面上。

TerryLee_WCSF17

接下来要做的就是配置ObjectContainerDataSource了,其实要配置也就是DataObjectTypeName属性而已,即ObjectContainerDataSource控件要包含的对象的类型。选择之前我们编写Product实体类,配置完成后ASPX中代码如下:

<asp:Content ID="content1" ContentPlaceHolderID="DefaultContent" Runat="Server">
<h1>NewProduct</h1>
<pp:ObjectContainerDataSource ID="ObjectContainerDataSource2" runat="server"
DataObjectTypeName="WebClientDemo1.Products.ModuleEntities.Product" />
</asp:Content>

这里配置DataObjectTypeName属性时有两点需要注意:

1.如果实现Insert、Update、Delete操作,所配置的DataObjectTypeName参数所对应的类型要有一个无参的构造函数;

2.如果实现Update、Delete操作,所配置的DataObjectTypeName参数对应的类型要有一个属性能够唯一表示该类型的一个实例,其实就是对应数据库中的主键,也支持联合主键,这个不难理解,想想SQL语句就知道了。

添加DetailsView、GridView绑定到ObjectContainerDataSource

在NewProduct.aspx页面上添加DetailsView、GridView控件,分布指定它们的DataSourceID为ObjectContainerDataSource1,如下图所示:

TerryLee_WCSF18

设置完成后的代码如下:

<asp:Content ID="content" ContentPlaceHolderID="DefaultContent" Runat="Server">
<h1>NewProduct</h1>
<hr/>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="Id" DataSourceID="ObjectContainerDataSource1" Width="400px">
<Columns>
<asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
<asp:BoundField DataField="Brand" HeaderText="Brand" SortExpression="Brand" />
<asp:CommandField ShowCancelButton="False" ShowDeleteButton="True" />
</Columns>
</asp:GridView>
<hr/>
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataSourceID="ObjectContainerDataSource1"
DefaultMode="Insert" Height="50px" Width="400px" DataKeyNames="Id">
<Fields>
<asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
<asp:BoundField DataField="Brand" HeaderText="Brand" SortExpression="Brand" />
<asp:CommandField ButtonType="Button" InsertText=" Add  " ShowCancelButton="False"
ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
<pp:ObjectContainerDataSource ID="ObjectContainerDataSource1" runat="server"
DataObjectTypeName="WebClientDemo1.Products.ModuleEntities.Product" />
</asp:Content>

实现数据的Insert、Update、Delete

为了实现的数据的增删改,我们需要修改在Web Client Software Factory系列(2):Composite Web应用程序块写过的Service,这里为了演示数据就不从数据库中读取了,而是保存在Session中。

IProductDataService接口:

public interface IProductDataService
{
Product GetProductById(string id);
List<Product> Products { get;set;}
void InsertProduct(Product product);
void UpdateProduct(Product product);
void DeleteProduct(Product product);
}
Service实现:
public class ProductDataService : IProductDataService
{
private List<Product> _products;
public List<Product> Products
{
get
{
_products = HttpContext.Current.Session["products"] as List<Product>;
if (_products == null)
{
_products = new List<Product>();
}
return _products;
}
set
{
HttpContext.Current.Session["products"] = value;
}
}
public void InsertProduct(Product product)
{
product.Id = Guid.NewGuid().ToString();
_products = HttpContext.Current.Session["products"] as List<Product>;
if (_products == null)
{
_products = new List<Product>();
}
_products.Add(product);
HttpContext.Current.Session["products"] = _products;
}
public void UpdateProduct(Product product)
{
Product result = FindProduct(Products, product);
if (result != null)
{
result.Name = product.Name;
result.Brand = product.Brand;
}
}
public void DeleteProduct(Product product)
{
Product result = FindProduct(Products, product);
if (result != null)
{
Products.Remove(result);
}
}
private static Product FindProduct(List<Product> products, Product product)
{
return products.Find(delegate(Product match)
{
return product.Id == match.Id;
});
}
}

打开视图INewProduct,编写如下代码

public interface INewProduct
{
List<Product> Products { set;}
}
并在NewProduct.aspx.cs中实现View接口
public List<Product> Products
{
set
{
this.ObjectContainerDataSource1.DataSource = value;
}
}
接下来我们要做的就是实现Controller,在Web Client Software Factory系列(2):Composite Web应用程序块中注册和使用服务一节中已经讲过了,直接给出代码:
public class ProductsController
{
public ProductsController()
{
}
private IProductDataService _productDataService;
[ServiceDependency]
public IProductDataService ProductDataService
{
set { _productDataService = value; }
}
public List<Product> Products
{
get
{
return _productDataService.Products;
}
set
{
_productDataService.Products = value;
}
}
public void InsertProduct(Product product)
{
_productDataService.InsertProduct(product);
}
public void UpdateProduct(Product product)
{
_productDataService.UpdateProduct(product);
}
public void DeleteProduct(Product product)
{
_productDataService.DeleteProduct(product);
}
}
实现我们的Presenter,具体的在Web Client Software Factory系列(3):视图、表示器和控制器中已经讲过
public class NewProductPresenter : Presenter<INewProduct>
{
private ProductsController _controller;
public NewProductPresenter([CreateNew] ProductsController controller)
{
_controller = controller;
}
public override void OnViewLoaded()
{
View.Products = _controller.Products;
}
public override void OnViewInitialized()
{
}
public void OnProductInserted(Product product)
{
_controller.InsertProduct(product);
}
public void OnProductUpdated(Product product)
{
_controller.UpdateProduct(product);
}
public void OnProductDeleted(Product product)
{
_controller.DeleteProduct(product);
}
}

最后为ObjectContainerDataSource控件添加相关的事件,并具体的操作交给Presenter:

protected void ObjectContainerDataSource1_Inserted(object sender, ObjectContainerDataSourceStatusEventArgs e)
{
_presenter.OnProductInserted((Product)e.Instance);
}
protected void ObjectContainerDataSource1_Updated(object sender, ObjectContainerDataSourceStatusEventArgs e)
{
_presenter.OnProductUpdated((Product)e.Instance);
}
protected void ObjectContainerDataSource1_Deleted(object sender, ObjectContainerDataSourceStatusEventArgs e)
{
_presenter.OnProductDeleted((Product)e.Instance);
}

这里需要说明的是ObjectContainerDataSourceStatusEventArgs有两个特别重要的属性是Instance和AffectedRows,ObjectContainerDataSource控件通过反射创建它所包含对象的类型的实力Instance,AffectedRows是受影响的行数。

运行后如下:

TerryLee_WCSF19

分页和排序

如果使用ObjectContainerDataSource默认的分页和排序功能,需要设置如下两个属性:

UsingServerPaging="True"
UsingServerSorting="True" 

并编写Selecting事件:

protected void CustomersDataSource_Selecting(object sender, ObjectContainerDataSourceSelectingEventArgs e)
{
_presenter.OnSelecting(e.Arguments.StartRowIndex, e.Arguments.MaximumRows, e.Arguments.SortExpression);
}

剩下的就到Presenter中处理分页了:)

结束语

关于Web Client Software Factory中使用View-Presenter模式进行数据绑定和ObjectContainerDataSource控件就到这里了,希望对您有所帮助。
示例代码下载:/Files/Terrylee/WebClientDemo2.rar

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2007-11-28 20:45 TerryLee 阅读(4031) 评论(24)  编辑 收藏 网摘 所属分类: [10]  模式与实践

  回复  引用  查看    
#1楼 2007-11-28 21:13 | 暗香浮动      
佩服哇
动作太快了。
  回复  引用  查看    
#2楼 2007-11-28 21:26 | 坐断东南 笑煞之!!      
高效优质。。
  回复  引用  查看    
#3楼 [楼主]2007-11-28 21:30 | TerryLee      
@坐断东南 笑煞之!!
@暗香浮动
呵呵,写完这篇还不知道下篇写什么呢:)
  回复  引用  查看    
#4楼 2007-11-28 21:35 | 紫色阴影      
lz真是快啊 WCSF的确还不错
  回复  引用  查看    
#5楼 2007-11-28 22:45 | Jeffrey Zhao      
你对于MVP是什么感觉呢?这个例子比较简单,所以似乎看起来比较好。
对于逻辑复杂的页面,我感觉就复杂了,View和Presenter都需要释放出很多成员。
而且这里的Dependency Injection感觉没有什么必要——有多少人需要这样呢?
  回复  引用  查看    
#6楼 [楼主]2007-11-28 23:28 | TerryLee      
@紫色阴影
:)
  回复  引用  查看    
#7楼 [楼主]2007-11-28 23:33 | TerryLee      
@Jeffrey Zhao
这个例子的业务逻辑还是相当简单的,使的MVP看起来很优美

如果在业务逻辑比较复杂的时候,要对UI进行Unit Test的话,我觉的还是要用的,否则就不一定会用它了
  回复  引用  查看    
#8楼 2007-11-29 09:22 | 暗香浮动      
Enterprise Library 3.0
这个系列倒是一个不错的选择。呵呵。
  回复  引用  查看    
#9楼 2007-11-29 09:40 | 老夫子系      
WCSF最大的优点之一就是让大项目可以分工很明确,一般都是让新手去编写页面代码,而不希望涉及业务逻辑,MVP就能很好解决这个问题。
其次,页面逻辑放在Presenter,并且页面都是实现相关接口的,从而很方便的实现单元测试,保证Presenter代码质量。
分工明细,才能责任明确。但是定义合理的IView接口和实现Presenter的代码,确实会存在一定的风险,毕竟我们已经习惯了把页面逻辑写在后台代码中。
所以这部分编码要分配给懂得MVP思想的高级程序员去做。

  回复  引用  查看    
#10楼 2007-11-29 09:42 | 老夫子系      
建议LZ讲些PageFlow,
  回复  引用  查看    
#11楼 [楼主]2007-11-29 10:17 | TerryLee      
@暗香浮动
EL实际用起来就不是那么回事了,权限程序块太简单,不能满足实际的需求,而日志程序块又太复杂,很难跟实际结合起来

最近在公司自己做了一套基础框架,比较简单,但是基本能满足公司开发的需求了:)
  回复  引用  查看    
#12楼 [楼主]2007-11-29 10:19 | TerryLee      
@老夫子系
说的不错,如果团队里面的开发人员不懂MVP的思想,实施MVP估计有风险

不过就像你说的,如果实现UI单元测试,MVP倒是非常不错的
  回复  引用  查看    
#13楼 [楼主]2007-11-29 10:21 | TerryLee      
@老夫子系
等把这部分写完了,再写一下PageFlow
  回复  引用  查看    
#14楼 2007-11-29 10:46 | Enzo      
@TerryLee
想的真周到 呵呵
  回复  引用  查看    
#15楼 2007-11-29 12:31 | 高海东      
模式与实践小组提供的软件工厂有:

Web Client Software Factory

Web Service Software Factory

Smart Client Software Factory

Mobile Client Software Factory

……
希望楼主能把这些都讲下,非常关注
  回复  引用  查看    
#16楼 [楼主]2007-11-29 20:09 | TerryLee      
@ Enzo
:)
  回复  引用  查看    
#17楼 [楼主]2007-11-29 20:10 | TerryLee      
@高海东
不是吧?

Mobile的东西从来没关注过,也不会
  回复  引用  查看    
#18楼 2007-11-29 20:37 | 高海东      
Web Client Software Factory

Web Service Software Factory

Smart Client Software Factory 会写吗
  回复  引用  查看    
#19楼 [楼主]2007-11-29 21:14 | TerryLee      
@高海东
WCSF不是正在写吗?-_-
  回复  引用  查看    
#20楼 2007-12-10 22:04 | 菌哥      
等待terry的下文...
下篇的时间也太长了吧):
  回复  引用  查看    
#21楼 [楼主]2007-12-11 19:21 | TerryLee      
@菌哥
下篇再等等吧,最近要写的东西比较多啊:)
  回复  引用    
#22楼 2008-07-09 05:20 | JohnnyNA [未注册用户]
Can you please tell how to dynamically set site map information based on user's rule in ModuleInitializer? Thanks!

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-10-28 12:17 编辑过
Google站内搜索



相关文章:

相关链接: