Entity Framework应用:导航属性
一、主键和外键
关系型数据库中的一条记录中有若干个属性,若其中某一个属性组是能唯一标识一条记录,该属性组就可以称为主键。例如:
学生版(学号、姓名、性别、班级)
其中每个学生的学号是唯一的,学号就是一个主键。
课程表(课程编号,课程名,学分)
其中课程编号是唯一的,课程编号就是一个主键。
成绩表(学号、课程号、成绩)
成绩表中单独的一个属性无法唯一标识一条记录,学号和课程号的组合才能唯一标识一条记录,所以学号和课程号的属性组是一个主键。
外键
成绩表中的学号不是成绩表的主键,但它和学生表中的学号相对应,并且学生表中的学号是学生表的主键,则称成绩表中的学号是学生表的外键。同理:成绩表中的课程号是课程表的外键。
EntityFramework中的导航属性即外键。下面通过例子讲解如何使用EF的导航属性。
二、导航属性
1、新建产品分类表,语句如下:
CREATE table Category
(
CategoryId int primary key not null identity,
CategoryName varchar(64)
)
新建产品明细表,其中CategoryId是外键
CREATE TABLE [dbo].[ProductDetail](
[ProductId] [int] IDENTITY(1,1) NOT NULL,
[ProductName] [varchar](32) NULL,
[Price] [decimal](9, 2) NULL,
[CategoryId] [int] NULL,
PRIMARY KEY CLUSTERED
(
[ProductId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] GO SET ANSI_PADDING OFF
GO ALTER TABLE [dbo].[ProductDetail] WITH CHECK ADD CONSTRAINT [FK_Category] FOREIGN KEY([CategoryId])
REFERENCES [dbo].[Category] ([CategoryId])
GO ALTER TABLE [dbo].[ProductDetail] CHECK CONSTRAINT [FK_Category]
GO
分别往Category表和ProductDetail表中插入一些测试数据:
--Category表插入数据
INSERT INTO Category (CategoryName)
select '电子产品' union
SELECT '家用电器' UNION
SELECT '图书' --ProductDetail表插入数据
INSERT INTO ProductDetail (ProductName,Price,CategoryId)
SELECT '苹果6s手机',5633,1 UNION
SELECT 'Dell电脑',6998,1 UNION
SELECT '佳能相机',5633,1 UNION
SELECT '海尔洗衣机',1234,2 UNION
SELECT '格力空调',2344,2 UNION
SELECT '美的冰箱',3218,2 UNION
SELECT '白鹿原',342,3 UNION
SELECT 'C#高级编程(第十版)',145,3 UNION
SELECT '平凡的世界',231,3
2、使用DataBase First模式生成edmx文件,然后查看Category表和ProductDetail表相对应的实体的定义
Category表定义:
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码已从模板生成。
//
// 手动更改此文件可能导致应用程序出现意外的行为。
// 如果重新生成代码,将覆盖对此文件的手动更改。
// </auto-generated>
//------------------------------------------------------------------------------ namespace EFNavigateDemo
{
using System;
using System.Collections.Generic; public partial class Category
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Category()
{
this.ProductDetails = new HashSet<ProductDetail>();
} public int CategoryId { get; set; }
public string CategoryName { get; set; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ProductDetail> ProductDetails { get; set; }
}
}
Category实体类中有一个ProductDetail类型的集合属性,表示是导航属性。
实体类型包含其他实体类型(POCO类)的属性(也可称为导航属性),且同时满足如下条件即可实现延迟加载:
1.该属性的类型必须为public且不能为Sealed。
2.属性标记为Virtual。
ProductDetail实体类定义如下:
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码已从模板生成。
//
// 手动更改此文件可能导致应用程序出现意外的行为。
// 如果重新生成代码,将覆盖对此文件的手动更改。
// </auto-generated>
//------------------------------------------------------------------------------ namespace EFNavigateDemo
{
using System;
using System.Collections.Generic; public partial class ProductDetail
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public Nullable<decimal> Price { get; set; }
public Nullable<int> CategoryId { get; set; } public virtual Category Category { get; set; }
}
}
ProductDetail类里面有一个Category类型的属性。
导航属性实现延迟加载的四种方式:
1、方式一
using (var dbContext = new CategoryEntities())
{
dbContext.Configuration.LazyLoadingEnabled = true; // 默认是true,针对导航属性
var categoryList = dbContext.Set<Category>().Where(p => p.CategoryId == );
// 只会在数据库里面查询Category表,不会查询ProductDetail表
foreach(var category in categoryList)
{
Console.WriteLine("CategoryId:"+category.CategoryId+ ",CategoryName:"+category.CategoryName);
// 这时才会去数据库查询ProductDetail表
foreach (var product in category.ProductDetails)
{
Console.WriteLine("ProductName:"+product.ProductName);
}
}
}
分别在两处foreach循环的地方添加断点,然后运行程序查看数据库执行的SQL语句情况:
执行到断点1时:

这时查看数据库监控:

继续执行到断点2:

这时在查看数据库监控:

会发现遍历ProductDetails属性时也会查询ProductDetail表。
2、方式二
using (var dbContext = new CategoryEntities())
{
dbContext.Configuration.LazyLoadingEnabled = false; // 不延迟加载,不会再次查询了
var categoryList = dbContext.Set<Category>().Where(p => p.CategoryId == );
// 只会在数据库里面查询Category表,不会查询ProductDetail表
foreach (var category in categoryList)
{
Console.WriteLine("CategoryId:" + category.CategoryId + ",CategoryName:" + category.CategoryName);
// 这时不会去数据库查询了,所以用户全是空的
foreach (var product in category.ProductDetails)
{
Console.WriteLine("ProductName:" + product.ProductName);
}
}
}
这时还是采用和上面一样的方法加入断点,只需要查看第二次循环时的数据库监控情况即可:

从上面的截图中看出,如果LazyLoadingEnabled设置为false,将不会再查询ProductDetail表的数据了。
3、方式三
// 显示加载
using (var dbContext = new CategoryEntities())
{
// 不延迟加载,指定Include,一次性加载主表和从表的所有数据
var categoryList = dbContext.Set<Category>().Include("ProductDetails").Where(p => p.CategoryId == );
foreach (var category in categoryList)
{
Console.WriteLine("CategoryId:" + category.CategoryId + ",CategoryName:" + category.CategoryName);
// 不会再查询
foreach (var product in category.ProductDetails)
{
Console.WriteLine("ProductName:" + product.ProductName);
}
}
}
使用Include()方法会一次性加载所有的数据:

4、方式四
在上面的方式2中把LazyLoadingEnabled设置为false以后就不会再查询ProductDetail表的数据了,这时如果想要查询ProductDetail表的数据该怎么办呢?这时可以使用手动加载,代码如下:
//LoadProperty 手动加载
using (var dbContext = new CategoryEntities())
{
dbContext.Configuration.LazyLoadingEnabled = false; // 不延迟加载,不会再次查询了
var categoryList = dbContext.Set<Category>().Where(p => p.CategoryId == );
foreach (var category in categoryList)
{
Console.WriteLine("CategoryId:" + category.CategoryId + ",CategoryName:" + category.CategoryName);
dbContext.Entry<Category>(category).Collection(p => p.ProductDetails).Load();// 集合显示加载
foreach (var product in category.ProductDetails)
{
Console.WriteLine("ProductName:" + product.ProductName);
}
}
}
添加断点:

查看数据库监控:

5、插入数据
对于Category和ProductDetail表如何同时插入数据?先看下面的一段代码:
using (var dbContext = new CategoryEntities())
{
using (TransactionScope trans = new TransactionScope())
{
Category category = new Category()
{
CategoryName = "自行车"
};
dbContext.Categories.Add(category);
dbContext.SaveChanges();//category.CategoryId赋值了
ProductDetail product = new ProductDetail()
{
ProductName = "美利达",
Price = ,
CategoryId = category.CategoryId
}; dbContext.ProductDetails.Add(product);
dbContext.SaveChanges();
trans.Complete();//提交事务
}
}
在第一次SaveChanges()后面的一行代码加断点,查看Category信息:

可以看到这是CategoryId已经有值了,查询数据库ProductDetail表:

这时Product的信息已经插入到数据库中了,而且CategordId也是上面生成的CategoryId。
但是这样会导致一种问题存在:如果第一次SaveChanges()成功,第二次SaveChanges()之前报错了,但是程序已经不能回滚了,这样就会导致数据不一致了。使用下面的代码进行优化:
using (var dbContext = new CategoryEntities())
{
using (TransactionScope trans = new TransactionScope())
{
Category category = new Category()
{
CategoryName = "汽车"
}; ProductDetail product = new ProductDetail()
{
ProductName = "上海大众",
Price = ,
CategoryId = category.CategoryId
}; category.ProductDetails = new List<ProductDetail>() { product};
dbContext.Categories.Add(category);
dbContext.SaveChanges();
trans.Complete();//提交事务
}
}
经过这样修改以后可以保证数据的一致性了。这是情况只适合有导航属性的。
示例代码下载地址:https://pan.baidu.com/s/1swge4txIlbBuSgs9GspC4g
Entity Framework应用:导航属性的更多相关文章
- Entity Framework Core导航属性加载问题
前言 今天下午在开发的时候发现EF Core实体模型中的导航属性为 null,经排查既不是没有加 virtual 关键字,也不是外键关系映射错误. 解决方法 通过查询官网文档,发现,原因在于EF Co ...
- 关闭Entity Framework的导航属性
public RIS30Entities() : base("name=RIS30Entities") { this.Configuration.ProxyCreationEnab ...
- Entity Framework Core 导航属性 加载数据
Loading Related Data https://docs.microsoft.com/en-us/ef/core/querying/related-data Eager loading me ...
- Entity Framework Code First属性映射约定 转载https://www.cnblogs.com/libingql/p/3352058.html
Entity Framework Code First属性映射约定 Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Flue ...
- Entity Framework Code First属性映射约定
Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...
- 补习知识:Entity Framework Code First属性映射约定
Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...
- 【EF】Entity Framework实现属性映射约定
Entity Framework Code First属性映射约定中“约定”一词,在原文版中为“Convention”,翻译成约定或许有些不好理解,这也是网上比较大多数的翻译,我们就当这是Entity ...
- Entity Framework实现属性映射约定
Entity Framework Code First属性映射约定中“约定”一词,在原文版中为“Convention”,翻译成约定或许有些不好理解,这也是网上比较大多数的翻译,我们就当这是Entity ...
- Entity Framework Core 1.1 Preview 1 简介
实体框架核心(EF Core)是Entity Framework的一个轻量级,可扩展和跨平台版本. 10月25日,Entity Framework Core 1.1 Preview 1发布了. 升级到 ...
- Working with Data » 使用Visual Studio开发ASP.NET Core MVC and Entity Framework Core初学者教程
原文地址:https://docs.asp.net/en/latest/data/ef-mvc/intro.html The Contoso University sample web applica ...
随机推荐
- U811.1接口EAI系列之一-通用访问EAI方法--统一调用EAI公共方法--VB语言
1.现在做的项目是关于业务系统与U811.1的接口项目. 2.那么就需要调整通过EAI调用生成U8业务单据. 3.下面就一个通用的向U8-EAI传递XML的通用方法 4.肯定有人会问怎么还用VB调用呢 ...
- ext: gridpanel中的点击事件的参数是什么意思?
listeners: { // 当用户单击列表项时激发该函数 itemclick: function(view, record, item, index, evt) //① ...
- 微服务,ApiGateway 与 Kong
一. 微服务 二. Api Gateway 三. Kong 的使用 一. 微服务 对于一些传统的 大型项目,传统的方式会有一些缺陷,比如说 新人熟悉系统成本高(因为整个系统作为一个整体,彼此会有一定的 ...
- [IOS]开源库RegexKitLite正则表达式的使用
1.去RegexKitLite下载类库,解压出来会有一个例子包及2个文件,其实用到的就这2个文件,添加到工程中. 2.工程中添加libicucore.dylib frameworks. 友情提醒:一般 ...
- 初学FPGA
刚开始感觉FPGA不过也就是和51,ARM单片机那样写写程序就完事了,现在看来根本不是那么回事.从夏宇闻老师的Verilog HDL,黑金教程开始学起,但是感觉看到黑金时序篇时感觉少点什么,原来是缺少 ...
- 顺时针旋转打印n阶矩阵(内测第0届第4题)
题目要求 问题描述:顺时针旋转打印n阶矩阵 样例输入:4 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 7 样例输出:1 2 ...
- 详细介绍Base64的编码转换方式
下面,详细介绍Base64的编码转换方式. 所谓Base64,就是说选出64个字符----小写字母a-z.大写字母A-Z.数字0-9.符号"+"."/"(再加上 ...
- python-enumerate方法
enumerate方法用于循环list或tuple,循环的时候可以得到索引值和当前索引的对象: letters = ['a', 'b', 'c', 'd', 'e'] for i, letter in ...
- angular指令中使用ngModelController
在这篇文章中 angular学习笔记(三十)-指令(10)-require和controller 说到了通过require属性和controller参数来让指令与指令之间互相交互. 本篇主要介绍的是指 ...
- [Windows Azure] Create and use a reporting service in Windows Azure SQL Reporting
Create and use a reporting service in Windows Azure SQL Reporting In this tutorial you will learn ab ...