一篇文章告诉你如何使用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. ...
随机推荐
- Bugzilla 系统企业应用案例
目录 一. 概述: - 4 - 二. 目的 - 4 - 三. 执行原则 - 4 - 四. 管理办法 - 4 - 五. BUG处理流程图 - 5 - 六. 主要职责 - 6 - 七. 需求类问题处理 - ...
- oracle中的类似BIN$MrkCYT9eTTK+0sStMwn7+Q==$0的表的作用
https://www.2cto.com/database/201211/166482.html https://docs.oracle.com/cd/E11882_01/server.112/e40 ...
- 夜话JAVA设计模式之策略模式
策略模式 定义了算法簇,分别封装起来,让他们之间可以互相替换,让算法簇的变化独立于使用算法的客户.设计原则1 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在 ...
- Ubuntu下非常规方法安装绿色软件(压缩包)
继上一篇http://www.cnblogs.com/EasonJim/p/7117567.html文章中说的常规方式安装的软件,都会自动在命令行及Dash Home中体现. 但是如果是使用压缩包进行 ...
- 使用Vundle管理配置Vim的插件
1.介绍: 安装需要Git,触发git clone,默认将每一个指定特定格式插件的仓库复制到~/.vim/bundle/. 搜索需要Curl支持. Windows用户请直接访问Windows setu ...
- C#中方法的详解
访问修饰符 修饰符 返回值类型 方法名(参数列表){ 语句块;} 访问修饰符:所有类成员访问修饰符都可以使用,如果省略访问修饰符,默认是private. 修饰符:在定义方法时修饰符包括virtual( ...
- Spring MVC JSON自己定义类型转换(续)
前面提到了两种转换类型的方法(Spring MVC JSON自己定义类型转换),这里针对Json转换提供一种更简便的方法. 通过配置全局的日期转换来避免使用麻烦的注解. 首先用到了一个简单的日期工具类 ...
- sql 分组取每组的前n条或每组的n%(百分之n)的数据
sql 分组取每组的前n条或每组的n%(百分之n)的数据 sql keyword: SELECT * ,ROW_NUMBER() OVER(partition by b.UserID order by ...
- UIView的层介绍
UIView的层介绍 subview在西安市到屏幕上时,是位于superview上层的. 同一个view的subview时依照增加的顺序显示相应层的.越晚增加的subview显示在越上层,反之也是如此 ...
- 李洪强经典面试题30-iOS应用性能调优的25个建议和技巧
iOS应用性能调优的25个建议和技巧 本文来自iOS Tutorial Team 的 Marcelo Fabri,他是Movile的一名 iOS 程序员.这是他的个人网站:http://www.mar ...