【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

ef復雜查詢:
https://www.cnblogs.com/wzk153/p/11850712.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM