# 在“.NET”和“ORMS与绘图战略比较”中的数据访问@(_Part @1}{-}实体框架核心=)

<!-- category -- .NET, EF Core, PostgreSQL, Performance, Database -->
<datetime class="hidden">2025-12-03T14:00</datetime>

当建设“.”NET应用程序时, “,”将做出最重要的建筑决定之一 是如何处理数据存取和天体映射”“MS K3”“.”网域生态系统提供了多种多样的方法\,}从完整的@--功能性ORM 到光速-金属SQL 执行“MSG8” 每一种方法都有自己的交易\□-偏差 在性能方面,开发者生产率*,}类型安全=,}以及可维护性#.}

在此综合的两张“MSMK0”指南中,我们“, '”将探索在 “.NET.}最受欢迎的数据访问模式。 我们用 PostgreSQL 和 Npgsql 在“MS K5 ”的例子中使用了PostgresQL与Npgskql 一样的数据访问模式,因为既然如此,“MSKO6”这个博客拥有什么权力?“), ” 这个部落格里, MSSK8 型号@, 和 Trade\-offers 平等适用于 SQL Server @, MySQRQLK12SQ Lite+, 和其他关联数据库。原则仍然是相同的“MSG15 QZ” 只有SQKQL方言和某些具体特点不同的“MSC16”

**本篇文章=)** 关注实体框架核心-,SQL Disease_,和共同陷阱=.
**部分#2** 将覆盖 Dapper, 原始 ADOMS K1NET}, 物体绘图库 <, 和混合方法\.

## 相关条款

如果你对实际的EF核心执行感兴趣的话 , 看看我的其他文章

- [添加博客日志的实体框架@(+Part @1)+%](/blog/addingentityframeworkforblogpostspt1) 建立从零开始的EF核心
- [EF 移徙的正确途径](/blog/efmigrationstherightway) 如何正确处理生产过程中的迁移
- [完整文本搜索@ ( @ pt @ MS K1}](/blog/textsearchingpt1) 使用 EF 核心的文本搜索
- [现代CQRS和事件观察](/blog/moderncqrsandeventsourcing) 具有EF核心的高级建筑图案

## 目录目录

## 数据获取方法的光谱

“.NET”数据访问图景可视化为频谱@:

```
Full Abstraction                                      Full Control
     ↓                                                      ↓
[EF Core] → [EF Core Raw SQL] → [Dapper] → [Npgsql ADO.NET]
```

当您从左向右移动时, , @ 得到性能和控制, 但失去方便和自动功能@ MS K1 @ Let' @ service 详细检查了每种方法@ MPK3}

### 数据获取流程比较

这里% 1\ ' @ action 比较每个方法如何处理典型的查询@ MS K1}

```mermaid
graph TB
    subgraph "EF Core Flow"
        A1[LINQ Query] -->|Compile| B1[Expression Tree]
        B1 -->|Translate| C1[SQL Query]
        C1 -->|Execute| D1[PostgreSQL]
        D1 -->|Results| E1[DbDataReader]
        E1 -->|Materialize| F1[Tracked Entities]
        F1 -->|Return| G1[Application]
    end

    subgraph "Dapper Flow"
        A2[SQL String] -->|Parameterize| B2[DbCommand]
        B2 -->|Execute| C2[PostgreSQL]
        C2 -->|Results| D2[DbDataReader]
        D2 -->|Map| E2[POCOs]
        E2 -->|Return| F2[Application]
    end

    subgraph "Raw Npgsql Flow"
        A3[SQL + Parameters] -->|Build Command| B3[NpgsqlCommand]
        B3 -->|Execute| C3[PostgreSQL]
        C3 -->|Results| D3[NpgsqlDataReader]
        D3 -->|Manual Mapping| E3[Objects]
        E3 -->|Return| F3[Application]
    end

    style A1 stroke:#2563eb,stroke-width:2px
    style B1 stroke:#2563eb,stroke-width:2px
    style C1 stroke:#2563eb,stroke-width:2px
    style D1 stroke:#2563eb,stroke-width:2px
    style E1 stroke:#2563eb,stroke-width:2px
    style F1 stroke:#2563eb,stroke-width:2px
    style G1 stroke:#2563eb,stroke-width:2px

    style A2 stroke:#059669,stroke-width:2px
    style B2 stroke:#059669,stroke-width:2px
    style C2 stroke:#059669,stroke-width:2px
    style D2 stroke:#059669,stroke-width:2px
    style E2 stroke:#059669,stroke-width:2px
    style F2 stroke:#059669,stroke-width:2px

    style A3 stroke:#dc2626,stroke-width:2px
    style B3 stroke:#dc2626,stroke-width:2px
    style C3 stroke:#dc2626,stroke-width:2px
    style D3 stroke:#dc2626,stroke-width:2px
    style E3 stroke:#dc2626,stroke-width:2px
    style F3 stroke:#dc2626,stroke-width:2px
```

### 实绩与开发者生产率贸易

```mermaid
graph LR
    A[High Productivity<br/>Low Performance] --> B[EF Core<br/>Full Tracking]
    B --> C[EF Core<br/>No Tracking]
    C --> D[EF Core<br/>Raw SQL]
    D --> E[Dapper]
    E --> F[Raw Npgsql]
    F --> G[Low Productivity<br/>High Performance]

    style A stroke:#2563eb,stroke-width:2px
    style B stroke:#2563eb,stroke-width:2px
    style C stroke:#3b82f6,stroke-width:2px
    style D stroke:#059669,stroke-width:2px
    style E stroke:#059669,stroke-width:2px
    style F stroke:#dc2626,stroke-width:2px
    style G stroke:#dc2626,stroke-width:2px
```

## 实体框架核心@: @ full @ -}指定ORM

[实体框架核心](https://learn.microsoft.com/en-us/ef/core/) 是 微软\ '}旗舰ORM,}为您的数据库提供一个完整的抽象@.}它支持 PostgreSQL 通过 [Npgsql . - Entity FrameworkCore\ MS K1 - PostgreSQL](https://www.npgsql.org/efcore/) 提供商 @.

有关在您的项目中建立EF核心的实用指南“,”见我的文章 [添加实体博客日志框架](/blog/addingentityframeworkforblogpostspt1).

### 密钥特征

- **跟踪变化情况**: 自动跟踪实体变化并生成相应的 SQL
- **移徙移民**:Dode-第一种方案管理和版本控制@(se [EF 移徙的正确途径](/blog/efmigrationstherightway))
- **LINQ 提供者**@ :_ type @ MPK1} 使用 C## 语言构造的安全查询
- **Lazy/ 高级装入**相关实体的灵活装载战略
- **高级邮后SQL特性**:#full_-}文本搜索@([参见我的文章](/blog/textsearchingpt1)), JSON 列, 数组MS K2 范围类型
- **拦截者和事件**“:” 交叉问题的扩展点

### 具有EF核心基本CRUD

```csharp
public class BlogDbContext : DbContext
{
    public DbSet<BlogPost> BlogPosts { get; set; }
    public DbSet<Comment> Comments { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseNpgsql("Host=localhost;Database=blog;Username=postgres;Password=secret");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // PostgreSQL-specific: Full-text search
        modelBuilder.Entity<BlogPost>()
            .HasGeneratedTsVectorColumn(
                p => p.SearchVector,
                "english",
                p => new { p.Title, p.Content })
            .HasIndex(p => p.SearchVector)
            .HasMethod("GIN");

        // PostgreSQL array type
        modelBuilder.Entity<BlogPost>()
            .Property(p => p.Tags)
            .HasPostgresArrayConversion(
                tag => tag.ToLowerInvariant(),
                tag => tag);
    }
}

public class BlogPost
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public string[] Tags { get; set; }
    public NpgsqlTsVector SearchVector { get; set; }
    public List<Comment> Comments { get; set; }
    public DateTime PublishedDate { get; set; }
}

// Usage
public class BlogService
{
    private readonly BlogDbContext _context;

    public async Task<List<BlogPost>> GetRecentPostsAsync(int count)
    {
        return await _context.BlogPosts
            .Include(p => p.Comments)
            .OrderByDescending(p => p.PublishedDate)
            .Take(count)
            .ToListAsync();
    }

    public async Task<List<BlogPost>> SearchPostsAsync(string searchTerm)
    {
        return await _context.BlogPosts
            .Where(p => p.SearchVector.Matches(EF.Functions.ToTsQuery("english", searchTerm)))
            .ToListAsync();
    }

    public async Task AddPostAsync(BlogPost post)
    {
        _context.BlogPosts.Add(post);
        await _context.SaveChangesAsync();
    }
}
```

### EF 核心部分,原SQL

EF 核心也支持原始 SQL 查询, 当您需要更多的控制时@: @%

```csharp
public async Task<List<BlogPost>> GetPostsByComplexCriteriaAsync()
{
    var searchTerm = "postgresql";

    return await _context.BlogPosts
        .FromSqlInterpolated($@"
            SELECT * FROM ""BlogPosts""
            WHERE ""SearchVector"" @@ to_tsquery('english', {searchTerm})
            AND array_length(""Tags"", 1) > 3
            ORDER BY ts_rank(""SearchVector"", to_tsquery('english', {searchTerm})) DESC
        ")
        .ToListAsync();
}

// Or with DbDataReader for maximum control
public async Task<List<PostStatistics>> GetPostStatisticsAsync()
{
    using var command = _context.Database.GetDbConnection().CreateCommand();
    command.CommandText = @"
        SELECT
            DATE_TRUNC('month', ""PublishedDate"") as Month,
            COUNT(*) as PostCount,
            AVG(ARRAY_LENGTH(""Tags"", 1)) as AvgTags
        FROM ""BlogPosts""
        GROUP BY DATE_TRUNC('month', ""PublishedDate"")
        ORDER BY Month DESC";

    await _context.Database.OpenConnectionAsync();

    var results = new List<PostStatistics>();
    using var reader = await command.ExecuteReaderAsync();

    while (await reader.ReadAsync())
    {
        results.Add(new PostStatistics
        {
            Month = reader.GetDateTime(0),
            PostCount = reader.GetInt32(1),
            AverageTags = reader.GetDouble(2)
        });
    }

    return results;
}
```

### 何时使用 EF 核心

**✅ 使用 EF 核心 当 @:**

- 建立一个新应用程序,满足不断变化的计划要求
- 您需要强大的打字和编译@ - @ time 查询验证
- 移民与计划版本非常重要, [我的移民指南](/blog/efmigrationstherightway))
- 您的团队比 SQL 更喜欢工作对象
- 您正在使用复杂域模式与关系
- 发展速度比原始业绩更关键
- 您需要交叉“ -” 数据库可移植性@ MS K1 @ 尽管 PostgreSQL-}特定功能将您锁定在“ MPK3” 中 。

**@❌ 避免EF核心 当@:**

- 最大性能是关键 ( @high@-}通过APIs*,}批量处理=)
- 您有复杂的 SQL 查询
- 您的查询与对象图形相匹配
- 您需要精细的 SQL 语句控制@ - @ grained 控制
- 内存用量是一个关键的制约因素 @(#change track subject@)
- 您正在与公约地图上的遗留方案合作

## EF SQL 核心SQL Pages *:}了解执行什么方法

有效使用EF核心的最重要方面之一是了解它产生什么SQL=.EF Core多年来大大改进了 SQL 一代人={,但对于核实发送到PostgreSQQL.}的询问至关重要

### 查看生成的 SQL

```csharp
// Enable sensitive data logging and detailed errors (development only!)
optionsBuilder
    .UseNpgsql(connectionString)
    .EnableSensitiveDataLogging()
    .EnableDetailedErrors()
    .LogTo(Console.WriteLine, LogLevel.Information);

// Or use logging to see SQL
public class BlogService
{
    private readonly BlogDbContext _context;
    private readonly ILogger<BlogService> _logger;

    public async Task<List<BlogPost>> GetPostsAsync()
    {
        var query = _context.BlogPosts
            .Where(p => p.PublishedDate > DateTime.UtcNow.AddDays(-30))
            .OrderByDescending(p => p.PublishedDate);

        // View the SQL before execution
        var sql = query.ToQueryString();
        _logger.LogInformation("Executing query: {Sql}", sql);

        return await query.ToListAsync();
    }
}
```

### 示例@:简单查询

**? ?**

```csharp
var recentPosts = await _context.BlogPosts
    .Where(p => p.CategoryId == 5)
    .OrderByDescending(p => p.PublishedDate)
    .Take(10)
    .ToListAsync();
```

**生成 SQL @ ([EF 核心“8+”](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew)):**

```sql
SELECT b."Id", b."Title", b."Content", b."CategoryId", b."PublishedDate"
FROM "BlogPosts" AS b
WHERE b."CategoryId" = @__categoryId_0
ORDER BY b."PublishedDate" DESC
LIMIT @__p_1
```

注意 EF 核心 @ 8+是如何生成清洁的\ MS K1 高效 SQL 且具有适当的参数化@ . [EF 核心“10”](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/whatsnew) 这一趋势继续发展下去,取得了更多的进步。

### 例: 加入包含 @( 在EF核心@5)之前

**C# 代码□:**

```csharp
var posts = await _context.BlogPosts
    .Include(p => p.Category)
    .Include(p => p.Comments)
    .ToListAsync();
```

**旧的SQL @(EF核心 #3.1}#-笛卡尔爆炸*):#**

```sql
SELECT b."Id", b."Title", c."Id", c."Name", cm."Id", cm."Content"
FROM "BlogPosts" AS b
LEFT JOIN "Categories" AS c ON b."CategoryId" = c."Id"
LEFT JOIN "Comments" AS cm ON b."Id" = cm."BlogPostId"
ORDER BY b."Id", c."Id"
```

这样产生 **笛卡尔产品** - @ {如果邮递有10} obsession * MS K2} 该行被重覆 {10} 乘以_!}

### 插件::+SplitQuery @(_EF核心@5+)

**C{#}代码, 带有分裂查询} :**

```csharp
var posts = await _context.BlogPosts
    .Include(p => p.Category)
    .Include(p => p.Comments)
    .AsSplitQuery()  // ← This is the key!
    .ToListAsync();
```

**生成 SQL @ ( @ 多端查询@ MS K1**

```sql
-- Query 1: Get posts and categories
SELECT b."Id", b."Title", b."Content", c."Id", c."Name"
FROM "BlogPosts" AS b
LEFT JOIN "Categories" AS c ON b."CategoryId" = c."Id"

-- Query 2: Get comments for those posts
SELECT cm."Id", cm."Content", cm."BlogPostId"
FROM "Comments" AS cm
INNER JOIN (
    SELECT b."Id"
    FROM "BlogPosts" AS b
) AS t ON cm."BlogPostId" = t."Id"
ORDER BY t."Id"
```

这消除了笛卡尔人的产品,而且往往 **速度快得多** 用于收藏@! @%

### 框注@:_BARBAR_ 过滤器中包含 @_EF 核心 @MS K2}

**C# 代码□:**

```csharp
var posts = await _context.BlogPosts
    .Include(p => p.Comments.Where(c => c.IsApproved))
    .ToListAsync();
```

**生成 SQL:**

```sql
SELECT b."Id", b."Title", b."Content", t."Id", t."Content", t."IsApproved"
FROM "BlogPosts" AS b
LEFT JOIN (
    SELECT c."Id", c."Content", c."IsApproved", c."BlogPostId"
    FROM "Comments" AS c
    WHERE c."IsApproved" = TRUE
) AS t ON b."Id" = t."BlogPostId"
ORDER BY b."Id"
```

### 例如::JSON列 查询: (EF核心:

**C# 代码□:**

```csharp
public class BlogPost
{
    public int Id { get; set; }
    public string Title { get; set; }
    public PostMetadata Metadata { get; set; }  // Stored as JSONB
}

public class PostMetadata
{
    public bool IsFeatured { get; set; }
    public int ViewCount { get; set; }
    public List<string> RelatedTags { get; set; }
}

// Query JSON properties
var featuredPosts = await _context.BlogPosts
    .Where(p => p.Metadata.IsFeatured)
    .ToListAsync();
```

**生成 SQL:**

```sql
SELECT b."Id", b."Title", b."Metadata"
FROM "BlogPosts" AS b
WHERE b."Metadata" ->> 'IsFeatured' = 'true'
```

EF Core {7+可以翻译JSON的地产访问到邮电站 JSON操作员@!}

### 执行更新@) @

**旧路 # M(# 效率不高 @ ):@**

```csharp
var posts = await _context.BlogPosts
    .Where(p => p.CategoryId == 5)
    .ToListAsync();

foreach (var post in posts)
{
    post.IsArchived = true;
}

await _context.SaveChangesAsync();  // Generates N UPDATE statements!
```

**新的路 #(#EF核心#7+):#**

```csharp
await _context.BlogPosts
    .Where(p => p.CategoryId == 5)
    .ExecuteUpdateAsync(setters => setters
        .SetProperty(p => p.IsArchived, true));
```

**生成 SQL @ ( @ single 查询@ MS K1**

```sql
UPDATE "BlogPosts" AS b
SET "IsArchived" = TRUE
WHERE b."CategoryId" = 5
```

这是 **大规模** @-一个SQL 语句,而不是 NZ!

### 框注::Bulk 删除 @(EF核心@7+)

**旧路 :**

```csharp
var oldPosts = await _context.BlogPosts
    .Where(p => p.PublishedDate < DateTime.UtcNow.AddYears(-5))
    .ToListAsync();

_context.BlogPosts.RemoveRange(oldPosts);
await _context.SaveChangesAsync();  // N DELETE statements
```

**新的路 #:#**

```csharp
await _context.BlogPosts
    .Where(p => p.PublishedDate < DateTime.UtcNow.AddYears(-5))
    .ExecuteDeleteAsync();
```

**生成 SQL:**

```sql
DELETE FROM "BlogPosts" AS b
WHERE b."PublishedDate" < @__p_0
```

### 实例::-复杂聚合

**C# 代码□:**

```csharp
var categoryStats = await _context.Categories
    .Select(c => new CategoryStats
    {
        CategoryName = c.Name,
        PostCount = c.BlogPosts.Count(),
        LatestPostDate = c.BlogPosts.Max(p => p.PublishedDate),
        AverageComments = c.BlogPosts.Average(p => p.Comments.Count)
    })
    .ToListAsync();
```

**生成 SQL @ (_ EF 核心@ 8+/10):**

```sql
SELECT c."Name" AS "CategoryName",
       COUNT(*)::int AS "PostCount",
       MAX(b."PublishedDate") AS "LatestPostDate",
       COALESCE(AVG((
           SELECT COUNT(*)::int
           FROM "Comments" AS c0
           WHERE b."Id" = c0."BlogPostId"
       ))::double precision, 0.0) AS "AverageComments"
FROM "Categories" AS c
LEFT JOIN "BlogPosts" AS b ON c."Id" = b."CategoryId"
GROUP BY c."Id", c."Name"
```

### PostgreSQL 全@ - @ Text 搜索

更深的潜入完整@-_text search @,}见我的文章 [使用 EF 核心的文本搜索](/blog/textsearchingpt1).

**C# 代码□:**

```csharp
var searchResults = await _context.BlogPosts
    .Where(p => p.SearchVector.Matches(EF.Functions.ToTsQuery("english", "postgresql & performance")))
    .OrderByDescending(p => p.SearchVector.Rank(EF.Functions.ToTsQuery("english", "postgresql & performance")))
    .Take(20)
    .ToListAsync();
```

**生成 SQL:**

```sql
SELECT b."Id", b."Title", b."Content", b."SearchVector"
FROM "BlogPosts" AS b
WHERE b."SearchVector" @@ to_tsquery('english', @__searchTerm_0)
ORDER BY ts_rank(b."SearchVector", to_tsquery('english', @__searchTerm_0)) DESC
LIMIT 20
```

## 关键EF核心警告和瀑布

### 1. 变化跟踪记忆泄漏

**问题:**

```csharp
// ❌ DANGER: This can cause memory leaks!
public class PostCache
{
    private readonly BlogDbContext _context;
    private List<BlogPost> _cachedPosts;

    public PostCache(BlogDbContext context)
    {
        _context = context;
    }

    public async Task LoadCacheAsync()
    {
        // These entities are now tracked by the context
        _cachedPosts = await _context.BlogPosts.ToListAsync();

        // The DbContext holds references to these entities FOREVER
        // They can never be garbage collected while the context lives!
    }
}
```

**为什么这有什么问题?**

- 跟踪实体在《公约》生效期间的记忆 `DbContext`
- 更改跟踪器维护引用@, 防止垃圾收集
- Long -活环境 @(e.gMS K3单吨@)#=内存泄漏
- 在 ASP.NET Core, 上下文默认范围为 @ (Good}!)
- 但如果你隐藏了追踪实体 你麻烦大了

**解决方案#:**

```csharp
public async Task LoadCacheAsync()
{
    // ✅ Use AsNoTracking() for read-only queries
    _cachedPosts = await _context.BlogPosts
        .AsNoTracking()
        .ToListAsync();

    // Or detach entities after loading
    var posts = await _context.BlogPosts.ToListAsync();
    foreach (var post in posts)
    {
        _context.Entry(post).State = EntityState.Detached;
    }
    _cachedPosts = posts;
}
```

### 2. 代理产生和懒惰装载危险

> **-=YTET -伊甸园字幕组=- 翻译:**
> 
> Lazy 装货代理 * @ + @ caching # =@ label **被保障的记忆沉默**
> 
> 如果您缓存 DbContext 实例或含有已启用代理文件的缓存实体@, @ 您 **意志** 代理机制仍然引用 DbContext, 防止垃圾收集@. 这是EF核心应用程序中最常见和危险的错误之一 @.}
> 
> **拇指规则**: 总是包括收藏 `.Include()`“. ” 只有完全理解权衡并永远不隐藏代理实体“.”才使用代理人。

**问题 @1:N+1查询噩梦**

```csharp
// ❌ Enable lazy loading
optionsBuilder
    .UseNpgsql(connectionString)
    .UseLazyLoadingProxies();  // Convenient but dangerous!

public class BlogPost
{
    public int Id { get; set; }
    public string Title { get; set; }
    public virtual Category Category { get; set; }  // Virtual = proxy
    public virtual List<Comment> Comments { get; set; }
}

// Somewhere in your code
var posts = await _context.BlogPosts.ToListAsync();

foreach (var post in posts)
{
    Console.WriteLine(post.Category.Name);  // N+1 query here!
    Console.WriteLine(post.Comments.Count);  // Another N+1 query!
}
```

**发生什么事了?**

1. 首次查询加载所有员额
2. 用于 **每个员额**@, 访问 `Category` 触发数据库查询
3. 用于 **每个员额**@, 访问 `Comments` 触发另一个查询
4. 如果您有 @100 @ poss @ ,_ 您刚刚执行处决 **201查询**!

**问题 @2: 代理@+ 缓冲 @ = 内存漏**

```csharp
// ❌ CATASTROPHIC: Lazy loading proxies + caching
public class BlogPostCache
{
    private static List<BlogPost> _cachedPosts;
    private readonly BlogDbContext _context;

    public BlogPostCache()
    {
        var optionsBuilder = new DbContextOptionsBuilder<BlogDbContext>();
        optionsBuilder
            .UseNpgsql(connectionString)
            .UseLazyLoadingProxies();  // ⚠️ DANGER!

        _context = new BlogDbContext(optionsBuilder.Options);
    }

    public async Task<List<BlogPost>> GetCachedPostsAsync()
    {
        if (_cachedPosts == null)
        {
            // ❌ These proxy entities hold references to _context
            _cachedPosts = await _context.BlogPosts.ToListAsync();
        }
        return _cachedPosts;
    }
}
```

**为什么这是灾难性的?**

- 代理实体保持提及其 `DbContext`
- 缩略 `DbContext` 继续提及所有跟踪实体
- 您的缓存现在防止了整个对象图形被垃圾回收
- 每次访问导航属性 , **已缓存的上下文@ option: check**
- 内存随着您加载更多数据而变得无约束
- 您最终会用完内存或排气连接池

**答案=: 明确**

```csharp
// ✅ NEVER use lazy loading proxies - always be explicit
optionsBuilder
    .UseNpgsql(connectionString);
    // NO .UseLazyLoadingProxies()!

public class BlogPost
{
    public int Id { get; set; }
    public string Title { get; set; }
    public Category Category { get; set; }  // NOT virtual
    public List<Comment> Comments { get; set; }  // NOT virtual
}

// ✅ Explicit eager loading - you control what's loaded
var posts = await _context.BlogPosts
    .Include(p => p.Category)
    .Include(p => p.Comments)
    .ToListAsync();

// ✅ Or use split queries for better performance
var posts = await _context.BlogPosts
    .Include(p => p.Category)
    .Include(p => p.Comments)
    .AsSplitQuery()
    .ToListAsync();

// ✅ Or use projection to DTOs (best for caching)
var posts = await _context.BlogPosts
    .Select(p => new PostDto
    {
        Title = p.Title,
        CategoryName = p.Category.Name,
        CommentCount = p.Comments.Count
    })
    .ToListAsync();

// ✅ If you MUST cache, use AsNoTracking and no proxies
public class SafeBlogPostCache
{
    private static List<BlogPost> _cachedPosts;
    private readonly IDbContextFactory<BlogDbContext> _contextFactory;

    public async Task<List<BlogPost>> GetCachedPostsAsync()
    {
        if (_cachedPosts == null)
        {
            using var context = await _contextFactory.CreateDbContextAsync();

            _cachedPosts = await context.BlogPosts
                .Include(p => p.Category)
                .Include(p => p.Comments)
                .AsNoTracking()  // Critical for caching!
                .ToListAsync();
        }
        return _cachedPosts;
    }
}
```

**当近邻可以接受时, “(”明白“折价交易”吗?“):”**

只有在“:” 时才能接受 Lazy 装载代名词 。

1. MSKO你有 **短的-生命** 每个 HTTP 请求 < ) { }
2. MSKO你 **从未** 缓存实体
3. ✅ 你're 同意 NMS K2查询性能
4. ✅你're原型,以后将优化
5. 您的团队完全理解 影响

但即使如此 , 明确 `Include()` 几乎总是更好的选择 因为:

- 它使数据加载 **明确和明显**
- {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF **更方便优化** “(”你可以看到“'”正在加载的是什么?
- 其《 它》 **防止意外的N+1查询**
- 它正确处理缓存和长-}生活环境
- {\fn方正黑体简体\fs18 **建议采用的方法** EF 核心小组

### 3. DbContext 寿命问题

有关DbContext生产寿命管理的详细信息请参见我的文章 [EF 移徙的正确途径](/blog/efmigrationstherightway).

**问题:**

```csharp
// ❌ NEVER do this - singleton DbContext
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<BlogDbContext>();  // WRONG!
}

// ❌ Also wrong - storing context in static field
public static class DataAccess
{
    private static BlogDbContext _context = new BlogDbContext();

    public static async Task<BlogPost> GetPostAsync(int id)
    {
        return await _context.BlogPosts.FindAsync(id);
    }
}
```

**为何会搞错?**

- `DbContext` 是 **非线索@ - @ 安全**
- 同时提出请求将造成数据腐败
- 变换跟踪器无限增长
- 连接池耗竭
- 从缓存中存储数据

**解决方案#:**

```csharp
// ✅ Use scoped lifetime (default in ASP.NET Core)
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<BlogDbContext>(options =>
        options.UseNpgsql(connectionString));
}

// ✅ Or use DbContext factory for background services
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContextFactory<BlogDbContext>(options =>
        options.UseNpgsql(connectionString));
}

public class BlogBackgroundService
{
    private readonly IDbContextFactory<BlogDbContext> _contextFactory;

    public async Task ProcessPostsAsync()
    {
        // Create a new context for this operation
        using var context = await _contextFactory.CreateDbContextAsync();

        var posts = await context.BlogPosts.ToListAsync();
        // Process posts...
    }
}
```

### 4. uninted 包括导航属性

**问题:**

```csharp
public class BlogPost
{
    public int Id { get; set; }
    public string Title { get; set; }
    public List<Comment> Comments { get; set; }
}

// You query one post...
var post = await _context.BlogPosts.FirstAsync();

// Add a new comment
var newComment = new Comment { Content = "Great post!" };
post.Comments.Add(newComment);

await _context.SaveChangesAsync();

// ❌ EF Core saves the comment, BUT...
// If Comments wasn't loaded, you just lost all existing comments!
// The collection is empty, so EF thinks there are no other comments
```

**解决方案#:**

```csharp
// ✅ Always load navigation properties before modifying
var post = await _context.BlogPosts
    .Include(p => p.Comments)
    .FirstAsync(p => p.Id == postId);

post.Comments.Add(newComment);
await _context.SaveChangesAsync();

// Or add directly to the DbSet
_context.Comments.Add(new Comment
{
    BlogPostId = postId,
    Content = "Great post!"
});
await _context.SaveChangesAsync();
```

### 5. Async vs同步混音

**问题:**

```csharp
// ❌ Mixing sync and async - deadlock risk!
public async Task<BlogPost> GetPostAsync(int id)
{
    var post = _context.BlogPosts
        .Where(p => p.Id == id)
        .FirstOrDefault();  // Sync method in async context!

    return post;
}

// ❌ Even worse - blocking async code
public BlogPost GetPost(int id)
{
    return _context.BlogPosts
        .FirstOrDefaultAsync(p => p.Id == id)
        .Result;  // DEADLOCK RISK!
}
```

**解决方案#:**

```csharp
// ✅ Use async all the way
public async Task<BlogPost> GetPostAsync(int id)
{
    return await _context.BlogPosts
        .FirstOrDefaultAsync(p => p.Id == id);
}

// ✅ Or use sync all the way (not recommended for ASP.NET Core)
public BlogPost GetPost(int id)
{
    return _context.BlogPosts
        .FirstOrDefault(p => p.Id == id);
}
```

## EF 核心 10: 什么是'}新变化和突破性变化

与 [EF 核心“10”](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/whatsnew) 升级@.}* 完整列表 < , 参见> [EF 核心“10”中的断开更改](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/breaking-changes).

### 所需运行时间

**EF Core {10} 需要.NET <10(EF 核心{MSk0 要求{MS K1} MSKQ2} ***.+NET @8,}.NET@9,}或{.NETFORLANSK6}這是最重要的變化 `net10.0` 在升级前@. @%

### 查询@-}相关断裂变化

#### 1. 参数收藏翻译@( Default changed)

EF Core 核心 @ 10}# 改变方式 `Contains()` 在% - memory concollation 中, 已翻译为 SQL.} 前称, EF Core `OpenJson()` (QSQL 服务器@)}或相似的 @.}现在默认为 **参数数组** 提供更好的查询计划 caching.

**影响**: 您可能看到不同的 SQL 生成了像 @:这样的查询 。

```csharp
var ids = new List<int> { 1, 2, 3, 4, 5 };
var posts = await _context.BlogPosts
    .Where(p => ids.Contains(p.Id))
    .ToListAsync();
```

**EF 核心 @8/9 @ @% (_ OpenJson@):#**

```sql
SELECT b."Id", b."Title"
FROM "BlogPosts" AS b
WHERE b."Id" IN (SELECT value FROM OPENJSON(@__ids_0))
```

**EF 核心 10 (Parater 阵列MS K2**

```sql
SELECT b."Id", b."Title"
FROM "BlogPosts" AS b
WHERE b."Id" = ANY(@__ids_0)  -- PostgreSQL
-- Or: WHERE b."Id" IN (@__ids_0_0, @__ids_0_1, @__ids_0_2, ...) -- SQL Server
```

**如果工作表现出现倒退**@,_BARBAR_ 回到旧的习惯上#:}

```csharp
// SQL Server
optionsBuilder.UseSqlServer(connectionString,
    o => o.UseParameterizedCollectionMode(ParameterTranslationMode.Constant));

// PostgreSQL - generally parameter arrays work well, but you can opt out if needed
```

#### 2. 执行更新Assync 签名更改

缩略 `ExecuteUpdateAsync` 签名已更改, 以支持非@ - @ 表达式 lambdas @ MS K1 @ this is more followly but **用于编程构建表达式树的断开代码**:

**旧式 @(#EF核心@7-9):#**

```csharp
// This still works
await _context.BlogPosts
    .Where(p => p.CategoryId == 5)
    .ExecuteUpdateAsync(setters => setters
        .SetProperty(p => p.IsArchived, true));
```

**新建于 EF 核心 @ 10 @ @ I- @ non-_ 表达式 # ambdas@ MS K3#**

```csharp
// Now you can include custom logic!
await _context.BlogPosts
    .Where(p => p.CategoryId == 5)
    .ExecuteUpdateAsync(setters =>
    {
        setters.SetProperty(p => p.IsArchived, true);
        setters.SetProperty(p => p.UpdatedAt, DateTime.UtcNow);
        // Can now include conditional logic, loops, etc.
    });
```

#### 3.}复式列命名

EF Core {10 改变嵌套复杂类型栏的命名方式,以防止数据腐败\:

**EF 核心“9:”**

```
NestedComplex_Property
```

**EF 核心“10:”**

```
OuterComplex_NestedComplex_Property
```

**移徙对移民的影响**: 如果您有复杂类型的现有表格 @, 您可能需要重命名列或配置清晰的栏名@:

```csharp
modelBuilder.Entity<Order>()
    .ComplexProperty(o => o.ShippingAddress)
    .Property(a => a.Street)
    .HasColumnName("ShippingAddress_Street"); // Explicit name
```

### SQL 服务器 */Azure SQl 特定更改

#### JSON 数据类型默认

对于 Azure SQL 数据库或 SQl 服务器 {2025}\ (}相容级别 *170+),EF Core {10} 默认为新本地端的默认值 `JSON` 数据类型代替 `NVARCHAR(MAX)`.

**选择不适用** (如果你需要逆向兼容@):

```csharp
optionsBuilder.UseAzureSql(connectionString,
    o => o.UseCompatibilityLevel(160)); // Use old NVARCHAR behavior
```

### 升级升级核对清单

当从 EF 核心 @8/9升级为EF Core:#10:

1. 更新目标框架 `net10.0`
2. ✅ 更新全部 `Microsoft.EntityFrameworkCore.*` 组合键到 & @10.#x
3. “✅ 更新” `Npgsql.EntityFrameworkCore.PostgreSQL` 到\ 10.x
4. ✅审查查询 `Contains()` 含有用于执行变化的收藏
5. ✅ 测试任何程序构建的代码 `ExecuteUpdateAsync` 表达式
6. ✅ 如果使用嵌套的复杂类型, 请检查复式列名称 。
7. ✅ 校對 SQL 伺服器 JSON 若使用 Azure SQ L/SQL server {2025}

### 什么' 新建 @(}亮光 @MS K2

- **改进LINQ翻译**更好地生成复杂查询的 SQL
- **执行更新 Async 中的非-表达式羔羊**在批量更新中更具灵活性
- **更好地处理参数**: 改进查询计划缓存
- **加强JSON支助**SQL 服务器“2025”上的土著 JSON类型
- **执行情况改进**快速实现和变化跟踪

## 性能特点

- **查询性能**与Dapper相比,简单查询的间接费用
- **内存使用***: 高,因为变化跟踪和代理生成
- **第一个查询**“: 慢”“(Qquery 编译和缓存”“ )”
- **随后的询问**\ :\ {} 由于已编译的查询缓存而更快@ info
- **插入@ / @ {} 更新**自动更改跟踪增加了间接费用。
- **散装作业**@:_ 使用默认方法的性能不佳@(\考虑 [EFCore.](https://github.com/borisdj/EFCore.BulkExtensions) 在 EF 核心中执行更新@7+) @%

## EF核心核心最佳做法

### 一般准则一般准则

1. **使用无跟踪@ () @% 1** 仅查询以减少内存管理费
2. **避免 N+1 查询** - 使用 `Include()` 或适当分开查询
3. **使用汇总查询** 重复查询模式
4. **考虑做为简单查询@ () @ {}** 复合物包括避免使用笛卡尔产品
5. **使用批量** 用于多个插入@ / @ 更新
6. **DTOs 项目** 早期减少数据传输和内存使用
7. **使用杠杆执行更新@ / @ execuutDelette** 用于散装作业的(EF核心7+)
8. **总是对生成的 SQL 进行日志和审查** 发展中

### 与 PostgreSQL 合作时

1. **使用全@ - @ text search** 功能@( @%[我的向导](/blog/textsearchingpt1)取代“)” `LIKE` 查询
2. **利用 JSONB 列** 用于灵活 schemaM-}无数据
3. **使用数组类型** 供实体内部收集
4. **配置连接共享** 适当调整工作量
5. **您的索引 `tsvector` 列** 带有 GIN 指数
6. **使用范围类型** 时间范围

## 即将出现在2的片段

在下篇文章“,”中,我们'将探索:

- **顶端**微小的-*ORM甜点
- **Raw ADO.NETNSK1Npgsql**最大性能和控制
- **目标绘图图书馆**: 映像师對自动 Mapper
- **混合办法**将EF核心和Dapper组合起来 (CQRS模式
- **业绩基准**-=YTET -伊甸园字幕组=- 翻译:
- **决定矩阵**: 选择适合您情景的正确工具

## 参考文献和进一步阅读

- [框架实体核心文件](https://learn.microsoft.com/en-us/ef/core/)
- [Npgsql实体框架核心提供者](https://www.npgsql.org/efcore/)
- [EF 核心业绩](https://learn.microsoft.com/en-us/ef/core/performance/)
- [EF核心的10中有什么新意?](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/whatsnew)
- [EF 核心“10”的断裂变化](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/breaking-changes)
- [EF核心的8中有什么新意?](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew)
- [PostgreSQL 文档](https://www.postgresql.org/docs/)

**此博客上的关联文章@: @**

- [添加实体博客日志框架](/blog/addingentityframeworkforblogpostspt1)
- [EF 移徙的正确途径](/blog/efmigrationstherightway)
- [以 EF 核心搜索全文](/blog/textsearchingpt1)
- [现代CQRS和事件观察](/blog/moderncqrsandeventsourcing)

---


*在“2,”部分中,我们'将潜入 DapperMS K2 原Npgsql},并探讨如何结合多种方法实现最佳性能和可维护性?*