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. file_put_contents 错误:failed to open stream: Invalid argument 一种原因

    今天在测试nilcms系统的时候,出现了一个报错,导致缓存无法更新: file_put_contents(C:\UPUPW_AP5.4\vhosts\d.tv\NilCMS_APP\include_r ...

  2. JavaScript中this指向的简单理解

    首先必须要说的是,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(这句话有些问题,后面会解释为什么会有问题,虽然 ...

  3. html5视频播放插件

    对于HTML5提供的新特性,给前端开发者带来了巨大的激情与动力,减轻了开发者的代码累赘,大大提高了网站性能以及网页的渲染效果.对于低版本的浏览器,由于生产厂商的原因,许多PC端低版本的浏览器还不兼容H ...

  4. javascript移动设备Web开发中对touch事件的封装实例

    在触屏设备上,一些比较基础的手势都需要通过对 touch 事件进行二次封装才能实现.zepto 是移动端上使用率比较高的一个类库,但是其 touch 模块模拟出来的一些事件存在一些兼容性问题,如 ta ...

  5. ArcGIS10.2.2 Desktop直接连接数据库的具体步骤

    ArcGIS10.2.2 Desktop直接连接数据库的具体步骤,以sqlserver2008R2和oracle11G数据库为例子,这里数据库的具体安装步骤不说了,不在讨论的范畴之内. 假如数据库软件 ...

  6. C# 在執行程式目錄下產生文件夾

    //產生一個Log文件夾string appPath = Application.StartupPath; if (!Directory.Exists(appPath + "/log&quo ...

  7. Unable to extract 64-bitimage. Run Process Explorer from a writeable directory

    Unable to extract 64-bitimage. Run Process Explorer from a writeable directory When we run Process E ...

  8. Android—android与js交互以及相互传参

    Android中可以通过WebView来实现与js的交互,让用户可以在android客户端看到js写的页面,接下来为大家介绍的就是怎样实现此功能: 首先android项目目录下有“assets”文件夹 ...

  9. 小心SQL SERVER 2014新特性——基数评估引起一些性能问题

    在前阵子写的一篇博文"SQL SERVER 2014 下IF EXITS 居然引起执行计划变更的案例分享"里介绍了数据库从SQL SERVER 2005升级到 SQL SERVER ...

  10. 查询表结构sql

    THEN obj.name ELSE '' END AS 表名, col.colorder AS 序号, col.name AS 列名, ISNULL(ep.value, N'') AS 列说明, t ...