簡介
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
