维护LINQ to SQL多对多表间关系[转]
在项目开发中,经常会碰到维护多对多(many to many)关系表间关系的操作,例如为人员配置角色、为人员配置部门、为产品配置类别等。如果没有经过程序设计而直接进行开发,将会过多地关注其细节问题,如:应删除那些数据、应添加哪些数据、应保留哪些数据等,导致开发效率降低。
名词解释
在本文开始之前,首先以用户-用户角色-角色表为例,声明三个概念:
l 主表:如果为用户配置角色,那么用户就是主表;如果为角色配置用户,那么角色就是主表。
l 从表:如果为用户配置角色,那么角色就是从表。
l 关系表:记录用户与角色表间关系的表。
行为描述
经过总结,发现其行为有统一的地方:传递主表对象与从表对象集合->获取现有关系->对比出要删除的关系->对比出要添加的关系->提交更改。
在下面的文章中,将一步步介绍完成的过程。
测试用例
使用ASP.NET网站程序,新建如下图所示的页面:
在左侧的用户选择DropDownList中,选择一个现有用户后,会在右侧的CheckBoxList中显示其具有的角色。在进行完配置后,可以点击上方的”保存”LinkButton进行保存。
建表脚本下载
下面我将4以种情况,来展示这个示例。在每个操作完成后,我们执行以下脚本,来查看操作是否成功:
脚本:
开始测试前:
添加一个关系
在本次操作中,将为马六配置一个“Java程序员”的角色。
点击保存后,查看数据库数据:
可以看到,已经成功添加一个关系。
添加两个关系,其中一个将新建,一个不做处理
在本次操作中,将为马六配置 “Java程序员 + .NET程序员”的角色。
点击保存后,查看数据库数据:
可以看到数据库中的关系数据变为了两条,并且RoleID为“63B04…”的关系数据的主键“064AB…”并没有发生改变,这说明我并没有做“全部删除,再全部添加”的暴力型操作。
添加两个关系,其中一个将新建,一个不做处理,一个将删除
在本次操作中,将为马六配置 “Java程序员 + 项目经理”的角色。
点击保存后,查看数据库数据:
可以看到RoleID为“71DFA…”的关系被删除了,而且新建了一个RoleID为“3F45B…”的关系,而RoleID为“63B04…”的关系没有发生改变。操作成功完成。
删除所有关系
在本次操作中,将马六的全部角色置空。
点击保存后,查看数据库数据:
可以看到用户的关系被全部删除了。至此,测试工作告一段落,开始介绍功能是如何完成的。
在点击保存按钮时,要做的就是将选中的用户,以及为其配置的角色保存到数据库中。有过这样的开发经验的开发人员会知道这个过程还是十分繁琐的,在下面的代码中,我将使用设计后的方式来完成这个操作:
/// 点击保存按钮时的操作
/// </summary>
protected void btnSave_Click(object sender, EventArgs arg)
{
Users user = DataContext.Users.SingleOrDefault(e => e.UserID == new Guid(ddlUsers.SelectedValue));
List<Roles> roles = new List<Roles>();
foreach (ListItem item in cblUserRoles.Items)
{
if (item.Selected)
{
Roles role = DataContext.Roles.SingleOrDefault(e => e.RoleID == new Guid(item.Value));
if (role != null)
{
roles.Add(role);
}
}
}
LinqM2MProvider.Execute(user, roles, true);
}
可以看到,除了封装参数(用户对象,角色对象集合)的常规操作外,只调用了一个方法:LinqM2MProvider.Execute(user, roles, true)。那么这个LinqM2MProvider是什么呢?答案是一个接口,下面的代码介绍了这个接口的构造方法:
/// 管理用户-用户角色-角色之间关系的提供程序
/// </summary>
public ILinqM2MProvider<Users, UsersInRoles, Roles> LinqM2MProvider
{
get
{
var provider = new LinqM2MProvider<Users, UsersInRoles, Roles>()
{
DataContext = this.DataContext,
GetRelationHandler = (m, s) => DataContext.UsersInRoles.SingleOrDefault(e => e.UserID == m.UserID && e.RoleID == s.RoleID),
RelationSetHandler = m => m.UsersInRoles,
CreateRelationHandler = (m, s) => new UsersInRoles()
{
UserInRoleID = Guid.NewGuid(),
Users = m,
Roles = s
}
};
return provider;
}
}
可以看到ILinqM2MProvider接口是一个泛型接口,它的三个泛型类型是用户-用户角色-角色,就跟本文“名词解释”段落中的图片所显示关系一致。使用它需要设置四个属性:
l DataContext
l GetRelationHandler
l RelationSetHandler
l CreateRelationHandler
那么这个四个属性分别代表什么含义呢?下文将进行详细的说明。
接口ILinqM2MProvider<M, R, S>
定义
/// 维护LINQ to SQL的多对多关联关系
/// </summary>
/// <typeparam name="M">主表类型</typeparam>
/// <typeparam name="R">关系表类型</typeparam>
/// <typeparam name="S">从表类型</typeparam>
/// <remarks>
/// Sunny D.D at 2010-8-20
/// sunny19788989@gmail.com
/// </remarks>
public interface ILinqM2MProvider<M, R, S>
where M : class
where R : class
where S : class
{
/// <summary>
/// LINQ to SQL入口
/// </summary>
DataContext DataContext { get; set; }
/// <summary>
/// 描述如何根据主表对象和从表对象获取中间关系表对象的行为
/// </summary>
Func<M, S, R> GetRelationHandler { get; set; }
/// <summary>
/// 描述如何根据主表对象和从表对象创建中间关系表对象的行为
/// </summary>
Func<M, S, R> CreateRelationHandler { get; set; }
/// <summary>
/// 描述如何根据主表对象获取获取中间关系表对象集合
/// </summary>
Func<M, EntitySet<R>> RelationSetHandler { get; set; }
/// <summary>
/// 获取将要删除的关系
/// </summary>
/// <param name="master">主表对象</param>
/// <param name="slaves">从表对象</param>
/// <returns></returns>
IEnumerable<R> GetDeleting(M master, IEnumerable<S> slaves);
/// <summary>
/// 获取将要新建的关系
/// </summary>
/// <param name="master">主表对象</param>
/// <param name="slaves">从表对象</param>
/// <returns></returns>
IEnumerable<R> GetAdding(M master, IEnumerable<S> slaves);
/// <summary>
/// 执行操作。默认在方法结束时不将变更提交至数据库,需要显式提交变更。
/// </summary>
/// <param name="master">主表对象</param>
/// <param name="slaves">从表对象</param>
void Execute(M master, IEnumerable<S> slaves);
/// <summary>
/// 执行操作
/// </summary>
/// <param name="master">主表对象</param>
/// <param name="slaves">从表对象</param>
/// <param name="isSubmitChanges">是否在方法结束时将变更提交至数据库</param>
void Execute(M master, IEnumerable<S> slaves, bool isSubmitChanges);
}
DataContext属性
类型:System.Data.Linq.DataContext
RelationSetHandler属性
描述如何根据主表对象获取获取中间关系表对象集合。
在LINQ to SQL架构中,一个表的外键对象集合是用EntitySet来表示的,EntitySet中的元素代表着主键表关联的外建表的条目。在本文的“测试用例”部分中,是这样赋值的:
RelationSetHandler = m => m.UsersInRoles
GetRelationHandler属性
描述如何根据主表对象和从表对象获取中间关系表对象的行为。在本文中,就是根据用户ID与角色ID获取一个UsersInRoles对象。
它的作用是确定有多少个中间表对象需要被操作。当然,最后提交至数据库的元素,是与RelationSetHandler操作结果中包含的元素进行比对后才被执行的。在本文的“测试用例”部分中,是这样赋值的:
GetRelationHandler = (m, s) => DataContext.UsersInRoles.SingleOrDefault(e => e.UserID == m.UserID && e.RoleID == s.RoleID)
CreateRelationHandler属性
描述如何根据主表对象和从表对象创建中间关系表对象的行为。
在传递的从表集合中,发现有需要新建的关系时,就要用到这个操作。在本文的“测试用例”部分中,是这样赋值的:
{
UserInRoleID = Guid.NewGuid(),
Users = m,
Roles = s
}
GetDeleting方法
获取将要删除的关系。
GetAdding方法
获取将要新建的关系。
Execute方法
执行操作。
接口的实现LinqM2MProvider<M, R, S>
至于接口的实现,各位肯定都有自己的方式,在这里我就不详细说明了,本文给出一个实现仅供参考。点击下载
总结
使用本文中介绍的方式来管理多对多表间关系,就可以不关注操作到底是进行添加关系、删除关系、还是更改关系了,开发人员需要做的只是将三个委托GetRelationHandler、CreateRelationHandler、RelationSetHandler构造好,并传递正确的参数(主表对象、从表集合)即可,从而不再关注操作细节,提高开发效率。
本文资源下载
建表脚本:下载
页面代码:UsersInRolesDemo.aspx UsersInRolesDemo.aspx.cs
接口ILinqM2MProvider:下载
实现LinqM2MProvider:下载
如果链接打不开,请配置hosts文件:
209.85.225.101 docs.google.com
74.125.127.100 writely.google.com
74.125.127.139 spreadsheets.google.com
原文链接:http://www.cnblogs.com/sunnycoder/archive/2010/08/22/1805875.html
转载于:https://www.cnblogs.com/poissonnotes/archive/2010/08/25/1808142.html
维护LINQ to SQL多对多表间关系[转]相关推荐
- Access和SQL server开启表间关系,并实现更新或删除母表数据自动更新或删除子表数据...
1.Access开启表间关系,并实现删除母表数据自动删除子表数据: 在Tables等界面 - > 右键 - > Relationships... -> 弹出Relationships ...
- LINQ TO SQL和Entity Framework 的关系 你了解多少?
1. LINQ TO SQL 和EF 特点: LINQ TO SQL和Entity Framework都是一种包含LINQ功能的ORM 也就是所谓的关系对象的映射.其中包括的有DBFrist C ...
- Hibernate之表间关系
ManyToOne 多对一,是最常见的表间关系,对应关系数据库中的外键关系.通常用于建立子实体和其父实体的关联关系 @Entity(name = "Person") public ...
- SAP-MM采购订单相关的主要后台表间关系
主要的表及描述如下: 1.相关表列举说明 EKPO 采购凭证项目 EKKO 采购凭证抬头 EORD 采购货源清单 EINA 采购信息记录 - 一般数据 EINE 采购信息记录 - 采购组织数据 EKE ...
- LINQ to SQL自定义映射表关系(1:N or 1:1)
以Northwind库为例,新建LINQ TO SQL Classes,我们将表Suppliers和Products拖进新建的dbml文件设计界面. 默认情况下,IDE自动生成的映射关系为1:N.即C ...
- mysql的关系表_mysql 数据库表间关系图怎么查看?
展开全部 mysql数据库表间的关系图可以通过navicat查看: 第一步:下载navicat打开: 第二步:点击navicat界面最右下角标注的按钮即可62616964757a686964616fe ...
- LINQ TO SQL (一):1. 对象关系设计器(O/R 设计器)
对象关系设计器(O/R 设计器)的作用: 1. 提供了一个可视化设计图面,用于创建基于数据库中对象的 LINQ to SQL 实体类和关系:创建映射到数据库中的对象的对象模型. 2. 生成一个强类型 ...
- 计算机 vfp表间关系有,VFP表间连接和VFP表的更新操作
VFP表间连接和VFP表的更新操作 分类:计算机等级 | 更新时间:2016-07-08| 来源:转载 一.建立表间连接:有时需要将不同表的内容按某种条件重新组成一个新表,可用连接命令join来实现该 ...
- Rhythmk 学习 Hibernate 05 - Hibernate 表间关系 [ManyToOne,OneToMany]
1.项目结构: 1.1.场景说明: 一个订单,包含多个产品 1.2.类文件: Order.java package com.rhythmk.model;import java.util.Date;pu ...
- vs05b2中给dataset添加表间关系
(转载)http://www.cnblogs.com/yang_sy/archive/2005/04/30/148344.aspx 转载于:https://www.cnblogs.com/loway/ ...
最新文章
- 一级计算机考试题库25套答案,全国计算机等级考试一级试题及答案(25套)..doc
- SAP CRM WebClient UI和Hybris Commerce里的跨组件跳转
- 如何删除Smartphone手机与Office同步后的重复项,如联系人、日程等。Keyword:office,Outlook,删除,重复项目...
- matlab 非线性电感,基于Matlab/Simulink利用动态和静态电感等磁参数建立了一种开关磁阻电机的非线性磁参数模型...
- 切换网段后,处理用友T6变成演示版故障
- 有人30岁转型做Android开发,老罗android开发视频教程
- Simulink 快速入门(二)--创建简单模型
- Html5餐饮管理app,哗啦啦餐饮软件 餐饮管理系统
- 一套绝佳的自定义3dmax快捷键!
- 天天向上的力量python代码解释_天天向上的力量 B
- PSP开发简明教程(3)
- android自动隐藏虚拟键,Android 隐藏底部虚拟键的两种方法
- WIN10计算机用户怎么改名,win10怎么改名字_win10怎么改用户名字
- 3D成像方法汇总(原理解析):双目视觉、激光三角、结构光、ToF、光场、全息...
- GPT-3: 最强的人工智能?
- 操作系统实验:添加系统调用修改主机名(hostname)
- Hexo+Github: 博客网站搭建完全教程(看这篇就够了)
- roundrobin来历_黑山:起源Source引擎低画质指令
- WPS表格 - 数字累加技巧总结
- 移动互联网的创业机会在哪