在 LINQ for SQL 中创建多对多关系






4.46/5 (7投票s)
2006 年 9 月 1 日
5分钟阅读

120245

1634
在 LINQ for SQL 中创建多对多关系。
引言
Microsoft 正在大力开发代号为“Orcas”的 Visual Studio,并最近发布了 LINQ CTP(五月版)和 ADO.NET vNext。本文基于 LINQ 2006 年 5 月 CTP。
必备组件
要运行示例并理解本文,您必须拥有
- Visual Studio 2005
- MS SQL Server 2005
- LINQ 五月 CTP
开始新项目
安装 LINQ 后,VS2005 中将出现与 LINQ 相关的模板。因此,请按以下步骤创建新项目:
- 单击“开始”|“程序”|“Microsoft Visual Studio 2005”|“Microsoft Visual Studio 2005”菜单命令。
- 单击“工具”|“选项”菜单命令。
- 在 Microsoft Visual Studio 中,单击“文件”|“新建”|“项目…”菜单命令。
- 在“新建项目”对话框中,在“项目类型”下,单击“Visual C#”|“LINQ Preview”。
- 在“模板”中,单击“LINQ Console Application”。
- 在“名称”字段中输入
LINQRelations
作为新项目的名称。 - 点击“确定”。
- 在警告对话框中,单击“确定”。
好了……现在我们有了一个带有空窗体的新的 Windows Forms 应用程序。让我们为数据库对象生成代码。
使用代码生成来创建对象模型
- 将数据库文件(来自LINQ_Databases.zip)附加到 SQL Server,名称为MovieCollection。
- 生成数据库表关系可能很繁琐且容易出错。在 Visual Studio 支持 LINQ 之前,您可以手动运行代码生成工具 SQLMetal。单击“开始”|“程序”|“Microsoft Visual Studio 2005”|“Visual Studio Tools”|“Visual Studio 2005 Command Prompt”菜单项。
- 执行以下命令更改为项目位置的目录(请将路径更改为您项目的实际路径):cd "D:\Projects\NET\LINQRelations\LINQRelations"。
- 通过输入以下命令生成完整的
MovieCollection
类层次结构,并带有主键和外键注解:"C:\Program Files\LINQ Preview\Bin\SqlMetal.exe" /server:.\SQLExpress /database:" MovieCollection " /pluralize /code:MovieCollection.cs。 - 在 Microsoft Visual Studio 的“解决方案资源管理器”中,单击“LINQRelations”|“添加”|“现有项”菜单命令。
- 找到新的MovieCollection.cs文件,然后单击“添加”。
- 在“解决方案资源管理器”中,双击MovieCollection.cs。
- 编译项目,以便在接下来的步骤中显示这些对象。
现在,我们已经生成了实体,并且它们已准备好进行查询。
使用 LINQ 列出实体
- 在 Visual Studio 2005(已打开 LINQRelations)中,打开“数据源”窗口(窗体菜单“数据”|“显示数据源”)。
- 右键单击“数据源”窗口中的任意位置,然后选择“添加新数据源…”
- 从向导中,选择“对象”作为数据源类型,然后单击“下一步”。
- 选择Movie,如下图所示,然后单击“下一步”|“完成”关闭向导。
- 打开 Form1。
- 将 Movie 从“数据源”窗口拖放到窗体上。
- 对于窗体的
Load
事件,我们需要编写一些代码来从服务器加载数据。private void Form1_Load(object sender, EventArgs e) { MovieCollection colllection = new MovieCollection(@"Data Source=.\SQLEXPRESS;Database" + @" = MovieCollection;Integrated Security=True;"); Table<Movie> movies = colllection.Movies; movieBindingSource.DataSource = movies; }
执行项目以确认结果。(我添加了一部电影,这样表格就不会是空的)。到目前为止一切顺利。Microsoft 已确认 SQL Metal 工具(我们用来生成 MovieCollection.cs 中代码的工具)支持一对一和一对多关系。如果我们迫不及待想支持它怎么办?
添加多对多关系
在此数据库中,我们有两个实体表(Movie 和 Category)和一个连接表 MovieCategory,用于支持多对多关系。
我们需要编写一些代码来支持它,但这值得付出努力。
首先,我们需要访问 DataContext 实例
我们必须从一个实例获取所有对象。这样,数据一致性就得到了保证。(更多信息,请参阅 LINQ CTP 的 C#/VB.NET 的 DLinq 实操实验室)。在Program.cs文件中,编写
private static MovieCollection dataContext = null;
internal static MovieCollection DataContext
{
get
{
if (null == dataContext)
{
dataContext =
new MovieCollection(@"Data Source=.\SQLEXPRESS;" +
@"Database = MovieCollection;" +
@"Integrated Security=True;");
}
return dataContext;
}
}
我们将使用此 Singleton 实现从一个点访问数据库。为确保这一点,我们必须将 Form_Load
事件更改为
private void Form1_Load(object sender, EventArgs e)
{
movieBindingSource.DataSource = Program.DataContext.Movies;
}
扩展 Movie 类
接下来要做的是扩展 Movie
类以返回所有 Category。我更倾向于在 .NET 2.0 中使用部分类,这样如果我们必须重新生成代码,就可以区分工具生成的代码。在 Visual Studio 2005 中,添加一个名为MovieCollectionExtend.cs的新文件。不要忘记将MovieCollection.cs中的类放在 LINQRelations
命名空间中。现在,让我们利用部分类的魔力。用以下内容填充MovieCollectionExtend.cs:
public partial class Movie
{
protected EntitySet<Category> categories;
/// <summary>
/// Return assigned categories to current movie
/// </summary>
public EntitySet<Category> Categories
{
get
{
categories = new EntitySet<Category>(onAddCategory, onRemoveCategory);
var cats = from c in Program.DataContext.Categories
join mc in Program.DataContext.MovieCategories
on c.CategoryID equals mc.CategoryID
where mc.MovieID == this.MovieID
select c;
//add to result
foreach (var c in cats)
categories.Add(c);
return categories;
}
}
protected void onAddCategory(Category arg)
{
//add entry to MovieCategories table
MovieCategory mc = new MovieCategory();
mc.MovieID = this.MovieID;
mc.CategoryID = arg.CategoryID;
Program.DataContext.MovieCategories.Add(mc);
}
protected void onRemoveCategory(Category arg)
{
}
}
信不信由你,这就是我们从特定电影中提取所有分类所需的全部。
我们刚才做了什么?
让我们一步一步地解释代码。我们为 Movie
类创建了一个名为 Categories
的新属性。在 setter 内部,我们创建了一个新的 Categories
集合实例,该实例将被填充并在请求时返回。在构造函数中定义了两个委托:一个在向集合添加新元素时执行,另一个在删除元素时执行。
categories = new EntitySet Category (onAddCategory, onRemoveCategory);
下一条语句对对象执行 LINQ 查询。不幸的是,那时我们必须能够访问 DataContext
实例。
var cats = from c in Program.DataContext.Categories
join mc in Program.DataContext.MovieCategories
on c.CategoryID equals mc.CategoryID
where mc.MovieID == this.MovieID
select c;
此查询将被翻译成 SQL:
SELECT [t0].[CategoryID], [t0].[CategoryName]
FROM [Category] AS [t0], [MovieCategory] AS [t1]
WHERE ([t1].[MovieID] = 1) AND ([t0].[CategoryID] = [t1].[CategoryID])
接下来的两行很简单:填充私有集合并将其作为结果返回。
//add to result
foreach (var c in cats)
categories.Add(c);
return categories;
委托 onAddCategory
负责在将新分类分配给电影时向连接表 MovieCategory
添加新行,而 onRemoveCategory
负责删除一行。就是这样。
你确定就这样了吗?
让我们创建另一个 DataGridView
来确保我们达成了目标。
- 在窗体上添加一个新的
DataGridView
控件,并将其命名为categoriesDataGridView
。 - 在
Form_load
事件中,添加一行以自动生成列。categoriesDataGridView.AutoGenerateColumns = true;
- 为
movieBindingSource
创建一个新的事件处理程序,用于CurrentChanged
事件。 - 为它编写以下代码:
private void movieBindingSource_CurrentChanged(object sender, EventArgs e) { categoriesDataGridView.DataSource = (movieBindingSource.Current as Movie).Categories; }
历史
- 版本 1 - 已发布。