一篇文章告诉你如何使用EF CodeFirst做增删改查
一、修改数据
其实修改涉及的内容挺多的,是相对于其他操作来说比较繁琐。也是本文的重头戏。
虽然都是基础内容,但是也是值得细细品味的。
1、最简单直接的修改数据就是从数据库里检索出数据修改相应的字段即可
数据表:

Code:
using (var db = new ApplicationDbContext())
{
ApplicationDbContext.Two old_entity = db.Twos.Single(t => t.Text == "90");
old_entity.Text = "update";
db.SaveChanges();
}
结果:

2、直接更新不需要检索出数据
数据表:

Code:
通过修改实体的状态来实现
ApplicationDbContext.Two entity = new ApplicationDbContext.Two() { TwoId = 1, Text = "update" };
using (var db = new ApplicationDbContext())
{
db.Entry(entity).State = EntityState.Modified;
db.SaveChanges();
}
结果:

这个方式有个不好地方,图中很明显的标注出来了,就是会一股脑更新全部字段,没有值就被更新成NULL。
3、指定字段更新
数据表:

Code:
修改实体状态为Unchanged.
设置指定属性的状态为修改
ApplicationDbContext.Two entity = new ApplicationDbContext.Two() { TwoId = 1, Text = "update" };
using (var db = new ApplicationDbContext())
{
db.Entry(entity).State = EntityState.Unchanged;
db.Entry(entity).Property("Text").IsModified = true;
db.SaveChanges();
}
结果:

4、禁用验证实体的有效性
有时我们在更新时有些字段是不更新的,但是这些字段又可能会有一些验证特性,比如不可以为空,这样我们在保存时会因实体验证不通过而报错。
实体:
Uu是必须的,不可为空
public class MyFirst
{
public int MyFirstId { get; set; }
public string Text { get; set; }
[Required]
public string Uu { get; set; }
}
Code:
ApplicationDbContext.MyFirst entity = new ApplicationDbContext.MyFirst() { MyFirstId = 1, Text = "update" };
using (var db = new ApplicationDbContext())
{
db.Entry(entity).State = EntityState.Unchanged;
db.Entry(entity).Property("Text").IsModified = true;
db.SaveChanges();
}
结果:
因没通过Uu的特性的验证而报错了,我们只更新了Text,没有给Uu赋值,但是Uu又是不可以为空的

只要我们在保存前取消这种验证就可以了,在保存后恢复验证
修改后的Code:
ApplicationDbContext.MyFirst entity = new ApplicationDbContext.MyFirst() { MyFirstId = 1, Text = "update" };
using (var db = new ApplicationDbContext())
{
db.Entry(entity).State = EntityState.Unchanged;
db.Entry(entity).Property("Text").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
db.Configuration.ValidateOnSaveEnabled = true;
}
数据表:

结果:

5、在不同的上下文中更新数据
情况一
Code:
ApplicationDbContext.Two entity;
using(var db1=new ApplicationDbContext())
{
entity = db1.Twos.Single(m => m.Text == "90");
}
entity.Text = "update";
using (var db = new ApplicationDbContext())
{
DbEntityEntry entry = db.Entry(entity);
entry.State = EntityState.Modified;
db.SaveChanges();
}
在这种场景下更新有一个问题是,数据会全部更新,并不是我们指定的Text更新,原因是不在一个上下文中,在另一个上下文中没有上一个上下文的状态,所以更新时没法判定就全部更新了。
数据图:

在db1获取数据时设置一个断点,手动修改数据库后的数据表:

结果数据:
会发现数据都被更新了,对比第二张图

情况二
Code:
ApplicationDbContext.Two entity;
using(var db1=new ApplicationDbContext())
{
entity = db1.Twos.Single(m => m.Text == "90");
}
entity.Text = "update";
using (var db = new ApplicationDbContext())
{
ApplicationDbContext.Two entity1 = db.Twos.Single(m => m.Text == "90");
DbEntityEntry entry = db.Entry(entity);
entry.State = EntityState.Modified;
db.SaveChanges();
}
在这种场景下,与上面的区别是第二个上下文也检索了数据,这种情况就是报错,原因就是同一个上下文中不能出现主键值相同的实体。db检索数据时存在了一个实体在上下文中,又要把db1检索出的实体添加到db上下文中,一但主键值相同就会报错

解决上述情况:
关键点是entry.CurrentValues.SetValues(entity);设置当前上下文实体的值。
Code:
ApplicationDbContext.Two entity;
using(var db1=new ApplicationDbContext())
{
entity = db1.Twos.Single(m => m.Text == "90");
}
entity.Text = "update";
using (var db = new ApplicationDbContext())
{
ApplicationDbContext.Two entity1 = db.Twos.Single(m => m.Text == "90");
DbEntityEntry entry = db.Entry(entity1);
entry.CurrentValues.SetValues(entity);
db.SaveChanges();
}
数据图:

在db获取数据时设置一个断点,手动修改数据库后的数据表:

结果数据:
会发现只有指定的数据更新了,对比第二张图

6、同一个上下文中有实体存在的情况下用外部数据更新
Code:
ApplicationDbContext.Two entity= new ApplicationDbContext.Two() { TwoId = 1, Text = "update" };
using (var db = new ApplicationDbContext())
{
ApplicationDbContext.Two entity1 = db.Twos.Single(m => m.Text == "90");
DbEntityEntry entry = db.Entry(entity);
entry.State = EntityState.Modified;
db.SaveChanges();
}
会报错,原因和上面情况一样,上下文中出现了主键一样的多个实体,本来有一个实体了,又加载了一个主键一样的实体,就报错了。

用上述方法解决试试看
Code:
ApplicationDbContext.Two entity= new ApplicationDbContext.Two() { TwoId = 1, Text = "update" };
using (var db = new ApplicationDbContext())
{
ApplicationDbContext.Two entity1 = db.Twos.Single(m => m.Text == "90");
DbEntityEntry entry = db.Entry(entity1);
entry.CurrentValues.SetValues(entity);
db.SaveChanges();
}
没有报错,成功更新数据,但是问题是全部数据都被更新,不是我们指定的数据更新
结果数据:

尝试使用指定属性更新:
结果你会发现数据没有发生任何变化,原因是entry.State = EntityState.Unchanged;把ntry.CurrentValues.SetValues(entity);改变得值有还原回去了。
ApplicationDbContext.Two entity= new ApplicationDbContext.Two() { TwoId = 1, Text = "update" };
using (var db = new ApplicationDbContext())
{
ApplicationDbContext.Two entity1 = db.Twos.Single(m => m.Text == "90");
DbEntityEntry entry = db.Entry(entity1);
entry.CurrentValues.SetValues(entity);
entry.State = EntityState.Unchanged;
entry.Property("Text").IsModified = true;
db.SaveChanges();
}
解决上述问题:
如果要解决上述问题应该使用ObjectContext的ObjectStateEntry设置当前值,状态,以及指定修改的属性
Code:
ApplicationDbContext.Two entity= new ApplicationDbContext.Two() { TwoId = 1, Text = "update" };
using (var db = new ApplicationDbContext())
{
ApplicationDbContext.Two entity1 = db.Twos.Single(m => m.Text == "90");
ObjectContext objectContext = ((IObjectContextAdapter)db).ObjectContext;
ObjectStateEntry entry = objectContext.ObjectStateManager.GetObjectStateEntry(entity1);
entry.ApplyCurrentValues(entity);
entry.ChangeState(EntityState.Unchanged);
entry.SetModifiedProperty("Text");
db.SaveChanges();
}
结果数据:

对上上述更新方式做一个封装
public static class Test
{
//指定更新
public static void Update<TEntity>(this DbContext dbcontext,
Expression<Func<TEntity, object>> propertyExpression,
params TEntity[] entities) where TEntity : EntityBase
{
if (propertyExpression == null)
{
throw new ArgumentException("propertyExpression");
}
if (entities == null)
{
throw new ArgumentException("entities");
}
ReadOnlyCollection<MemberInfo> memberInfos = ((dynamic)propertyExpression.Body).Members;
foreach (TEntity entity in entities)
{
try
{
DbEntityEntry<TEntity> entry = dbcontext.Entry(entity);
entry.State = EntityState.Unchanged;
foreach (var memberInfo in memberInfos)
{
entry.Property(memberInfo.Name).IsModified = true;
}
}
catch
{
TEntity originalEntity = dbcontext.Set<TEntity>().Single(m => m.Id == entity.Id);
ObjectContext objectContext = ((IObjectContextAdapter)dbcontext).ObjectContext;
ObjectStateEntry objectEntry = objectContext.ObjectStateManager.GetObjectStateEntry(originalEntity);
objectEntry.ApplyCurrentValues(entity);
objectEntry.ChangeState(EntityState.Unchanged);
foreach (var memberInfo in memberInfos)
{
objectEntry.SetModifiedProperty(memberInfo.Name);
}
}
}
}
//提交保存
public static int SaveChanges(this DbContext dbContext, bool validateOnSaveEnabled)
{
bool isReturn = dbContext.Configuration.ValidateOnSaveEnabled != validateOnSaveEnabled;
try
{
dbContext.Configuration.ValidateOnSaveEnabled = validateOnSaveEnabled;
return dbContext.SaveChanges();
}
finally
{
if (isReturn)
{
dbContext.Configuration.ValidateOnSaveEnabled = !validateOnSaveEnabled;
}
}
}
//全部更新
public static void Update1<TEntity>(this DbContext dbContext, params TEntity[] entities) where TEntity : EntityBase
{
if (dbContext == null) throw new ArgumentException("dbContext");
if (entities == null) throw new ArgumentException("entities");
foreach(TEntity entity in entities)
{
DbSet<TEntity> dbSet = dbContext.Set<TEntity>();
try
{
DbEntityEntry<TEntity> entry = dbContext.Entry(entity);
if (entry.State == EntityState.Detached)
{
entry.State = EntityState.Modified;
}
}
catch
{
TEntity oldEntity = dbSet.Find(entity.Id);
dbContext.Entry(oldEntity).CurrentValues.SetValues(entity);
}
}
}
}
public class EntityBase
{
public int Id { get; set; }
}
public class Three : EntityBase
{
public string Text { get; set; }
public string Uu { get; set; }
}
封装后的使用实例:
pplicationDbContext.Three entity= new ApplicationDbContext.Three() { Id = 1, Text = "update" };
using (var db = new ApplicationDbContext())
{
ApplicationDbContext.Three entity1 = db.Threes.Single(m => m.Text == "90");
db.Update<ApplicationDbContext.Three>(m => new { m.Text }, entity);
db.SaveChanges();
}
至此更新部分已经全部完毕
Ps:其实扩展方法update有个坑,就是一但出现并发时就会报错走catch,而其实不是想要的结果,这是个忽略的地方,并发应该抛出错误,用catch是忽略了并发的情况...额...
二、添加数据
简单明了没有啥好说的
Code:
ApplicationDbContext.Three entity = new ApplicationDbContext.Three() { Text = "我哦哦哦" };
using (var db = new ApplicationDbContext())
{
db.Threes.Add(entity);
db.SaveChanges();
}
数据图:

三、删除数据
简单明了没啥好说的
通过Attach把实体加载到上下文,关键点就是实体要存在于上下文中
Code:
ApplicationDbContext.Three entity = new ApplicationDbContext.Three() { Id=2 };
using (var db = new ApplicationDbContext())
{
db.Threes.Attach(entity);
db.Threes.Remove(entity);
db.SaveChanges();
}
Code:
ApplicationDbContext.Three entity = new ApplicationDbContext.Three() { Id = 3 };
using (var db = new ApplicationDbContext())
{
db.Entry(entity).State = EntityState.Deleted;
db.SaveChanges();
}
四、查询
1、简单查询
int[] scores = { 90, 71, 82, 93, 75, 82 };
IEnumerable<int> scoreQuery =
from score in scores
where score > 80
orderby score descending
select score;
foreach (int testScore in scoreQuery)
{
Console.WriteLine(testScore);
}
//输出结果:93 90 82 82
2、分组查询
var queryLastNames =
from student in students
group student by student.LastName into newGroup
orderby newGroup.Key
select newGroup;
3、内联查询
var query = from person in people
join pet in pets on person equals pet.Owner
select new { OwnerName = person.FirstName, PetName = pet.Name };
4、分组连接
结果是:一个名字对应一个集合
var query = from person in people
join pet in pets on person equals pet.Owner into gj
select new { OwnerName = person.FirstName, Pets = gj };
5、外联查询
var query = from person in people
join pet in pets on person equals pet.Owner into gj
from subpet in gj.DefaultIfEmpty()
select new { person.FirstName, PetName = (subpet == null ? String.Empty : subpet.Name) };
6、在查询中处理null
var query1 =
from c in categories
where c != null
join p in products on c.ID equals
(p == null ? null : p.CategoryID)
select new { Category = c.Name, Name = p.Name };
var query =
from o in db.Orders
join e in db.Employees
on o.EmployeeID equals (int?)e.EmployeeID
select new { o.OrderID, e.FirstName };
7、在查询中处理异常
IEnumerable<int> dataSource;
try
{
dataSource = GetData();
}
catch (InvalidOperationException)
{
Console.WriteLine("Invalid operation");
goto Exit;
}
var query = from i in dataSource
select i * i;
foreach (var i in query)
Console.WriteLine(i.ToString());
Exit:
Console.WriteLine("Press any key to exit");
string[] files = { "fileA.txt", "fileB.txt", "fileC.txt" };
var exceptionDemoQuery =
from file in files
let n = SomeMethodThatMightThrow(file)
select n;
try
{
foreach (var item in exceptionDemoQuery)
{
Console.WriteLine("Processing {0}", item);
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.Message);
}
更多关于查询请查考MSDN,里面很详细
https://msdn.microsoft.com/zh-cn/library/bb384065.aspx
一篇文章告诉你如何使用EF CodeFirst做增删改查的更多相关文章
- Entity - 使用EF框架进行增删改查 - 模型先行
模型先行:先创建数据库实体模型,然后再进行数据库的增删改查. 基本步骤是不变的,可参照 <Entity - 使用EF框架进行增删改查 - 数据库先行> 其中的不同是,在创建数据库实体模型的 ...
- MVC学习-用EF做增删改查
在做增删改查先,先介绍几个知识点: 1.代理类 在将对象方法EF数据上下文时,EF会为该对象封装 一个代理类对象, 同时为该对象的每一个属性添加一个标志:unchanged, 当对该对象某个属性进行操 ...
- 用DBContext (EF) 实现通用增删改查的REST方法
我们用ADO.NET Entity Data Model来生成实体类后,一般都会对这些类进行基本的增删改查操作,如果每个类都要写这些基本的方法,实在太乏味了.下面就是通过step by step的方式 ...
- C# 数据操作系列 - 8. EF Core的增删改查
0.前言 到目前为止,我们看了一下如何声明EF Core的初步使用,也整体的看了下EF Core的映射关系配置以及导航属性的配置. 这一篇,我带大家分享一下,我在工作中需要的EF Core的用法. 1 ...
- Entity - 使用EF框架进行增删改查 - 数据库先行
数据库先行:先创建数据库,然后进行增删查该操作. 要操作的表结构(表名:Tb_Category): 创建一个控制台程序: 添加一个ADO.NET实体数据模型: 1.对控制台程序右键 2.选择ADO.N ...
- .Net EF框架的增删改查
创建上下文对象: WordBoradEntities db = new WordBoradEntities(); 添加: //1.1创建实体对象 User uObj = new User() { uN ...
- VS2012里面使用EF框架的增删改查和分页的方法
public class BaseRepository<T> where T : class { //实例化EF框架 DataModelContainer ...
- .net EF框架-实现增删改查
声明一个EF上下文对象 Model dbContext = new Model(); 添加操作(向表中插入一条数据) //声明一个表的实体 Contact contact = new Contact( ...
- 二、MVC3+EF单表增删改查
document 表为例 写入静态类 NorthwindDataProvider: Controller可直接调用:如 //获取document表全部数据 NorthwindDataProvider. ...
随机推荐
- Linux command2
. CentOS 想查看哪个port开了,却提示命令无效 # yum -y install net-tools 2. How to install "wget" command i ...
- POJ 1328 Radar Installation 贪心算法
Description Assume the coasting is an infinite straight line. Land is in one side of coasting, sea i ...
- T1683 车厢重组 codevs
http://codevs.cn/problem/1683/ 时间限制: 1 s 空间限制: 1000 KB 题目等级 : 白银 Silver 题目描述 Description 在一个旧式的火车 ...
- Eureka集群的那些坑
今天遇到一个Eureka集群的一个坑. 问题现场类似是这样的:两台Eureka组成的服务注册中心集群,两台服务提供方server1.server2,两个服务调用方client1.client2. 正常 ...
- JS实现转动随机数抽奖的特效代码
JS实现转动随机数抽奖的特效代码 大家都玩过抽奖游戏,或者梦想抽到大奖吧.可是有没有想过抽奖游戏是怎么实现的呐?今天就给大家分享一款转动随机数抽奖的JS特效代码. 实现代码例如以下 <!Doct ...
- Xsolla带你走进印度移动游戏市场
作为世界上人口第二大国的印度.随着移动时代的到来,越来越多的开发人员将目光瞄准了这个国家.今天Xsolla就带领大家一起来了解这样一个新兴的移动游戏市场. 印度移动市场潜力巨大 作为世界四大宗教和数字 ...
- 已知目标qps跟并发用户数20,压测平均响应时间实例
Jmeter性能测试案例(一) 转:https://blog.csdn.net/lovesoo/article/details/78579547 测试需求:测试20个用户访问网站在负载达到30QPS时 ...
- java集合: List、Set、Map总结 + HashMap/Hashtable 差别
List:(有序,能够反复)通过下标索引 ----ArrayList 可变数组,随机查找 ----LinkedList 链表,不论什么位置插入删除快 ----Vecto ...
- 怎样对Android设备进行网络抓包
问题描写叙述: 前段时间自己的app訪问server的url总是会出现间接性失败的问题,于是和server的同事开了个会.提出了他们server存在的这个bug,我的同事自然说自己的server没问题 ...
- 怎样设置mysql远程訪问
Mysql默认是不能够通过远程机器訪问的,通过以下的配置能够开启远程訪问 在MySQL Server端: 运行mysql 命令进入mysql 命令模式, mysql> use mysql; ...