Entity Framework的最佳實踐一

来源:https://www.cnblogs.com/workcn/archive/2023/05/24/17427415.html
-Advertisement-
Play Games

文件比較平常都是用Beyond Compare,可以說離不開的神器,特別是針對代碼比較這塊,確實挺好用的。 不過Beyond Compare平常拿它主要是用來做代碼比較,用來做一些大批量的二進位文件比較,其實有點不是很方便。 於是造輪子,重新寫了一個簡單的文件夾比較的小工具。 平常主要是拿來做一些N ...


Entity Framework (EF) Core 是輕量化、可擴展、開源和跨平臺版的常用 Entity Framework 數據訪問技術。 EF Core 可用作對象關係映射程式 (O/RM)

 

創建DbContext 對象

DbContext的生存期

DbContext 的生存期從創建實例時開始,併在釋放實例時結束。

DbContext生成

  • 通過依賴關係
為每個請求創建一個 ApplicationDbContext 實例,並傳遞給控制器,以在請求結束後釋放前執行工作單元

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddDbContext<ApplicationDbContext>(
        options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));
}
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}
public class MyController
{
    private readonly ApplicationDbContext _context;
    public MyController(ApplicationDbContext context)
    {
        _context = context;
    }
}
  • 通過關鍵字 “new”
 public class ApplicationDbContext : DbContext
{
    private readonly string _connectionString;
    public ApplicationDbContext(string connectionString)
    {
        _connectionString = connectionString;
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(_connectionString);
    }
}
//或者
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}
var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
    .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test")
    .Options;

using var context = new ApplicationDbContext(contextOptions);

 

DBContext的屬性

DbContextOptionsBuilder

所有DBContext配置的起始點都是 DbContextOptionsBuilder。 可以通過三種方式獲取此生成器:
  • 在 AddDbContext 和相關方法中
  • 在 OnConfiguring 中
  • 使用 new 顯式構造

DbContextOptions

允許多個具體子類使用其不同的泛型 DbContextOptions 的實例初始化DBContext
 public sealed class ApplicationDbContext1 : ApplicationDbContextBase
{
    public ApplicationDbContext1(DbContextOptions<ApplicationDbContext1> contextOptions)
        : base(contextOptions)
    {
    }
}
public sealed class ApplicationDbContext2 : ApplicationDbContextBase
{
    public ApplicationDbContext2(DbContextOptions<ApplicationDbContext2> contextOptions)
        : base(contextOptions)
    {
    }
}
   protected ApplicationDbContext(DbContextOptions contextOptions)
        : base(contextOptions)
    {
    }

 

DBContext的兩個關鍵方法

OnConfiguring配置方法

DBContext的配置入口
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        //配置資料庫提供程式,還有其他資料庫配置如下圖:
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test");
        //日誌記錄配置,通過在控制台輸出
        optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information).EnableSensitiveDataLogging();
        //關於其他DBContext配置,參看下圖
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder, typeof(BaseEntity));
    }
關於其他資料庫提供程式
資料庫系統 配置示例 NuGet 程式包
SQL Server 或 Azure SQL .UseSqlServer(connectionString) Microsoft.EntityFrameworkCore.SqlServer
Azure Cosmos DB .UseCosmos(connectionString, databaseName) Microsoft.EntityFrameworkCore.Cosmos
SQLite .UseSqlite(connectionString) Microsoft.EntityFrameworkCore.Sqlite
EF Core 記憶體中資料庫 .UseInMemoryDatabase(databaseName) Microsoft.EntityFrameworkCore.InMemory
PostgreSQL* .UseNpgsql(connectionString) Npgsql.EntityFrameworkCore.PostgreSQL
MySQL/MariaDB* .UseMySql(connectionString) Pomelo.EntityFrameworkCore.MySql
Oracle* .UseOracle(connectionString) Oracle.EntityFrameworkCore
關於DbContextOptionsBuilder 的更多配置
DbContextOptionsBuilder 方法 作用 瞭解更多
UseQueryTrackingBehavior 設置查詢的預設跟蹤行為 查詢跟蹤行為
LogTo 獲取 EF Core 日誌的一種簡單方法 日誌記錄、事件和診斷
UseLoggerFactory 註冊Microsoft.Extensions.Logging工廠 日誌記錄、事件和診斷
EnableSensitiveDataLogging 在異常和日誌記錄中包括應用程式數據 日誌記錄、事件和診斷
EnableDetailedErrors 更詳細的查詢錯誤(以性能為代價) 日誌記錄、事件和診斷
ConfigureWarnings 忽略或引發警告和其他事件 日誌記錄、事件和診斷
AddInterceptors 註冊 EF Core 偵聽器 日誌記錄、事件和診斷
UseLazyLoadingProxies 使用動態代理進行延遲載入 延遲載入
UseChangeTrackingProxies 使用動態代理進行更改跟蹤 即將推出...

 

OnModelCreating配置方法

實體的配置入口方法
 
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        modelBuilder.Entity<AuditEntry>();
    }
 
使用 fluent API 配置模型替代OnModelCreating
關於實體的配置,還可使用Fluent api 可在上下文中替代 OnModelCreating 方法,並使用 Fluent API 來配置模型。 此配置方法最為有效,並可在不修改實體類的情況下指定配置。 Fluent API 配置具有最高優先順序,並將替代約定和數據註釋。 配置按調用方法的順序應用,如果存在任何衝突,最新調用將替代以前指定的配置。 查看代碼
 modelBuilder.Entity<Blog>()
            .Property(b => b.Url)
            .IsRequired();  //代替修改實體
還有這種等等            
[NotMapped]
public class BlogMetadata
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Ignore<BlogMetadata>();
}

 

DBContext的結構

模型

模型由實體類和表示資料庫會話的上下文對象構成。 上下文對象允許查詢並保存數據
     public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(
            @"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public int Rating { get; set; }
    public List<Post> Posts { get; set; }
}

 

實體

在上下文中包含一種類型的 DbSet 意味著它包含在 EF Core 的模型中,或者在OnModelCreating 指定的;我們通常將此類類型稱為實體

加入實體的三種方式

  • 在上下文的 DbSet 屬性中公開。
  • 通過實體 Blog.Posts 的導航屬性發現的。
  • 在 OnModelCreating 中指定的。
 internal class MyContext : DbContext
{

    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        modelBuilder.Entity<AuditEntry>();
    }
}

public class Blog
{
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{

    public string Content { get; set; }

}

 

實體的屬性約束

關於屬性的約束,查看一下鏈接 實體屬性 - EF Core | Microsoft Learn 以下列出常用
等等  
[NotMapped]  
[Column("blog_id")] 指定資料庫列映射,資料庫列與實體列不一致
[Column(TypeName = "varchar(200)")]  
[MaxLength(500)]  
[Required]  
[Comment("The URL of the blog")] 描述
[Column(Order = 2)]  

 

實體狀態變化規則

DBContext上下文為跟蹤狀態時,實體變化如下;

 

EntityState的5個狀態解釋:

  0  
成員名稱 說明
Detached 對象存在,但沒有被跟蹤。 在創建實體之後、但將其添加到對象上下文之前,該實體處於此狀態。 An entity is also in this state after it has been removed from the context by calling the Detach method or if it is loaded by using a NoTrackingMergeOption. 沒有 ObjectStateEntry 實例與狀態為 Detached 的對象關聯。
Unchanged 自對象附加到上下文中後,或自上次調用 SaveChanges 方法後,此對象尚未經過修改。
Added 對象為新對象,並且已添加到對象上下文,但尚未調用 SaveChanges 方法。 在保存更改後,對象狀態將更改為 Unchanged。 狀態為 Added 的對象在 ObjectStateEntry 中沒有原始值。
Modified 對象上的一個標量屬性已更改,但尚未調用 SaveChanges 方法。 在不帶更改跟蹤代理的 POCO 實體中,調用 DetectChanges 方法時,已修改屬性的狀態將更改為 Modified。 在保存更改後,對象狀態將更改為 Unchanged。
Deleted 刪除狀態

 

手動設置實體狀態

  _mIPODataDBContext.Entry(t_TaskOrderFiles).Property(x => x.Url).IsModified = true;
 _mIPODataDBContext.SaveChanges();

 

DBContext的查詢方式

跟蹤和不跟蹤查詢(AsNoTracking)

 //跟蹤
var blog = context.Blogs.SingleOrDefault(b => b.BlogId == 1);
blog.Rating = 5;
context.SaveChanges();
//不跟蹤
var blogs = context.Blogs
    .AsNoTracking()
    .ToList();
//在上下文實例級別取消跟蹤
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

 

導航查詢(Include,ThenInclude)

   //同級Include,子級,更深級別的關聯 ThenInclude
  var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ToList();

 

Inner Join查詢

通過linq表達式
 var query = from photo in context.Set<PersonPhoto>()
            join person in context.Set<Person>()
                on new { Id = (int?)photo.PersonPhotoId, photo.Caption }
                equals new { Id = person.PhotoId, Caption = "SN" }
            where b.ProjectOrderId == Guid.Parse(proIdParam.Value) && p.IsDel == false
            select new { person, photo };
            
SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]
FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON ([p0].[PersonPhotoId] = [p].[PhotoId] AND ([p0].[Caption] = N'SN'))
 
通過lamada表達式
 var query = from b in context.Set<Blog>()
            from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId)
            select new { b, p };
            
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
INNER JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]

 

Left Join查詢

lamada表達式
 var query = from b in context.Set<Blog>()
            join p in context.Set<Post>()
                on b.BlogId equals p.BlogId into grouping
            from p in grouping.DefaultIfEmpty()
            select new { b, p };
 
linq表達式
 var query2 = from b in context.Set<Blog>()
             from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()
             select new { b, p };

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]

 

GroupJoin查詢

 var query = from b in context.Set<Blog>()
            join p in context.Set<Post>()
                on b.BlogId equals p.BlogId into grouping
            select new { b, Posts = grouping.Where(p => p.Content.Contains("EF")).ToList() };

 

GroupBy查詢

 var query = from p in context.Set<Post>()
            group p by p.AuthorId
            into g
            where g.Count() > 0
            orderby g.Key
            select new { g.Key, Count = g.Count() };
            
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
HAVING COUNT(*) > 0
ORDER BY [p].[AuthorId]    
支持的其他聚合運算符
.NET SQL
Average(x => x.Property) AVG(Property)
Count() COUNT(*)
LongCount() COUNT(*)
Max(x => x.Property) MAX(Property)
Min(x => x.Property) MIN(Property)
Sum(x => x.Property) SUM(Property)

 

純SQL查詢

  var rowsModified = context.Database.ExecuteSql($"UPDATE [Blogs] SET [Url] = NULL");
 
 var overAverageIds = context.Database
    .SqlQuery<int>($"SELECT [BlogId] AS [Value] FROM [Blogs]")
    .Include(b => b.Posts)
    .Where(id => id > context.Blogs.Average(b => b.BlogId))
    .ToList();
 
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、準備一下 開發環境 Pycharm python 3.8 ffmpeg 模塊的使用 requests re subprocess 二、基本思路流程 1、明確需求 採集下破站視頻數據通過開發者工具進行抓包分析,分析破站視頻數據的來源。 開發者工具的使用 打開方式: 滑鼠右鍵點擊檢查選擇Networ ...
  • ## 前言 如題,這個小玩意,就是不限制你查的是哪張表,用的是什麼類。 我直接一把梭,嘎嘎給你一頓導出。 我知道,這是很多人都想過的, 至少我就收到很多人問過我這個類似的問題。 我也跟他們說了,但是他們就是不動手,其實真的很簡單。 不動手怎麼辦? 我出手唄。 不多說開搞 。 ## 正文 玩法很簡單。 ...
  • # 超輕量級 DynamicTableNameInnerInterceptor是mybatis-plug的一個攔截器插件,可以自己定義需要攔截的表單,然後對它進行加工,這時mybatis-plus就會把SQL代碼的表名加上你的這個裝飾。 # 封裝的思想 我們通常把mybatis做成一個包,公司其它同 ...
  • 用`markdown`寫文檔很方便,但是有個困擾的地方,就是標題的編號問題。 寫文檔的時候,經常會在中間插入新的標題和內容,所以手動管理編號的話,如果新的標題插在前面,則要調整後面所有的編號。 如果在文檔完成後再手動加上編號的話,不僅容易忘記, 而且有時候我們是在其他編輯器里編輯文檔再導出`mark ...
  • # 一、環境準備 Netty需要的運行環境很簡單,只有2個。 - JDK 1.8+ - Apache Maven 3.3.9+ # 二、Netty 客戶端/伺服器概覽 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/c49191e6ee6e448f8c525b450 ...
  • # 原因 當mysql資料庫單表大於1千萬以後,查詢的性能就不能保證了,我們必須考慮分庫,分表的方案了,還好,sharding-jdbc可以很優雅的與springboot對接,完成對mysql的分庫和分表。 # 依賴整理 > 為了不影響其它小容量的表,所有添加了動態數據源,只對需要分庫分表的進行配置 ...
  • # Maven的概述 @[toc] Java 項目開發過程中,構建指的是使用『原材料生產產品』的過程。 - 原材料 - Java 源代碼 - 基於 HTML 的 Thymeleaf 文件 - 圖片 - 配置文件 - …… - 產品 - 一個可以在伺服器上運行的項目 構建過程包含的主要的環節: - 清 ...
  • > ML.Net - 開源的跨平臺機器學習框架 > - 支持CPU/GPU訓練 > - 輕鬆簡潔的預測代碼 > - 可擴展其他的機器學習平臺 > - 跨平臺 ![img](https://img2023.cnblogs.com/blog/1339560/202305/1339560-20230524 ...
一周排行
    -Advertisement-
    Play Games
  • 通過WPF的按鈕、文本輸入框實現了一個簡單的SpinBox數字輸入用戶組件並可以通過數據綁定數值和步長。本文中介紹了通過Xaml代碼實現自定義組件的佈局,依賴屬性的定義和使用等知識點。 ...
  • 以前,我看到一個朋友在對一個系統做初始化的時候,通過一組魔幻般的按鍵,調出來一個隱藏的系統設置界面,這個界面在常規的菜單或者工具欄是看不到的,因為它是一個後臺設置的關鍵界面,不公開,同時避免常規用戶的誤操作,它是作為一個超級管理員的入口功能,這個是很不錯的思路。其實Winform做這樣的處理也是很容... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他的程式每次關閉時就會自動崩潰,一直找不到原因讓我幫忙看一下怎麼回事,這位朋友應該是第二次找我了,分析了下 dump 還是挺經典的,拿出來給大家分享一下吧。 二:WinDbg 分析 1. 為什麼會崩潰 找崩潰原因比較簡單,用 !analyze -v 命 ...
  • 在一些報表模塊中,需要我們根據用戶操作的名稱,來動態根據人員姓名,更新報表的簽名圖片,也就是電子手寫簽名效果,本篇隨筆介紹一下使用FastReport報表動態更新人員簽名圖片。 ...
  • 最新內容優先發佈於個人博客:小虎技術分享站,隨後逐步搬運到博客園。 創作不易,如果覺得有用請在Github上為博主點亮一顆小星星吧! 博主開始學習編程於11年前,年少時還只會使用cin 和cout ,給單片機點點燈。那時候,類似async/await 和future/promise 模型的認知還不是 ...
  • 之前在阿裡雲ECS 99元/年的活動實例上搭建了一個測試用的MINIO服務,以前都是直接當基礎設施來使用的,這次準備自己學一下S3相容API相關的對象存儲開發,因此有了這個小工具。目前僅包含上傳功能,後續計劃開發一個類似圖床的對象存儲應用。 ...
  • 目錄簡介快速入門安裝 NuGet 包實體類User資料庫類DbFactory增刪改查InsertSelectUpdateDelete總結 簡介 NPoco 是 PetaPoco 的一個分支,具有一些額外的功能,截至現在 github 星數 839。NPoco 中文資料沒多少,我是被博客園群友推薦的, ...
  • 前言 前面使用 Admin.Core 的代碼生成器生成了通用代碼生成器的基礎模塊 分組,模板,項目,項目模型,項目欄位的基礎功能,本篇繼續完善,實現最核心的模板生成功能,並提供生成預覽及代碼文件壓縮下載 準備 首先清楚幾個模塊的關係,如何使用,簡單畫一個流程圖 前面完成了基礎的模板組,模板管理,項目 ...
  • 假設需要實現一個圖標和文本結合的按鈕 ,普通做法是 直接重寫該按鈕的模板; 如果想作為通用的呢? 兩種做法: 附加屬性 自定義控制項 推薦使用附加屬性的形式 第一種:附加屬性 創建Button的附加屬性 ButtonExtensions 1 public static class ButtonExte ...
  • 在C#中,委托是一種引用類型的數據類型,允許我們封裝方法的引用。通過使用委托,我們可以將方法作為參數傳遞給其他方法,或者將多個方法組合在一起,從而實現更靈活的編程模式。委托類似於函數指針,但提供了類型安全和垃圾回收等現代語言特性。 基本概念 定義委托 定義委托需要指定它所代表的方法的原型,包括返回類 ...