Entity Framework Core 3.1 入门(四)增删改查
此入门教程是记录下方参考资料视频的过程,本例基于Entity Framework Core 3.1
开发工具:Visual Studio 2019
目录
Entity Framework Core 3.1 入门(八)在 ASP.NET Core 中配置 Entity Framework Core
控制台输出执行的SQL语句
- Demo.Data 项目下使用 NuGet 安装 Microsoft.Extensions.Logging.Console
- DbContext中添加方法
public static readonly ILoggerFactory CnosoleLoggerFactory = LoggerFactory.Create(builder =>
{
//添加过滤,只输出数据库命令
builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
});
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//UseLoggerFactory(),添加日志功能
//EnableSensitiveDataLogging(),生成的SQL语句显示参数实际的值
optionsBuilder.UseLoggerFactory(CnosoleLoggerFactory)
.EnableSensitiveDataLogging();
}
添加
- 添加一个数据,直接上代码
public static void Func_01()
{
//使用完调用Dispose方法清理资源
//C# 8.0的语法,using不需要加小括号
using var context = new DemoContext();
var serieA = new League
{
Country = "Italy",
Name = "Serie A"
};
//执行完Add方法后,仍然没有与数据库进行交互
context.Leagues.Add(serieA);
//调用此处执行SQL语句
//SaveChanges()方法会检查所有追踪对象的状态
//然后在同一个事务中对增删改的变化执行SQL语句
//一个失败就整体回滚
//执行成功会返回影响的行数
var count = context.SaveChanges();
Console.WriteLine("==================");
Console.WriteLine(count);
}
- 添加多个相同类型的数据
public static void Func_02()
{
using var context = new DemoContext();
var serieB = new League
{
Country = "Italy",
Name = "Serie B"
};
var serieC = new League
{
Country = "Italy",
Name = "Serie C"
};
//两种形式
context.Leagues.AddRange(serieB, serieC);
context.Leagues.AddRange(new List<League> { serieB, serieC });
var count = context.SaveChanges();
Console.WriteLine("==================");
Console.WriteLine(count);
}
- 添加多个不同类型的数据
public static void Func_03()
{
using var context = new DemoContext();
var serieA = context.Leagues.Single(x => x.Name == "Serie A");
var serieB = new League
{
Country = "Italy",
Name = "Serie B"
};
var serieC = new League
{
Country = "Italy",
Name = "Serie C"
};
var milan = new Club
{
Name = "AC Milan",
City = "Milan",
DateOfEstablishment = new DateTime(1899, 12, 16),
League = serieA
};
context.AddRange(serieB, serieC, milan);
//context也可以添加一个数据
//context.Add();
var count = context.SaveChanges();
Console.WriteLine("==================");
Console.WriteLine(count);
}
查询
Linq 查询
public static void Func_04()
{
//Linq查询有两种方式
//1、Linq中的方法去查询
//2、Linq语句查询
using var context = new DemoContext();
//查询所有结果
//调用ToList()方法时才会访问数据库
//var leagues = context.Leagues.ToList();
//查询所有结果,条件查询
//var leagues = context.Leagues.Where(x => x.Country == "Italy").ToList();
//Linq语句,查询所有结果
//var league = (from lg in context.Leagues select lg).ToList();
//Linq语句,查询所有结果,条件查询
//var league = (from lg in context.Leagues where lg.Country == "Italy" select lg).ToList();
//如果查询的条件像上面一样写死,那么(生成)执行的SQL语句就是写死的
//如果用变量作为查询条件,那么就会使用参数查询
//var italy = "Italy";
//var league = context.Leagues.Where(x => x.Country == italy).ToList();
//模糊查询,条件为 %e%,两种形式
//var league = context.Leagues.Where(x => x.Country.Contains("e")).ToList();
//var league = context.Leagues.Where(x => EF.Functions.Like(x.Country, "%e%")).ToList();
//遇到foreach也会去查询数据库,保持数据库连接直到循环结束
//如果循环体是耗时操作,可能导致数据冲突,所以尽量不要这么写
//建议先ToList(),再对查询的结果进行操作
//foreach (var item in context.Leagues)
//{
// Console.WriteLine(item.Name);
//}
//针对主键进行查询,两种形式
//只会生成一次SQL语句
//因为context会追踪查询出来的数据,Find()再执行时,如果context能在内存里找到数据,那么就不会去数据库查询
//只有Find()方法会这样执行
//var first = context.Leagues.SingleOrDefault(x => x.Id == 2);
//var one = context.Leagues.Find(2);
//Console.WriteLine(first?.Name);
//Console.WriteLine(one?.Name);
//Last()和LastOrDeafult()方法查询必须排序
//OrderBy(),正向排序
//OrderByDescending(),方向排序
//var last = context.Leagues.OrderBy(x => x.Id).LastOrDefault(x => x.Name.Contains("e"));
}
常用的查询方法(看帮助文档):
ToList(),返回集合
First(),返回(符合条件的第)一个数据,必须有一个数据,没有数据就会报错,方法中可以直接写条件,不需要Where()
FirstOrDefaule(),返回(符合条件的第)一个数据,可以没有数据,方法中可以直接写条件,不需要Where()
Single(),返回序列的唯一元素;如果该序列并非恰好包含一个元素,则会引发异常。
SingleOrDefault(),返回序列中的唯一元素;如果该序列为空,则返回默认值;如果该序列包含多个元素,此方法将引发异常。
Last()
LastOrDefaule()
实际应用中,经常使用OrDefault的方法
Find(),根据主键查找,属于DbSet的方法
Count()
LongCount()
Min()
Max()
Average()
Sum()
删除
直接上代码
public static void Func_05()
{
using var context = new DemoContext();
//Delete
//只能删除被context追踪的数据,只有先查询出来才能被追踪
var milan = context.Clubs.Single(x => x.Name == "AC Milan");
//调用删除方法,四种
//context.Clubs.Remove(milan);
//context.Remove(milan);
//删除多个结果
//context.Clubs.RemoveRange(milan, milan);
//context.RemoveRange(milan, milan);
var count = context.SaveChanges();
Console.WriteLine("==============");
Console.WriteLine(count);
}
修改
- 修改数据
public static void Func_06()
{
using var context = new DemoContext();
//只能修改被context追踪的数据,只有先查询出来才能被追踪
var league = context.Leagues.First();
//context知道league.Name被修改,设置为修改状态
league.Name += "~~";
//执行对应的SQL语句
var count = context.SaveChanges();
Console.WriteLine("==============");
Console.WriteLine(count);
}
- 修改多个数据
public static void Func_07()
{
using var context = new DemoContext();
//Skip(),跳过序列中指定数量的元素
//Take(),从序列的开头返回指定数量的连续元素。
var leagues = context.Leagues.Skip(1).Take(3).ToList();
//不符合实际应用
foreach (var league in leagues)
{
league.Name += "~~";
}
var count = context.SaveChanges();
Console.WriteLine("==============");
Console.WriteLine(count);
}
- 修改数据,模拟前端数据传递到后端(AsNoTracking(),数据离线)
public static void Func_08()
{
using var context = new DemoContext();
//AsNoTracking(),不对数据进行变化追踪
//在一些情况下,我们只需要查询返回一个只读的数据记录,而不会对数据记录进行任何的修改。
//使用AsNoTracking()会有显着的性能提升
var league = context.Leagues.AsNoTracking().First();
league.Name += "++";
//Update(),将数据添加到追踪范围内,未追踪的数据一定要执行Update(),否则会报错
//也有UpdateRange(),context也有
//使用Update(),会将所有属性都设置为修改状态,也可以做到只修改一个属性
context.Leagues.Update(league);
//设置状态,实现部分字段修改
context.Entry(league).Property("Id").IsModified = false;
var count = context.SaveChanges();
Console.WriteLine("==============");
Console.WriteLine(count);
}
设置全局不追踪
在DbContext的构造器中配置,本案例不使用。
public DemoContext(DbContextOptions<DemoContext> options):base(options)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}