65.9K
CodeProject 正在变化。 阅读更多。
Home

一个用于显示、编辑和自动更新多对多关系的基类 Windows 窗体 - 第一部分

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.92/5 (6投票s)

2005年9月29日

5分钟阅读

viewsIcon

43965

downloadIcon

848

一个用于显示、编辑和自动更新多对多关系的基类 Windows 窗体。

引言

为了感谢我在 .NET 使用过程中所学习到的所有来自互联网的人,我希望能提供这个窗体,用于显示、编辑和自动更新数据库(Access)中的多对多关系。

我试图使其尽可能通用,以便其他人可以轻松地根据自己的需求进行改编。

这个窗体的第一个版本是使用 .NET 1.0 和 1.1 编写的,我遇到了许多问题。特别是,我希望窗体能够自动更新底层数据库,而无需按“提交”按钮,这对我来说似乎是倒退。在编写了使用 VBA for Access 的一些程序后,我体会到数据是立即更新的。[然而,我也理解 .NET 理念中的断开连接性质,并认识到应该有一种方式可以不立即提交更改。]

考虑到这一点,我想编写使用事件来按需触发更新的应用程序。 .NET 1.1 的 DataGrid 的原始结构设计似乎不足以完成这项任务。我不得不寻找变通方法,并深入研究货币管理器和其他一些我不太想涉及的复杂结构,而且我仍然觉得我原始代码中存在 bug(用于一个面向学校的商业在线评估系统)。

随着 .NET 2.0 和 DataGridView 的出现,我想知道设计是否有所增强,所以我决定重写我的代码。

我已经从我的新应用程序中剥离了所有不必要的代码和结构,这里是一个更纯粹的版本。

老实说,最初的代码是在大量试验、阅读海量文档、许多深夜和汗水之后创建的——我现在已经记不清为什么当时要做某些事情的细节了!我不再是专业程序员了,所以我不每天都做这些事,我会忘记。

我希望同事们能够优化代码并修正任何不当的使用,以便这个基础代码能对大家非常有益。

多对多关系是很常见的。在学校环境中,我们有许多老师和许多学生。学生可以属于许多老师所教的课程,而老师有许多学生班级。

所以 Moore 先生的班级可能有 John、Fred、Julie、Usha。Smith 女士的班级可能有 Fred、Usha 和 Ellen。

所以 Fred 和 Usha 都在 Moore 先生和 Smith 女士的班级里,而 John 只在 Moore 先生的班级里,Ellen 只在 Smith 女士的班级里。

为了在关系数据库中映射这一点,我们通常使用一个中间映射表。

Table A

AID    AName

1    Mr Moore
2    Ms Smith
3
4
...

Table B

BID    BName

1    John
2    Fred
3    Julie
4    Usha
5    Ellen
6
...

Table AB

ABID AID  BID
1    1    1
2    1    2
3    1    3
4    1    4
5    2    2
6    2    4
7    2    5
...

AID 是表 A 的唯一 ID,BID 是表 B 的唯一 ID。ABID 是映射表的唯一标识符。[尽管我不确定它是否严格必要。]

表 AB 提供了表 A 和表 B 之间数据的多对多映射。我们可以从两个方向查看这些关系。

在实现方面,我使用了一个 MS Access 数据库。它为唯一标识符提供了自动编号功能,但在与 .NET 结合使用时存在一些棘手的问题——尤其是并发冲突问题,自动编号字段可能会与 DataSet 的编号不同步。虽然我的原始版本(勉强)使用了自动编号,但我决定,因为我可能想将我的设计移植到 SQL Server 或其他数据库,所以我最好不假设存在自动编号功能,而且这样实现就不会局限于 Access。

所以,虽然 Access 数据库表有自动编号 ID 列,并且我允许它们生成,但我并不使用它们。取而代之的是,应用程序将生成并管理它们。[我不太确定这在多用户环境中的影响。我认为可能需要更强大的例程来避免并发冲突。这留给其他人去解决。]

代码的第一部分只是用于显示数据的简单内容。下一部分将添加编辑和数据库更新例程。

我加入了手动显示 DataGridView 列的代码。

我确信可以通过设计器手动完成所有这些工作,但我不知道如何绑定到手动编码的 DataSet。对于简单的表来说很容易,但这并不是一个简单的 DataSet。如果有人知道如何做到,请告诉我,我会修改代码。

以下是代码的要点。首先,我们获取连接,并从 Access 数据库 AoB.mdb 加载数据到 DataSet

using Data.OleDB

string myDB = "AorB.mdb"; //or whatever your database is called
OleDbConnection conn = null;
DataSet ds;
OleDbDataAdapter daA;
OleDbDataAdapter daB;
OleDbDataAdapter daAB;
OleDbDataAdapter daBA;

DataRelation drAB;
DataRelation drBA;

public void InitialCode() //called from FormLoad or elsewhere.
{

    //create a connection string to the access database
    conn = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;
    User Id=;Password=;
    Data Source=" + myDB);

    LoadDatabase();

}

public void LoadDatabase()
{

    //clear the dataset
    ds = null;
    ds = new DataSet();

    //set up the link
    daA = new OleDbDataAdapter("SELECT * FROM A ORDER BY AName", conn);
    daB = new OleDbDataAdapter("SELECT * FROM B ORDER BY BName", conn);

    //fill the dataset with data
    daA.Fill(ds, "A");
    daB.Fill(ds, "B");

    // Create Data Relation from A to B, ForeignKey constraint
    // auto created apparently - a bit complex!

    daAB = new OleDbDataAdapter("SELECT AB.ABID, AB.AID, AB.BID," + 
                                " B.BName FROM B INNER JOIN AB ON" + 
                                " B.BID = AB.BID ORDER BY B.BName", conn);
    daAB.Fill(ds, "ABx");

    drAB = new DataRelation("AToB",
    ds.Tables["A"].Columns["AID"],
    ds.Tables["ABx"].Columns["AID"], true);
    ds.Relations.Add(drAB);

    daBA = new OleDbDataAdapter("SELECT AB.ABID, AB.BID, AB.AID," + 
                                " A.AName FROM A INNER JOIN AB ON" + 
                                " A.AID = AB.AID ORDER BY A.AName", conn);
    daBA.Fill(ds, "BAx");

    drBA = new DataRelation("BToA",
    ds.Tables["B"].Columns["BID"],
    ds.Tables["BAx"].Columns["BID"], true);
    ds.Relations.Add(drBA);

    //enforce cascade contraints, auto added, if need change 
    //then look up ForeignKeyContraints.DeleteRule
    ds.EnforceConstraints = true;  

    BindDataGridViews();

}

然后将数据绑定到 DataGridViews。我创建了一个带有两个选项卡页的控件,每个选项卡上有两个 DataView,并手动整理了列。

public void BindDataGridViews()
{

    //bind the datagridviews
    dgvA.DataSource = ds;
    dgvA.DataMember = "A";
    dgvAtoB.DataSource = ds;
    dgvAtoB.DataMember = "A.AToB";

    dgvB.DataSource = ds;
    dgvB.DataMember = "B";
    dgvBtoA.DataSource = ds;
    dgvBtoA.DataMember = "B.BToA";

    DisplayColumnsinGrids();
}

//note the form designer sorted out all the form stuff 
//I just renamed the default datagridviews to be called dgvA
//dgvAtoB (on tab page 1) dgvB and dgvBtoA (on tab page 2)

DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
//...ect

private void DisplayColumnsinGrids()
{
    //sort out columns - I'm not sure how or if it's possible 
    //to use the designer to do this with datarelations etc?
    //so I created some unbound columns and manually 
    //wired it all up - anyone know the correct procedure?
    //I know the designer can create unbound columns, 
    //but I couldn't see how to bind them.

    dataGridViewTextBoxColumn1 = 
       new System.Windows.Forms.DataGridViewTextBoxColumn();
    dataGridViewTextBoxColumn1.HeaderText = "AID";
    dataGridViewTextBoxColumn1.Name = "AID";
    dataGridViewTextBoxColumn1.DataPropertyName = "AID";
    dataGridViewTextBoxColumn1.Width = 30;
    dgvA.Columns.Add(this.dataGridViewTextBoxColumn1);

    //...and so on for all 12 columns - see the full source 
    //in the download zip

}

看来 C# 的新 Express 版本与我最初使用的 C# 版本有所不同。特别是源代码现在被放在了不同的文件中,所以最好下载 zip 文件并在那里查看所有内容。

特别感谢 George Shepherd 的Windows Form FAQ,以及 Google 上的许多群组,还有这个尊贵的社区 :)

© . All rights reserved.