AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理、职称管理、部门管理、角色管理、角色权限管理等模块。

关联表的查询操作

使用 Include 方法,我们可以在一次数据库查询中将关联表的数据一并取出。

比如查询在线用户列表页面,需要在前端显示关联的用户信息,如下所示:

<x:Grid ID="Grid1" runat="server" BoxFlex="1" ShowBorder="true" ShowHeader="false"
EnableCheckBoxSelect="false" EnableRowNumber="true" DataKeyNames="ID" AllowSorting="true"
OnSort="Grid1_Sort" SortColumnIndex="0" SortDirection="DESC" AllowPaging="true"
IsDatabasePaging="true" OnPageIndexChange="Grid1_PageIndexChange">
<Columns>
<x:BoundField DataField="UpdateTime" SortField="UpdateTime" DataFormatString="{0:yyyy-MM-dd HH:mm}"
Width="120px" HeaderText="最后操作时间" />
<x:BoundField DataField="LoginTime" SortField="LoginTime" Width="120px" DataFormatString="{0:yyyy-MM-dd HH:mm}"
HeaderText="登录时间" />
<x:BoundField DataField="User.Name" SortField="User.Name" Width="100px" HeaderText="用户名" />
<x:BoundField DataField="User.ChineseName" SortField="User.ChineseName" Width="100px" HeaderText="中文名" />
<x:BoundField DataField="IPAdddress" ExpandUnusedSpace="true" HeaderText="IP地址" />
<x:WindowField Text="查看" Title="查看" WindowID="Window1" DataIFrameUrlFields="User.ID"
DataIFrameUrlFormatString="~/admin/user_view.aspx?id={0}" Width="50px" />
</Columns>
</x:Grid>

其中“用户名”和“中文名”以及“查看”按钮,都用到了导航属性User(Navigation Property),所以我们在查询时也使用Include来避免延迟加载:

IQueryable<Online> q = DB.Onlines.Include(o => o.User);

// 在用户名中搜索
string searchText = ttbSearchMessage.Text.Trim();
if (!String.IsNullOrEmpty(searchText))
{
q = q.Where(o => o.User.Name.Contains(searchText));
} DateTime lastD = DateTime.Now.AddHours(-2);
q = q.Where(o => o.UpdateTime > lastD); // 在添加条件之后,排序和分页之前获取总记录数
Grid1.RecordCount = q.Count(); // 排列和数据库分页
q = SortAndPage<Online>(q, Grid1); Grid1.DataSource = q;
Grid1.DataBind();

  

为了更形象的说明 Include 的作用,我们使用工具ExpressProfile来监视对数据库的操作:http://expressprofiler.codeplex.com/  

首先看下页面显示效果:

使用 Include 情况下,看下对数据库的操作:

可以看到有两次数据库查询(获取总记录数查询和分页数据查询),相应的查询语句分别为:

获取总记录数的SQL查询语句:

exec sp_executesql N'SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Onlines] AS [Extent1]
WHERE [Extent1].[UpdateTime] > @p__linq__0
) AS [GroupBy1]',N'@p__linq__0 datetime2(7)',@p__linq__0='2013-08-23 15:35:08.2573160'
go

分页数据查询的SQL语句:

exec sp_executesql N'SELECT TOP (20)
[Project1].[ID] AS [ID],
[Project1].[IPAdddress] AS [IPAdddress],
[Project1].[LoginTime] AS [LoginTime],
[Project1].[UpdateTime] AS [UpdateTime],
[Project1].[ID1] AS [ID1],
[Project1].[Name] AS [Name],
[Project1].[Email] AS [Email],
[Project1].[Password] AS [Password],
[Project1].[Enabled] AS [Enabled],
[Project1].[Gender] AS [Gender],
[Project1].[ChineseName] AS [ChineseName],
[Project1].[EnglishName] AS [EnglishName],
[Project1].[Photo] AS [Photo],
[Project1].[QQ] AS [QQ],
[Project1].[CompanyEmail] AS [CompanyEmail],
[Project1].[OfficePhone] AS [OfficePhone],
[Project1].[OfficePhoneExt] AS [OfficePhoneExt],
[Project1].[HomePhone] AS [HomePhone],
[Project1].[CellPhone] AS [CellPhone],
[Project1].[Address] AS [Address],
[Project1].[Remark] AS [Remark],
[Project1].[IdentityCard] AS [IdentityCard],
[Project1].[Birthday] AS [Birthday],
[Project1].[TakeOfficeTime] AS [TakeOfficeTime],
[Project1].[LastLoginTime] AS [LastLoginTime],
[Project1].[CreateTime] AS [CreateTime],
[Project1].[DeptID] AS [DeptID]
FROM ( SELECT [Project1].[ID] AS [ID], [Project1].[IPAdddress] AS [IPAdddress], [Project1].[LoginTime] AS [LoginTime], [Project1].[UpdateTime] AS [UpdateTime], [Project1].[ID1] AS [ID1], [Project1].[Name] AS [Name], [Project1].[Email] AS [Email], [Project1].[Password] AS [Password], [Project1].[Enabled] AS [Enabled], [Project1].[Gender] AS [Gender], [Project1].[ChineseName] AS [ChineseName], [Project1].[EnglishName] AS [EnglishName], [Project1].[Photo] AS [Photo], [Project1].[QQ] AS [QQ], [Project1].[CompanyEmail] AS [CompanyEmail], [Project1].[OfficePhone] AS [OfficePhone], [Project1].[OfficePhoneExt] AS [OfficePhoneExt], [Project1].[HomePhone] AS [HomePhone], [Project1].[CellPhone] AS [CellPhone], [Project1].[Address] AS [Address], [Project1].[Remark] AS [Remark], [Project1].[IdentityCard] AS [IdentityCard], [Project1].[Birthday] AS [Birthday], [Project1].[TakeOfficeTime] AS [TakeOfficeTime], [Project1].[LastLoginTime] AS [LastLoginTime], [Project1].[CreateTime] AS [CreateTime], [Project1].[DeptID] AS [DeptID], row_number() OVER (ORDER BY [Project1].[UpdateTime] DESC) AS [row_number]
FROM ( SELECT
[Extent1].[ID] AS [ID],
[Extent1].[IPAdddress] AS [IPAdddress],
[Extent1].[LoginTime] AS [LoginTime],
[Extent1].[UpdateTime] AS [UpdateTime],
[Extent2].[ID] AS [ID1],
[Extent2].[Name] AS [Name],
[Extent2].[Email] AS [Email],
[Extent2].[Password] AS [Password],
[Extent2].[Enabled] AS [Enabled],
[Extent2].[Gender] AS [Gender],
[Extent2].[ChineseName] AS [ChineseName],
[Extent2].[EnglishName] AS [EnglishName],
[Extent2].[Photo] AS [Photo],
[Extent2].[QQ] AS [QQ],
[Extent2].[CompanyEmail] AS [CompanyEmail],
[Extent2].[OfficePhone] AS [OfficePhone],
[Extent2].[OfficePhoneExt] AS [OfficePhoneExt],
[Extent2].[HomePhone] AS [HomePhone],
[Extent2].[CellPhone] AS [CellPhone],
[Extent2].[Address] AS [Address],
[Extent2].[Remark] AS [Remark],
[Extent2].[IdentityCard] AS [IdentityCard],
[Extent2].[Birthday] AS [Birthday],
[Extent2].[TakeOfficeTime] AS [TakeOfficeTime],
[Extent2].[LastLoginTime] AS [LastLoginTime],
[Extent2].[CreateTime] AS [CreateTime],
[Extent2].[DeptID] AS [DeptID]
FROM [dbo].[Onlines] AS [Extent1]
INNER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[UserID] = [Extent2].[ID]
WHERE [Extent1].[UpdateTime] > @p__linq__0
) AS [Project1]
) AS [Project1]
WHERE [Project1].[row_number] > 0
ORDER BY [Project1].[UpdateTime] DESC',N'@p__linq__0 datetime2(7)',@p__linq__0='2013-08-23 15:35:08.2573160'
go

Entity Framework生成的SQL语句包含了子查询和关联查询,还是蛮复杂的。

我们把这两条SQL语句分别在VS2012的服务器资源管理器中执行一下,就能看到和页面上相同的结果了:  

  

作为对比,来看下不使用 Include 的数据库SQL查询语句:

此时,除了获取总记录数的SQL查询和获取分页数据的SQL查询,还多了 3 条获取用户信息的SQL查询,其中一个获取用户信息的SQL语句为:

exec sp_executesql N'SELECT
[Extent2].[ID] AS [ID],
[Extent2].[Name] AS [Name],
[Extent2].[Email] AS [Email],
[Extent2].[Password] AS [Password],
[Extent2].[Enabled] AS [Enabled],
[Extent2].[Gender] AS [Gender],
[Extent2].[ChineseName] AS [ChineseName],
[Extent2].[EnglishName] AS [EnglishName],
[Extent2].[Photo] AS [Photo],
[Extent2].[QQ] AS [QQ],
[Extent2].[CompanyEmail] AS [CompanyEmail],
[Extent2].[OfficePhone] AS [OfficePhone],
[Extent2].[OfficePhoneExt] AS [OfficePhoneExt],
[Extent2].[HomePhone] AS [HomePhone],
[Extent2].[CellPhone] AS [CellPhone],
[Extent2].[Address] AS [Address],
[Extent2].[Remark] AS [Remark],
[Extent2].[IdentityCard] AS [IdentityCard],
[Extent2].[Birthday] AS [Birthday],
[Extent2].[TakeOfficeTime] AS [TakeOfficeTime],
[Extent2].[LastLoginTime] AS [LastLoginTime],
[Extent2].[CreateTime] AS [CreateTime],
[Extent2].[DeptID] AS [DeptID]
FROM [dbo].[Onlines] AS [Extent1]
INNER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[UserID] = [Extent2].[ID]
WHERE [Extent1].[ID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=2
go

其在VS2012中的执行结果为:

  

关联表的新增操作

在前面的文章中,我们已经看到如何新增一个用户,并为用户添加角色列表,如下所示:

User item = new User();
item.Name = tbxName.Text.Trim();
item.Password = PasswordUtil.CreateDbPassword(tbxPassword.Text.Trim());
item.ChineseName = tbxRealName.Text.Trim();
item.Gender = ddlGender.SelectedValue;
item.CompanyEmail = tbxCompanyEmail.Text.Trim();
item.Email = tbxEmail.Text.Trim();
item.OfficePhone = tbxOfficePhone.Text.Trim();
item.OfficePhoneExt = tbxOfficePhoneExt.Text.Trim();
item.HomePhone = tbxHomePhone.Text.Trim();
item.CellPhone = tbxCellPhone.Text.Trim();
item.Remark = tbxRemark.Text.Trim();
item.Enabled = cbxEnabled.Checked;
item.CreateTime = DateTime.Now; // 添加所有角色
if (!String.IsNullOrEmpty(hfSelectedRole.Text))
{
int[] roleIDs = StringUtil.GetIntArrayFromString(hfSelectedRole.Text); item.Roles = new List<Role>();
foreach (int roleID in roleIDs)
{
Role role = new Role { ID = roleID };
DB.Roles.Attach(role);
item.Roles.Add(role);
}
} DB.Users.Add(item);
DB.SaveChanges();

  

关联表的删除操作

从角色中删除多个用户的操作,用Subsonic来实现的代码:

List<int> roleIds = GetSelectedDataKeyIDs(Grid1);
List<int> userIds = GetSelectedDataKeyIDs(Grid2); new Delete().From<XRoleUser>()
.Where(XRoleUser.RoleIdColumn).IsEqualTo(roleIds[0])
.And(XRoleUser.UserIdColumn).In(userIds)
.Execute();

使用Entity Framework时,由于需要先将需要的数据载入,所以代码稍微有点复杂:

Role role = DB.Roles.Include(r => r.Users)
.Where(r => r.ID == roleID)
.FirstOrDefault(); foreach (int userID in userIDs)
{
User user = role.Users.Where(u => u.ID == userID).FirstOrDefault();
if (user != null)
{
role.Users.Remove(user);
}
} DB.SaveChanges();

上面代码也可以简化为:

Role role = DB.Roles.Include(r => r.Users)
.Where(r => r.ID == roleID)
.FirstOrDefault(); role.Users.Where(u => userIDs.Contains(u.ID)).ToList().ForEach(u => role.Users.Remove(u)); DB.SaveChanges();

  

下面再来看看如果移除属于某个部门的多个用户,由于部门和用户是一对多的关系,所以我们可以先找到这些用户,再清空这些用户所属的部门,如下所示:

DB.Users.Include(u => u.Dept)
.Where(u => userIDs.Contains(u.ID))
.ToList()
.ForEach(u => u.Dept = null); DB.SaveChanges();

另一种做法,先找到这个部门,然后从部门的用户列表Users(导航属性)中删除这些用户,如下所示:

Dept dept = DB.Depts.Include(d => d.Users)
.Where(d => d.ID == deptID)
.FirstOrDefault(); foreach (int userID in userIDs)
{
User user = dept.Users.Where(u => u.ID == userID).FirstOrDefault();
if (user != null)
{
dept.Users.Remove(user);
}
} DB.SaveChanges();

上面两种做法的结果是完全一致的。  

关联表的更新操作

以更新单个用户信息为例,一个用户可以属于一个部门(Dept) ,可以拥有多个角色(Role),使用Subsonic更新用户信息的代码如下所示:

using (TransactionScope scope = new TransactionScope())
{
int userId = GetQueryIntValue("id");
XUser item = XUser.FetchByID(userId);
//item.Name = tbxName.Text.Trim();
item.ChineseName = tbxRealName.Text.Trim();
item.Gender = ddlGender.SelectedValue;
item.CompanyEmail = tbxEmail.Text.Trim();
item.PersonalEmail = tbxPersonalEmail.Text.Trim();
item.CellPhone = tbxCellPhone.Text.Trim();
item.OfficePhone = tbxOfficePhone.Text.Trim();
item.OfficePhoneExt = tbxOfficePhoneExt.Text.Trim();
item.HomePhone = tbxHomePhone.Text.Trim();
item.Remark = tbxRemark.Text.Trim();
item.Enabled = cbxEnabled.Checked;
//item.RoleId = Convert.ToInt32(ddlRole.SelectedValue);
//item.DeptId = Convert.ToInt32(ddlDept.SelectedValue);
item.Save(); // 删除用户所属的所有角色
new Delete().From(XRoleUser.Schema)
.Where(XRoleUser.UserIdColumn).IsEqualTo(userId)
.Execute(); // 添加所有角色
if (!String.IsNullOrEmpty(hfSelectedRole.Text))
{
XRoleUserCollection roleUsers = new XRoleUserCollection();
foreach (string roleIdStr in hfSelectedRole.Text.Split(','))
{
int id = Convert.ToInt32(roleIdStr); XRoleUser roleUser = new XRoleUser();
roleUser.RoleId = id;
roleUser.UserId = userId; roleUsers.Add(roleUser);
}
roleUsers.SaveAll();
} // 删除用户所属的所有部门
new Delete().From(XDeptUser.Schema)
.Where(XDeptUser.UserIdColumn).IsEqualTo(userId)
.Execute(); // 添加所有部门
if (!String.IsNullOrEmpty(hfSelectedDept.Text))
{
XDeptUser deptUser = new XDeptUser();
deptUser.DeptId = Convert.ToInt32(hfSelectedDept.Text);
deptUser.UserId = userId;
deptUser.Save();
} scope.Complete();
}

这里的数据库查询比较多,分别为:

1. 查询单个数据的信息;

2. 删除用户所属的所有角色;

3. 添加用户的新角色;

4. 删除用户所属的部门;

5. 添加用户所属的新部门;(这个地方是数据库设计也不大合理,其实不需单独表来保存部门用户信息)  

完成相同的功能,来看下Entity Framework的实现:

int id = GetQueryIntValue("id");
User item = DB.Users
.Include(u => u.Dept)
.Include(u => u.Roles)
.Where(u => u.UserID == id).FirstOrDefault();
//item.Name = tbxName.Text.Trim();
item.ChineseName = tbxRealName.Text.Trim();
item.Gender = ddlGender.SelectedValue;
item.CompanyEmail = tbxCompanyEmail.Text.Trim();
item.Email = tbxEmail.Text.Trim();
item.CellPhone = tbxCellPhone.Text.Trim();
item.OfficePhone = tbxOfficePhone.Text.Trim();
item.OfficePhoneExt = tbxOfficePhoneExt.Text.Trim();
item.HomePhone = tbxHomePhone.Text.Trim();
item.Remark = tbxRemark.Text.Trim();
item.Enabled = cbxEnabled.Checked; if (String.IsNullOrEmpty(hfSelectedDept.Text))
{
item.Dept = null;
}
else
{
int newDeptID = Convert.ToInt32(hfSelectedDept.Text);
if (item.Dept.DeptID != newDeptID)
{
Dept newDept = new Dept { DeptID = newDeptID };
DB.Depts.Attach(newDept);
item.Dept = newDept;
}
} if (String.IsNullOrEmpty(hfSelectedRole.Text))
{
item.Roles = null;
}
else
{
int[] roleIDs = StringUtil.GetIntArrayFromString(hfSelectedRole.Text); int[] addRoleIDs = roleIDs.Except(item.Roles.Select(r => r.RoleID)).ToArray();
int[] removeRoleIDs = item.Roles.Select(r => r.RoleID).Except(roleIDs).ToArray(); foreach (int roleID in addRoleIDs)
{
var role = new Role { RoleID = roleID };
DB.Roles.Attach(role);
item.Roles.Add(role);
}
foreach (int roleID in removeRoleIDs)
{
item.Roles.Remove(item.Roles.Single(r => r.RoleID == roleID));
} } DB.SaveChanges();

  

这里,我们又再次用到了 Attach 方法,实际上这样使用 Attach 是有风险的,下一节我们会重点讲解 Attach 的使用。

下载或捐赠AppBox

1. AppBox v2.0 是免费软件,免费提供下载:http://fineui.com/bbs/forum.php?mod=viewthread&tid=3788

2. AppBox v3.0 是捐赠软件,你可以通过捐赠作者来获取AppBox v3.0的全部源代码(http://fineui.com/donate/)。

AppBox升级进行时 - 关联表查询与更新(Entity Framework)的更多相关文章

  1. 在MyBatis中查询数据、涉及多参数的数据访问操作、插入数据时获取数据自增长的id、关联表查询操作、动态SQL、关于配置MyBatis映射没有代码提示的解决方案

    1. 单元测试 在单元测试中,每个测试方法都需要执行相同的前置代码和后置代码,则可以自定义2个方法,分别在这2个方法中执行前置代码和后置代码,并为这2个方法添加@Before和@After注解,然后, ...

  2. AppBox升级进行时 - 拥抱Entity Framework的Code First开发模式

    AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. 从Subsonic到Entity Framework Subsonic最早发布 ...

  3. AppBox升级进行时 - 扁平化的权限设计

    AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. AppBox v2.0中的权限实现 AppBox v2.0中权限管理中涉及三个 ...

  4. Mybatis源码分析--关联表查询及延迟加载原理(二)

    在上一篇博客Mybatis源码分析--关联表查询及延迟加载(一)中我们简单介绍了Mybatis的延迟加载的编程,接下来我们通过分析源码来分析一下Mybatis延迟加载的实现原理. 其实简单来说Myba ...

  5. .NetCore中EFCore的使用整理(二)-关联表查询

    EF常用处理关联加载的方式有3中:延迟加载(Lazy Loading).贪婪加载 (Eager Loading)以及显示加载. 一.EF Core  1.1 1.当前的版本,还不支持延迟加载(Lazy ...

  6. SpringBoot Data JPA 关联表查询的方法

    SpringBoot Data JPA实现 一对多.多对一关联表查询 开发环境 IDEA 2017.1 Java1.8 SpringBoot 2.0 MySQL 5.X 功能需求 通过关联关系查询商店 ...

  7. AppBox升级进行时 - Entity Framework的增删改查

    AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. Entity Framework新增数据 以新增用户为例,作为对比,先来看下使 ...

  8. AppBox升级进行时 - Any与All的用法(Entity Framework)

    AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. 属于某个角色的用户列表(Any的用法) 使用Subsonic,我们有两种方法获 ...

  9. Mybatis源码分析--关联表查询及延迟加载(一)

    Mybatis提供了关联查询映射的功能. 一.一对一关联

随机推荐

  1. CSS3与页面布局学习笔记(七)——前端预处理技术(Less、Sass、CoffeeScript、TypeScript)

    CSS不像其它高级语言一样支持算术运算.变量.流程控制与面向对象特性,所以CSS样式较多时会引起一些问题,如修改复杂,冗余,某些别的语言很简单的功能实现不了等.而javascript则是一种半面向对象 ...

  2. 深入理解和应用Float属性

    一.Float的特性 1. 应用于文字围绕图片 2. 创建一个块级框 3. 多列浮动布局 4. 浮动元素的宽度.高度自适应,但可以设置其值. 二.核心解决的问题 文字围绕图片:img标签与多个文本标签 ...

  3. 推荐8个实现 SVG 动画的 JavaScript 库

    SVG 是一种分辨率无关的图形(矢量图形).这意味着它在任何类型的屏幕都不会遭受任何质量损失.除此之外,你可以让 SVG 灵活现一些动画效果.这篇文章就给大家推荐8个实现 SVG 动画的 JavaSc ...

  4. js圣诞节倒计时网页

    在线预览:http://keleyi.com/keleyi/phtml/jstexiao/17.htm 以下是代码: <!DOCTYPE html> <html xmlns=&quo ...

  5. jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)

    上一篇记录了BaiduTemplate模板引擎使用示例附源码,在此基础上对使用方法进行了封装 自定义插件jajaxrefresh.js 代码如下: //闭包限定命名空间 (function ($) { ...

  6. iOS如何用代码控制以不同屏幕方向打开新页面?

    转载:http://blogread.cn/it/article/7765?f=wb#original 代码示例:https://github.com/johnlui/Swift-On-iOS/tre ...

  7. ArcSDE给Oracle添加SDE函数包

    SDE函数包中包含大量的空间计算分析函数,是我们做空间相关分析的一把利刃(目前好像我们只有这一把),有关SDE函数的使用,请见我空间另外的一篇帖子的附件.按照通常教程,过程是这样的1.找到listen ...

  8. SharePoint 2007 User Re-created in AD with new SID issue on MySite

    When active directory users get deleted and re-created, even with the same user id, there's a nasty ...

  9. JAVA简单工厂模式(从现实生活角度理解代码原理)

    简单工厂模式(Simple Factory),说他简单是因为我们可以将此模式比作一个简单的民间作坊,他们只有固定的生产线生产固定的产品.也可以称他为静态工厂设计模式,类似于之前提到过静态代理设计模式, ...

  10. iOS--手势之谜

    原文转至: http://www.cnblogs.com/huangjianwu/p/4675648.html iOS在手机APP的手势操作中包含了:拖动.捏合.旋转.点按.长按.轻扫.自定义等等,详 ...