EF Core是一个ORM(对象关系映射),它使.NET 开发人员可以直接使用.NET对象来操作数据库,消除了大部分的数据访问代码,开发者通常只需要编写对象即可。支持多种数据库引擎。
一、上下文
using Microsoft.EntityFrameworkcore; using system.collections.Generic; namespace Intro { public class DemoDbcontext : Dbcontext public Dbset<demo> demo { get; set; } }
services.AddDbContext<DemoDbContext>(opt=>opt.UseMySql("server=.;Database=demo;Uid=root;Pwd=123;Port=3306;"));
二、上下文线程问题
efcore不支持在同一个DbContext实例上运行多个并行操作,这包括异步查询的并行执行以及从多个线程进行的任何显式并发使用。 因此,始终 await 异步调用,或对并行执行的操作使用单独的 DbContext 实例。
三、EF日志记录
public class EFCoreLogger : ILogger { private readonly string categoryName; public EFCoreLogger(string categoryName) => this.categoryName = categoryName; public bool IsEnabled(LogLevel logLevel) => true; public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { //EF Core执行SQL语句时的categoryName为Microsoft.EntityFrameworkCore.Database.Command,日志级别为Information if (categoryName == "Microsoft.EntityFrameworkCore.Database.Command" && logLevel == LogLevel.Information) { var logContent = formatter(state, exception); //写入文本中 LogHelper.Log("SqlLog", "【SQL语句】:" + logContent); } } public IDisposable BeginScope<TState>(TState state) => null; }
public class EFCoreLoggerProvider : ILoggerProvider { public ILogger CreateLogger(string categoryName) => new EFCoreLogger(categoryName); public void Dispose() { } }
var loggerFactory = new LoggerFactory(); loggerFactory.AddProvider(new EFCoreLoggerProvider()); optionsBuilder.UseSqlServer(ConnectionString).UseLoggerFactory(loggerFactory).EnableSensitiveDataLogging();;
四、新增
- 新增一条
var demo = new demo{CreateTime = DateTime.Now, Name = "demo"}; await _context.demo.AddAsync(student); await _context.SaveChangesAsync();
- 级联新增
var demo = new demo { name = "demo", demo1 = new List<demo1 > { new demo1 {name1= "评论1"}, new demo1 {name1 = "评论2"}, new demo1 {name1 = "评论3"}, } }; await _context.demo .AddAsync(demo); await _context.SaveChangesAsync();
- 批量新增
var list = new List<demo>(); for (int i = 0; i < num; i++) { list.Add(new demo{ CreateTime = DateTime.Now, Name = "demo" }); } await _context.demo.AddRangeAsync(list); await _context.SaveChangesAsync();
五、查询
EF使用Linq查询数据库中的数据,使用Linq可编写强类型的查询。当命令执行时,EF先将Linq表达式转换成sql脚本,然后再提交给数据库执行。可在日志中查看生成的sql脚本。
await _context.demo.Where(x=>x.Id>0).ToListAsync();
- 取单个实体
First,FirstOrDefault,Single,SingleOrDefault
- first和single的区别,来看看生成的sql
first,firstOrDefault: SELECT `x`.`id`, `x`.`create_date`, `x`.`title` FROM `blog` AS `x` WHERE `x`.`id` > 10 LIMIT 1 single,singleOrdefault: SELECT `x`.`id`, `x`.`create_date`, `x`.`title` FROM `blog` AS `x` WHERE `x`.`id` > 10 LIMIT 2
由上面可看到 LIMIT1和LIMIT2,first查询一条就够了,而Single需要查询2条数据。当数据超过1条时Single会报返回数据超过一条的报错。所以Single方法仅适用于查询条件对应的数据只有一条的场景。
比如指定唯一键做为查询条件
await _context.demo.SingleOrDefaultAsync(x => x.Id==100);
first,single与firstOrDefault,SingleOrDefault的区别是当sql脚本执行查询不到数据时,带后缀的会返回空值,而不带后缀的则会直接报异常。
- 组连接:即根据分组得到结果。
var QueryByGroup = from book in context.Books group book by book.Publisher into grouping select new { PublisherName = grouping.Key.Name, Books = grouping };
- 内连接:即找出两个序列的交集
var joinQuery = from publisher in Context.Publishers join book in Context.Books on publisher equals book.Publisher select new { PublisherName = publisher.Name, BookName = book.Title };
- 左外连接:左外连接与SqL中left join一样。
var leftJoinQuerybyDefault = from publisher in Context.Publishers join book in Context.Books on publisher equals book.Publisher into publisherBooks from book in publisherBooks.DefaultIfEmpty() select new { PublisherName = publisher.Name, BookName = (book == default(Book)) ? "no book" : book.Title };
DefaultIfEmpty操作符,它能够为实序列提供一个默认的元素。DefaultIfEmpty使用了泛型中的default关键字。如不使用default,则要DefaultIfEmpty中给定当空时的默认对象值。
var leftJoinQuery = from publisher in Context.Publishers join book in Context.Books on publisher equals book.Publisher into publisherBooks from book in publisherBooks.DefaultIfEmpty( new Book { Title = "" }//设置为空时的默认值 ) select new { PublisherName = publisher.Name, BookName = book.Title };
- 交叉连接 :交叉连接与SqL中Cross join一样。如下例中找出Publishers与Books的交叉连接。
var crossJoinQuery = from publisher in Context.Publishers from book in Context.Books select new { PublisherName = publisher.Name, BookName = book.Title };
六、修改与删除
修改与删除跟新增差不多,只是方法换一下。
await _context.demo.UpdateAsync(demo);
七、Code First
添加引用
EF类库nuget添加: Microsoft.EntityFrameworkCore.SqlServer API解决方案添加: Microsoft.EntityFrameworkCore.Tools
1、在.Core类库里加上一个Entity的文件夹存放表模型,并添加一个SysUser类
public class User { /// <summary> /// /// </summary> public string Id { get; set; } /// <summary> /// /// </summary> public string Name { get; set; } /// <summary> /// /// </summary> public string Password { get; set; } }
2、在.EF的类库里面新增一个DBContext的文件夹存放数据库上下文,新建一个类
public class AidenDbContext:DbContext { /// <summary> /// /// </summary> /// <param name="options"></param> public AidenDbContext(DbContextOptions<AidenDbContext> options) : base(options) { } /// <summary> /// /// </summary> public DbSet<User> Users { get; set; } }
3、在.api中的appsetting.json里加上连接字符串
{ "ConnectionStrings": { "adminConn": "Data Source=localhost;Initial Catalog=AidenAdmin; User Id=sa;Password=" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
4、在.api中的Program.cs加上数据库配置
builder.Services.AddSqlServer<AidenDbContext>(builder.Configuration.GetConnectionString("adminConn"));
5、 在数据库创建连接中的数据库,然后在vs 默认项目选中EF项目,执行如下命令
Add-Migration Update-Database
八、DB FIrst
添加引用
Install-Package Microsoft.EntityFrameworkCore Install-Package Microsoft.EntityFrameworkCore.SqlServer Install-Package Microsoft.EntityFrameworkCore.Tools Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design
Scaffold-DbContext "Data Source=.;Initial Catalog=EFCore_dbfirst;User ID=sa;Password=sa.123" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Force
-OutputDir *** 实体文件所存放的文件目录 -ContextDir *** DbContext文件存放的目录 -Context *** DbContext文件名 -Schemas *** 需要生成实体数据的数据表所在的模式 -Tables *** 需要生成实体数据的数据表的集合 -DataAnnotations -UseDatabaseNames 直接使用数据库中的表名和列名(某些版本不支持) -Force 强制执行,重写已经存在的实体文件