一篇文章告诉你如何使用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. ...
随机推荐
- Python学习笔记 (2)变量、常量和数据类型
变量 顾名思义,变量就是一个会变的量,用一个变量名表示,指向内存中一片区域,而指向的区域存的是什么,这个变量就是什么数据类型,和C/C++挺不一样的.变量数据类型可以通过赋值变来变去(这就叫动态语言, ...
- HBase shell的常用命令(CRUD)
@来源是传智播客hadoop的视频教程,觉得入门不错,就copy过来了 一.启动HBase: sudo -i cd /home/cx/itcast/hbase-1.2.6/bin ./start- ...
- BZOJ(5) 1083: [SCOI2005]繁忙的都市
1083: [SCOI2005]繁忙的都市 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4105 Solved: 2595[Submit][Sta ...
- 4560 NOIP2015 D2T2 子串 code vs
4560 NOIP2015 D2T2 子串 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 有两个仅包含小写 ...
- curl -O 下载文件
curl -O 下载文件 学习了:http://blog.csdn.net/wulong710/article/details/53127606 curl -O http://a.b.c/a.tar ...
- C++对象模型——继承体系下的对象构造(第五章)
5.2 继承体系下的对象构造 当定义一个object例如以下: T object; 时,实际上会发生什么事情呢?假设T有一个constructor(不论是由user提供或是由编译器合成),它会被调用. ...
- yum install -y dos2unix
yum install -y dos2unix linux 怎么把^M去掉 - CSDN博客 http://blog.csdn.net/humanof/article/details/53044217 ...
- cocos2d-x 3.0 常见问题及解决
我自己遇到的问题记录,会及时更新.希望对大家有帮助 1.打包图片生成plist后,假设在游戏中图片挨在一起可能会出现黑线,打包时将Extrude设为1就可以 2.Xcode环境下.更新资源后执行的时候 ...
- webview 播放H5视频问题 黑屏 只有声音没有画面
android 用webview 播放网络视频怎控制播放按键? 在代码中加入webview.getSettings().setJavaScriptEnabled(true);//支持jswebview ...
- P3052 [USACO12MAR]摩天大楼里的奶牛Cows in a Skyscraper 状压dp
这个状压dp其实很明显,n < 18写在前面了当然是状压.状态其实也很好想,但是有点问题,就是如何判断空间是否够大. 再单开一个g数组,存剩余空间就行了. 题干: 题目描述 A little k ...