前面我们介绍了EF4对数据的增删改查的操作。可以借助于EF4,开发人员的工作量将变得特别简单。
这次我们介绍EF4的数据绑定功能。这次你将会发现EF4的更加简单方面的又一用途:即EF4作为数据源控件的“数据源”。
汗!!好拗口哟。为什么这么说呢?
因为当我们在项目中使用数据源控件时,是需要指出给该数据源控件从哪里取数据的,即指出数据源控件的数据源。以前我们会用Dataset作为数据源控件的数据源。然后,由Dataadapter与数据库交互,把数据填充到Dataset中。再呈现到显示控件上(如datagridview)。这次我们使用EF4的来代替以前Dataadapter与dataset所一起完成的任务。我们已经知道:EF4是可以把数据表的记录映射成实体与实体集合,并且可跟踪管理这些实体。那么我们就可以把这些实体和实体集合作为数据源控件的数据源。这样数据源控件不需要直接和数据库打交道,而是和EF4的实体容器(Container)交互,再由EF4和数据库交互。
======================================================
好了,我们先启动VS2010.
1. File->new-->Windows form project。(名字:EFDataBindingDemo)
2. 在解决方案上,右键--》添加--》新项目--》类库。(名称:EFData)。(我们为体现分层的思想,这次把*.EDMX文件单独建立到一个类库项目中。),
3. 在EFData类库项目上面右键--》添加--》ADO.Net entity data model 。(名称:Northwind)。点击“确定”,如下图:
4.找到服务器上的northwind数据库。如下图:
5.选择三张表:Category,Product,Supplier。如下图:
6.在EFDataBindingDemo项目中添加项目引用:如下图:(同时别忘了把EFData项目中的App.config文件也烤贝到EFDataBindingDemo的项目下面。最简单的方法是你可以直接用鼠标把该文件拖拽到EFDataBindingDemo的项目中)
7.添加.NET类库引用,如下图:
8. 选中EFDataBindingDemo项目,然后打开数据源窗口,如下图:
9.点击数据源窗口上,最左边的的按钮--》添加数据源:如现数据源类型供选择,如下图:
10.点击Next,如下图:如果出现空白,说明你的*.EDMX忘记了编译,
选择“取消”。编译EFData项目,然后重新添加数据源,到这一步。
注意:当你用鼠标选中“EFData”项目时,数据源窗口显示的是“EFData”项目的数据源;只有你选中EFDataBindingDemo项目时,数据源窗口显示的才是EFDataBindingDemo项目的数据源。不要混淆了。
11.重新选中EFDataBindingDemo项目,在数据源窗口--》添加数据源,重复步骤9,如下图:
12.此时数据源窗口会显示添加过的数据源:如下图:
13.拖拽“product”数据源节点,到form1上面,松开鼠标。如下图:(注意观察图中5个标号,这是自动添加)
14.运行程序:如下图:
说明:没有取到数据,很失望吧?呵呵,之前我们用指向dataset的数据源绑定控件时,是主动从数据库往外取,所以是可以加载到数据的。但是现在我们不是直接使用数据库,而是使用EF4(其实就是使用EF4的容器),再有EF4与数据库打交道。因为,我们的所有实体都是放到context容器中的,而现在我们并没有创建这个容器啊,所以加载不到数据(实体)。
15.我们在窗体的Load事件中添加下面的代码:
1 private void Form1_Load(object sender, EventArgs e)
2 {
3 var context = new NorthwindEntities();
4 ObjectResult<Product> products = context.Products.Execute(MergeOption.AppendOnly);
5 // 给数据源控件的数据源属性赋值。
6 this.productBindingSource.DataSource = products;
7 }
运行程序,如下图:(数据是加载了,但是Category,和Supplier两列的数据只有类型,没有实际数据值)
16.在productDataGridView上面右键——》edit columns;添加两列CategoryName和SupplierContactNameName,如下图:(每列的Name和HeaderText设置相同)把新添加的列移到最下端,方便查看。
在productDataGridView控件的RowPrePaint事件中添加如下代码:
1 private void productDataGridView_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
2 {
3 if (e.RowIndex < productBindingSource.Count)
4 {
5 var prod = (Product)productBindingSource[e.RowIndex];
6
7 var grid = productDataGridView;
8 if (prod.Category != null)
9 {
10 grid.Rows[e.RowIndex].Cells[CategoryName.Index].Value = prod.Category.CategoryName;
11 }
12
13 if (prod.Supplier != null)
14 {
15 grid.Rows[e.RowIndex].Cells[SupplierContactName.Index].Value = prod.Supplier.CompanyName;
16 }
17 }
18 }
17.运行代码:如下图:(图中的两个红框分别是:处理前的列和处理后的列)
18. 使用添加、删除、保存功能:
productDataGridView控件的右上方有一个黑色的小小实心三角形,叫“智能标记”。点开智能标记,设置里面的Enable Adding,Enable Editing, Enable Deleting,为选中状态。
窗体上方的导航条,有三个图标:添加,删除,保存。分别用鼠标右键-》Enable 。把它们选为可用。分别为这三个图标添加click事件(在上面双击,即可)
我们把窗体load事件中定义的context移到类中,这样多个方法都可以使用这个context变量。
添加,删除按钮是直接对productBindingSource进行了修改。而productBindingSource会直接影响productDataGridView的变化。这些我们都不必处理。
下面我们处理保存按钮事件:代码如下:
1 private void productBindingNavigatorSaveItem_Click(object sender, EventArgs e)
2 {
3
4 try
5 {
6 this.productBindingSource.EndEdit();
7 context.SaveChanges();
8 MessageBox.Show("save successfuly");
9 }
10 catch (Exception ex)
11 {
12 if (ex.InnerException != null)
13 MessageBox.Show(ex.InnerException.Message.ToString());
14 else
15 MessageBox.Show(ex.Message.ToString());
16 }
17
18 }
运行程序,这时你可以进行添加、修改和删除,然后保存到数据库中。
(注意:
1.在删除Product时,可能会引发异常,因为数据库中有Order_detail表的外键引用了product中的主键。所以,你只能删除那些没有被其它表引用到的product。
2.一般情况下,在界面显示时,我们是不让用看到ID号的,因为这些对用户来说没有意义,所以我们在实际项目中最好把这一ID列(无论是主键列还是外键列)全部隐藏掉。看清是隐藏掉,不是删除掉,因为我们在程序中是要使用这些ID号进行CRUD的操作的。
3. 在处理更新的某一个单元格的数据的时候。我遇到了困惑:以前使用dataset作数据绑定时,是直接可以把对单元格的修改反馈到数据库中的,而这次我使用EF时,却不能把对单元格的修改反馈到数据库了,即使我调用了context.savechanges()方法。后来我找了许多资料,也没能解决问题。在我一次偶然的测试中,我一次性修改了多个单元格,然后保存。这次竟然保存成功了。感觉是因为我对单元格作的修改量太小了,context并没有把它们及时更新入数据库中,而是对这些进行了本地缓存的原因。但我只是猜测,并不确定。希望高人给我指点一下。、谢谢。)
可以看到,我们可以非常轻松地处理这些数据了。是不是感觉世界又美好了许多。哈哈。
窗体后台完整代码如下:
1 public partial class Form1 : Form
2 {
3 NorthwindEntities context;
4
5 public Form1()
6 {
7 InitializeComponent();
8 }
9
10 private void Form1_Load(object sender, EventArgs e)
11 {
12 context = new NorthwindEntities();
13 ObjectResult<Product> products = context.Products.Execute(MergeOption.OverwriteChanges);
14 // 给数据源控件的数据源属性赋值。
15 this.productBindingSource.DataSource = products;
16
17 }
18
19 private void productDataGridView_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
20 {
21 if (e.RowIndex < productBindingSource.Count)
22 {
23 var prod = (Product)productBindingSource[e.RowIndex];
24
25 var grid = productDataGridView;
26 if (prod.Category != null)
27 {
28 grid.Rows[e.RowIndex].Cells[CategoryName.Index].Value = prod.Category.CategoryName;
29 }
30
31 if (prod.Supplier != null)
32 {
33 grid.Rows[e.RowIndex].Cells[SupplierContactName.Index].Value = prod.Supplier.CompanyName;
34 }
35 }
36 }
37
38
39
40 private void productBindingNavigatorSaveItem_Click(object sender, EventArgs e)
41 {
42
43 try
44 {
45 this.productBindingSource.EndEdit();
46 context.SaveChanges();
47 MessageBox.Show("save successfuly");
48 }
49 catch (Exception ex)
50 {
51 if (ex.InnerException != null)
52 MessageBox.Show(ex.InnerException.Message.ToString());
53 else
54 MessageBox.Show(ex.Message.ToString());
55 }
56
57 }
58
59
60 }