【EF Core】基础
简介
Entity Framework Core(EF Core)是微软官方的ORM框架。优点:功能强大、官方支持、生产效率高、力求屏蔽底层数据库差异;缺点:复杂、上手门槛高、不熟悉EFCore的话可能会进坑。
Nuget包推荐
SqlServer:Microsoft.EntityFrameworkCore.SqlServer
MySQL:Pomelo.EntityFrameworkCore.MySql (开源开发者,下载量最高)
Code First开发流程
Console控制台
- 安装对应数据库的Nuget包,如下:
Install-Package Microsoft.EntityFrameworkCore.SqlServer
- 创建实体类
public class Book
{
public long Id { get; set; }//主键
public string Title { get; set; }//标题
public DateTime PubTime { get; set; }//发布日期
public double Price { get; set; }//单价
public string AuthorName { get; set; }//作者名字
public string Decription { get; set; }
//public string Content { get; set; }
}
- 创建实体配置类
配置类需要实现接口IEntityTypeConfiguration<TEntity>
,它是用来配置实体类和表的对应关系
public class BookEntityConfig : IEntityTypeConfiguration<Book>
{
public void Configure(EntityTypeBuilder<Book> builder)
{
builder.ToTable("T_Books");
builder.Property(e => e.Title).HasMaxLength(50).IsRequired();
builder.Property(e => e.AuthorName).HasColumnType("varchar(100)").IsRequired();
}
}
- 创建DbContext
SqlServer:
public class TestDbContext : DbContext
{
public DbSet<Book> Books { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connStr = "Data Source=.;Initial Catalog=EFCoreDB;User ID=sa;Password=123456";
optionsBuilder.UseSqlServer(connStr);
optionsBuilder.LogTo(Console.WriteLine);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
}
}
MySql:
class TestDbContext : DbContext
{
public DbSet<Book> Books { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql("server=localhost;user=root;password=adfa3_ioz09_08nljo;database=ef",
new MySqlServerVersion(new Version(8, 6, 20)));
}
}
ASP.NET Core
DbContext:
public class TestDbContext:DbContext
{
public DbSet<Dog> Dogs { get; private set; }
public TestDbContext(DbContextOptions<TestDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
}
}
注册DbContext:
services.AddDbContext<TestDbContext>(opt => {
string connStr = "Server=.;User ID=sa;Password=123456;Database=DogsDemo;Trusted_Connection=True;";
opt.UseSqlServer(connStr);
});
使用DbContext:
var sp = services.BuildServiceProvider();
var ctx = sp.GetRequiredService<TestDbContext>();
约定映射规则
1:表名采用DbContext中的对应的DbSet的属性名。
2:数据表列的名字采用实体类属性的名字,列的数据类型采用和实体类属性类型最兼容的类型。
3:数据表列的可空性取决于对应实体类属性的可空性。
4:名字为Id的属性为主键,如果主键为short, int 或者 long类型,则默认采用自增字段,如果主键为Guid类型,则默认采用默认的Guid生成机制生成主键值。
两种配置方式
Data Annotation
把配置以特性(Annotation
)的形式标注在实体类中。
[Table("T_Books")]
public class Book
{
}
优点:简单;缺点:耦合。
Fluent API
把配置写到单独的配置类中。
builder.ToTable("T_Books");
缺点:复杂;优点:解耦
大部分功能重叠。可以混用,但是不建议混用。
IQueryable
1、IQueryable只是代表一个“可以放到数据库服务器去执行的查询”,它没有立即执行,只是“可以被执行”而已。
2、对于IQueryable接口调用非终结方法的时候不会执行查询,而调用终结方法的时候则会立即执行查询。
3、终结方法:遍历、ToArray()、ToList()、Min()、Max()、Count()等;
4、非终结方法:GroupBy()、OrderBy()、Include()、Skip()、Take()等。
5、简单判断:一个方法的返回值类型如果是IQueryable类型,那么这个方法一般就是非终结方法,否则就是终结方法。
IEnumerable、IQueryable区别
普通集合的版本(IEnumerable)是在内存中过滤(客户端评估),而IQueryable版本则是把查询操作翻译成SQL语句(服务器端评估)
使用EF执行sql
尽管EF Core已经非常强大,但是仍然存在着无法被写成标准EF Core调用方法的SQL语句,少数情况下仍然需要写原生SQL。
三种情况:非查询语句、实体查询、任意SQL查询
非查询语句
使用dbCtx.Database. ExecuteSqlInterpolated () dbCtx.Database. ExecuteSqlInterpolatedAsync()方法来执行原生的非查询SQL语句。
ctx.Database.ExecuteSqlInterpolatedAsync(@$"insert into T_Books(Title,PubTime,Price,AuthorName)
select Title, PubTime, Price,{aName} from T_Books where Price > {price}");
除了ExecuteSqlInterpolated ()、ExecuteSqlInterpolatedAsync() ,还有ExecuteSqlRaw()、ExecuteSqlRawAsync() 也可以执行原生SQL语句,但需要开发人员自己处理查询参数等了,因此不推荐使用
实体相关sql
如果要执行的原生SQL是一个查询语句,并且查询的结果也能对应一个实体,就可以调用对应实体的DbSet的FromSqlInterpolated()方法来执行一个查询SQL语句,同样使用字符串内插来传递参数。
IQueryable<Book> books = ctx.Books.FromSqlInterpolated(@$"select * from T_Books
where DatePart(year,PubTime)>{year}
order by newid()");
FromSqlInterpolated()方法的返回值是IQueryable类型的,因此我们可以在实际执行IQueryable之前,对IQueryable进行进一步的处理。
IQueryable<Book> books = ctx.Books.FromSqlInterpolated(@$"select * from T_Books where DatePart(year,PubTime)>{year}");
foreach(Book b in books.Skip(3).Take(6))
把只能用原生SQL语句写的逻辑用FromSqlInterpolated()去执行,然后把分页、分组、二次过滤、排序、Include等其他逻辑尽可能仍然使用EF Core的标准操作去实现。
局限性:
SQL 查询必须返回实体类型对应数据库表的所有列;结果集中的列名必须与属性映射到的列名称匹配。
只能单表查询,不能使用Join语句进行关联查询。但是可以在查询后面使用Include()来进行关联数据的获取。
执行任意sql
FromSqlInterpolated()
只能单表查询,但是在实现报表查询等的时候,SQL语句通常是非常复杂的,不仅要多表Join,而且返回的查询结果一般也都不会和一个实体类完整对应。因此需要一种执行任意SQL查询语句的机制。
dbCxt.Database.GetDbConnection()
获得ADO.NET Core的数据库连接对象。这里不讲解ADO.NET基础知识。
DbConnection conn = ctx.Database.GetDbConnection();
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = @"xxx";
var p1 = cmd.CreateParameter();
p1.ParameterName = "@year";
p1.Value = year;
cmd.Parameters.Add(p1);
using (var reader = cmd.ExecuteReader())
}
推荐用Dapper等框架执行原生复杂查询SQL。
将sql输出到控制台
第一种方式:
public class TestDbContext : DbContext
{
public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connStr = "Data Source=.;Initial Catalog=EFCoreDB;User ID=sa;Password=123456";
optionsBuilder.UseSqlServer(connStr);
optionsBuilder.UseLoggerFactory(MyLoggerFactory);
}
}
第二种方式:
输出的信息很多,如果只想看sql语句,需要自己过滤下
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connStr = "Data Source=.;Initial Catalog=EFCoreDB;User ID=sa;Password=123456";
optionsBuilder.UseSqlServer(connStr);
//optionsBuilder.LogTo(Console.WriteLine);//输出全部信息
optionsBuilder.LogTo(msg => {
if (!msg.Contains("CommandExecuting"))
{
Console.WriteLine(msg);
}
});
}
第三种方式:
如果想查看单个sql语句,可以使用ToQueryString()
方法
using (var ctx = new TestDbContext())
{
var books = ctx.Books.Where(b => b.Id == 1);
string sql = books.ToQueryString();
Console.WriteLine(sql);
Console.ReadKey();
}
EF系列:
https://blog.csdn.net/xingkongtianyuzhao/category_9704639.html