EntityFramework

参考

官方文档

EFCore性能

深入研究EF Core AddDbContext 引起的内存泄露的原因

在生产环境下处理EFCore数据库迁移的五种方法

EF6 

ORM

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。

 

数据库提供程序

Sqlserver

引入的包

  • Microsoft.EntityFrameworkCore.SqlServer

appsettings.json中链接字符串的配置

  • "CoreMVCContext": "Server=.;Database=jlhrtest;Persist Security Info=True;User ID=sa;Password=1"

Mysql

参考:

引入的包

  • MySql.Data.EntityFrameworkCore

appsettings.json中链接字符串的配置

  • "CoreMVCContext": "server=localhost;port=3306;database=JLHRtest;uid=root;pwd=root;CharSet=utf8"

EF用到的NuGet包常用命令

dotnet ef与NuGet包的【程序包管理器控制台】

这里列出的都是NuGet包的【程序包管理器控制台】的命令,如果是用dotnet ef命令,要在项目的文件夹下打开CMD执行

全部安装ef命令工具:dotnet tool install --global dotnet-ef

添加迁移:dotnet ef migrations add 迁移名称

更新数据库:dotnet ef database update

迁移注意事项

    • 迁移前检查上下文ConText文件,如果没有DbSet类可以不用迁移,下面红色框内是需要迁移的DbSet类
    • Migrations文件夹下如果没有迁移文件,先添加迁移,后更新数据库。
    • 迁移文件过多的,可以删除Migrations文件夹,重新添加迁移,重点注意:在生产的环境禁用此操作
    • 迁移是提示有多个上下文Context
      • 要在迁移命令后用-Context + 上下文t名称,添加迁移和更新数据库时都需要在后面增加,例如:Add-Migration user2020-11-24  -Context UserContext
    • 迁移时提示引用报错
      • 一般迁移的时候,会启动Startup.cs文件,如果这个文件引用了其他项目或者服务、或者使用了注册中心、心跳检测等,迁移的时候也会调用的,所以可以把数据库以外的配置和依赖注入都注释掉
    • 两个地方都要锁定同一个项目问题,否则失败
      •  

         

创建迁移且更新到数据库

备注:每次数据库实体类有变更后比需要执行这两条的命令

  • 添加迁移命令:Add-Migration  迁移名称
    • 名称可任意定义但不能重复,建议使用业务+日期+时间来命名,避免重复,例如user20200704-1911,或者不要业务,直接日期+时间
  • 更新到数据库命令:Update-Database
    • 此操作会根据全部的迁移文件去数据库中生成对应的数据库、表、字段
    • 执行更新命令后,如果手工去删除数据库的一些表或字段,再重新执行更新也没有用的,不会把删除的表或字段给添加回来的,除非把数据库也删除了才会整个数据库添加回来

 

多个上下文多个数据库

多个上下文

多个连接字符串

多个上下文服务注册

迁移时需要锁定上下文

添加迁移和更新数据库操作时要在命令后加-Context + 上下文名称 , 来锁定上下文。

vs2019创建ASP.NET Core MVC新项目自带有EFCore、上下文Context、控制器、视图

创建asp.net core mvc项目,不进行身份证验证

刚建立好的项目是没有NuGet包的

 

创建数据库实体模型

鼠标右键Controllers=>添加=》控制器

选择【视图使用Entity Framework的MVC控制器】

选择建立好的数据库实体模型、点击加号+创建Context上下文、上下可改名为“CoreMVCContext”,点击添加,再点击添加

如果生成报错,那就清理解决方案,然后生成解决方案,再重新生成试试

生成成功的结果如下:自动创建了4个包、上下文类 、控制器、视图

 

自动生成了上下文类

 

    /// <summary>
    /// 数据库上下文类
    /// </summary>
    public class CoreMVCContext : DbContext
    {
        public CoreMVCContext (DbContextOptions<CoreMVCContext> options)
            : base(options)
        {
        }

        public DbSet<CoreMVC.Models.Zldept> Zldept { get; set; }
    }

 

appsettings.json配置文件自动添加了CoreMVCContext节点,需要修改下数据库连接地址和密码

 

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "CoreMVCContext": "Server=.;Database=jlhrtest;Persist Security Info=True;User ID=sa;Password=1"
  }
}

 

Startup.cs文件也自动注册上下文服务,且加载数据连接信息,默认是SqlServer,可更改为他数据库,例如MySql的要把UseSqlServer改为UseMySQL

        public void ConfigureServices(IServiceCollection services)
        {
            //注册上下文服务且加载appsettings.json配置文件中“CoreMVCContext”节点的信息
            //默认是SqlServer数据库,如果用MySql数据库,要把UseSqlServer改为UseMySQL
            services.AddDbContext<CoreMVCContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("CoreMVCContext")));
        }

 

创建模型 

  • 约定是ID或<表名ID>为主键,也可以通过[Key]在属性上面设置主键
  • EF中主键是不能更新的,可以删除

索引

  • 不能使用数据批注创建索引,要在数据库上下文类中重写OnModelCreating方法
  • 索引列:HasIndex
  • 索引唯一性:IsUnique
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //索引设置
            modelBuilder.Entity<Person>()
                .HasIndex(p => new { p.FirstName, p.LastName }) //索引列
                .IsUnique();  //索引唯一性
        }

 

关系

主键

外键

导航属性

查询数据

常用查询方法

  • ToList:集合查询
  • Single、singleordefault:查询单个实体
  • Where:过滤

分页

EFCore3.0以上的分页是使用Skip().Take(),因为3.0以上不再支持sql2008,统一使用sql2012新增的Offset Fetch分页

如果要兼容sql2008的Row_Number分页要用EFCore2.1的UseRowNumberForPaging()

查询几个字段

//查询一个字段
var a = _context.Zlemployee.Select(e => e.Name).ToList();
//查询2个字段
var b = _context.Zlemployee.Select(e=>new { e.Code,e.Name}).ToList(); 

 

复杂查询运算符

参考

加载相关数据Include

EF Core中如何使用LEFT JOIN

ef 三表join,三表left join

 

数据库与EF转换

  • in、not in:Contains()
  • inner join:  Join()
  • left join: DefaultIfEmpty()
  • GroupJoin: 分组左连接
  • is null: string.IsNullOrEmpty()
  • distinct: Select(x=>new { } ).Distinct()
  • group by: GroupBy().Select(x=>x.Key)
 
join:连接

模型代码:

    /// <summary>
    /// 部门表数据库模型
    /// </summary>
    public class Zldept
    {
        public int ID { get; set; }
        public string Code { get; set; }//部门编码
        public string Name { get; set; }//部门名称
    }

    /// <summary>
    /// 人事档案表数据库
    /// </summary>
    public class Zlemployee
    {
        public int ID { get; set; }
        public string Code { get; set; }//工号
        public string Name { get; set; }//姓名
        public string ZleptCode { get; set; }//部门编码
    }

 

控制器代码:

// left join :返回第一个表(左表)的全部记录,第二个表(右表)没有匹配的数据也会显示
// inner join :返回两个表交集的记录,要两个表同时有数据匹配才会显示
// 总结:因为inner join比left join性能高,如果业务允许尽量推荐使用inner join,但是有不少业务是必须使用left join

//LINQ:inner join: 用join on equals
var result = (from e in _context.Zlemployee
                join d in _context.Zldept on e.ZleptCode equals d.Code
                select new ZlemployeeView { ID = e.ID, Code = e.Code, Name = e.Name, ZldeptName = d.Name, E_zhiwuName = z.Name }
                ).ToList();

//LINQ:inner join: 用from
var result = (from e in _context.Set<Zlemployee>()
                from d in _context.Set<Zldept>().Where(d => d.Code == e.ZleptCode)
                select new { ID = e.ID, Code = e.Code, Name = e.Name, ZldeptName = d.Name }
                ).ToList();

//Lambda:inner join:用Join
var result = _context.Zlemployee
                .Join(_context.Zldept, a => a.ZleptCode, b => b.Code, (a, b) => new { a.Code, a.Name, ZldeptName = b.Name, a.E_zhiwuID })
                .Join(_context.E_zhiwu, c => c.E_zhiwuID, d => d.ID, (c, d) => new { c.Code, c.Name, ZldeptName = c.ZldeptName, E_zhiwuName = d.Name })
                .ToList();

//LINQ:Left Join: 用into、DefaultIfEmpty()
var result = (from e in _context.Zlemployee
                join d in _context.Zldept on e.ZleptCode equals d.Code into zlemptgrouping
                from d in zlemptgrouping.DefaultIfEmpty()
                select new { Code = e.Code, Name = e.Name, ZldeptName = d.Name }
                ).ToList();

 

生成的sql语句:

inner join:

SELECT [z].[Code], [z].[Name], [z0].[Name] AS [ZldeptName], [e].[Name] AS [E_zhiwuName]
FROM [Zlemployee] AS [z]
INNER JOIN [Zldept] AS [z0] ON [z].[ZleptCode] = [z0].[Code]
INNER JOIN [E_zhiwu] AS [e] ON [z].[E_zhiwuID] = [e].[ID]

 

left join:

SELECT [z].[Code], [z].[Name], [z0].[Name] AS [ZldeptName], [e].[Name] AS [E_zhiwuName]
FROM [Zlemployee] AS [z]
LEFT JOIN [Zldept] AS [z0] ON [z].[ZleptCode] = [z0].[Code]
LEFT JOIN [E_zhiwu] AS [e] ON [z].[E_zhiwuID] = [e].[ID]

 

DISTINCT:过滤重复
var test = db.Set<Student>().Select(x => new { x.Name, x.Dept }).Distinct();

生成SQL语句

SELECT 
    [Distinct1].[C1] AS [C1], 
    [Distinct1].[Name] AS [Name], 
    [Distinct1].[Dept] AS [Dept]
    FROM ( SELECT DISTINCT 
        [Extent1].[Name] AS [Name], 
        [Extent1].[Dept] AS [Dept], 
        1 AS [C1]
        FROM [dbo].[Students] AS [Extent1]
    )  AS [Distinct1]

 

group by:分组
var test = db.Students.GroupBy(s=>new { s.Name, s.Dept }).Select(s=> new { s.Key, counts = s.Count() }).ToList();

 

 

加载相关数据

预先加载

加载关联其他实体的数据,可以加载单个实体,也可以集合

Include()

ThenInclude()

显式加载

Entry()

延迟加载

默认关闭,需要手动开启

增加、修改、删除

参考:保存数据

批量更新、删除

参考:Entity Framework Core 5中实现批量更新、删除 

 

迁移

应用迁移

  • SQL 脚本:生产环境首次创建数据库是使用,在项目中使用命令生产SQL脚本:    Script-Migration,也可在开发环境的数据库中生成整个数据库脚本。
  • 幂等 SQL 脚本:生产环境持续迁移时使用,在项目中使用命令生产溟等SQL脚本:Script-Migration -Idempotent
    • 就是生成脚本前检查是否迁移过,迁移过的就不在生成脚本,只生成没迁移过的脚本,参考:冥等
  • 在运行时应用迁移:在程序中使用编程方式迁移

反向工程(基架)

参考:

EF core 从数据库中获取实体模型

 

执行sql语句

FromSqlRaw

基于原始SQL查询创建LINQ查询,代替旧版的FromSql、SqlQuery

基本原生 SQL 查询

可使用 FromSqlRaw 扩展方法基于原始 SQL 查询开始 LINQ 查询。 FromSqlRaw 只能在直接位于 DbSet<> 上的查询根上使用

var blogs = context.Blogs .FromSqlRaw("SELECT * FROM dbo.Blogs") .ToList();

执行存储过程

var blogs = context.Blogs .FromSqlRaw("EXECUTE dbo.GetMostPopularBlogs") .ToList();

传递参数

       //在 SQL 查询字符串中包含形参占位符并提供额外的实参,将单个形参传递到存储过程   Blogs是Context上下文中配置的实体类属性  
            var user = "johndoe";
            var blogs = context.Blogs.FromSqlRaw("EXECUTE dbo.GetMostPopularBlogsForUser {0}", user).ToList();

            //使用字符串内插语法,该值会转换为 DbParameter,且不易受到 SQL 注入攻击
            var user = "johndoe";
            var blogs = context.Blogs.FromSqlInterpolated($"EXECUTE dbo.GetMostPopularBlogsForUser {user}").ToList();

            //DbParameter 并将其作为参数值提供。 由于使用了常规 SQL 参数占位符而不是字符串占位符,因此可安全地使用 FromSqlRaw
            var user = new SqlParameter("user", "johndoe");
            var blogs = context.Blogs.FromSqlRaw("EXECUTE dbo.GetMostPopularBlogsForUser @user", user).ToList();

            //借助 FromSqlRaw,可以在 SQL 查询字符串中使用已命名的参数,这在存储的流程具有可选参数时非常有用
            var user = new SqlParameter("user", "johndoe");
            var blogs = context.Blogs.FromSqlRaw("EXECUTE dbo.GetMostPopularBlogsForUser @filterByUser=@user", user).ToList();

Database.ExecuteSqlRaw 

针对数据库执行给定的SQL,并返回受影响的行数,代替旧版的ExecuteSqlCommand

 

日志记录、时间和诊断 

查看EF生成的SQL语句 

参考

EF Core 日志跟踪sql语句

efcore 如何查看生成的sql 百度经验

两种查看EFCore生成Sql语句的方法

.net core 利用日志查看ef生成的SQL语句

.NET Core实用技巧(一)如何将EF Core生成的SQL语句显示在控制台中

 

简单的日志记录--NET5后建议使用

在数据库上下文类中增加下面代码

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
                .LogTo(info =>
                {
                    Console.WriteLine(info.Contains("Executed") ? info : null);//输出SQL执行脚本,需要控制台运行项目             
                });

 

 

连接池:DbContextPool

参考

官方说明:DbContextPool 连接池   、 AddDbContextPool API说明 、SQL Server 连接池 (ADO.NET)

EF Core 小坑:DbContextPool 会引起数据库连接池连接耗尽

博客园升级到.net core3.0翻车之:峰回路转:去掉 DbContextPool 后 Windows 上的 .NET Core 版博客表现出色

详解数据库连接池概念、原理、运行机制等

数据库 -- 由数据库连接池引出的三种设计模式

数据库连接池到底应该设多大?这篇文章可能会颠覆你的认知

概念

在版本.net core 2.0 中,我们引入了一种在依赖关系注入中注册自定义 DbContext 类型的新方法,即以透明形式引入可重用 DbContext 实例的池。 要使用 DbContext 池,请在服务注册期间使用 AddDbContextPool 而不是 AddDbContext:services.AddDbContextPool<BloggingContext>( options => options.UseSqlServer(connectionString));

如果使用此方法,那么在控制器请求 DbContext 实例时,我们会首先检查池中有无可用的实例。 请求处理完成后,实例的任何状态都将被重置,并且实例本身会返回池中。

从概念上讲,此方法类似于连接池在 ADO.NET 提供程序中的运行原理,并具有节约 DbContext 实例初始化成本的优势。

源码阅读

此类中提示:这是一个内部API,支持Entity Framework Core基础结构,并且不受与公共API相同的兼容性标准的约束。·在任何版本中,它都可能更改或删除,恕不另行通知。您仅应非常谨慎地在代码中直接使用它,并且知道这样做会导致在更新到新的Entity Framework Core版本时导致应用程序失败。

DbContextPool继承IDbContextPool接口

DbContextPool构造函数判断是都第一次使用,如果是就通过激活器CreateActivator来创建

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Internal
{
    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    ///翻译:
    ///    这是一个内部API,支持Entity Framework Core基础结构,并且不受与公共API相同的兼容性标准的约束。 在任何版本中,它都可能更改或删除,恕不另行通知。
    ///    您仅应非常谨慎地在代码中直接使用它,并且知道这样做会导致在更新到新的Entity Framework Core版本时导致应用程序失败。
    /// 
    ///DbContextPool类源码阅读步骤:
    ///     1 查看构造函数
    ///     2 DbContextPool类被EntityFrameworkServiceCollectionExtensions类的两个AddDbContextPool方法分别调用了,相当于调用2次
    ///
    ///疑问:ef默认的AddDbContext有没有使用连接池的?难道一定要一定要AddDbContextPool才会使用连接池 
    /// </summary>
    public class DbContextPool<TContext> : IDbContextPool<TContext>, IDisposable, IAsyncDisposable
        where TContext : DbContext
    {
        private const int DefaultPoolSize = 32;

        //ConcurrentQueue 并发队列:表示线程安全的先进先出(FIFO)集合。
        private readonly ConcurrentQueue<IDbContextPoolable> _pool = new ConcurrentQueue<IDbContextPoolable>();

        private readonly Func<DbContext> _activator;

        private int _maxSize;
        private int _count;

        /// <summary>
        /// 构造函数
        /// </summary>
        /* 使用方式:services.AddDbContextPool<BloggingContext>(options => options.UseSqlServer(connectionString))
         * DbContextOptions:上下文选项类
         * FindExtension:获取指定类型的扩展名。 如果未配置指定类型的扩展名,则返回null
         */
        public DbContextPool([NotNull] DbContextOptions<TContext> options)
        {
            //最大值
            _maxSize = options.FindExtension<CoreOptionsExtension>()?.MaxPoolSize ?? DefaultPoolSize;
            //冻结:指定不应再配置此选项对象
            options.Freeze();
            //创建激活器:参数是上下文配置类 ------重点 
            _activator = CreateActivator(options);

            if (_activator == null)
            {
                //InvalidOperationException:初始化的新实例System.InvalidOperationException使用指定的错误消息初始化。
                throw new InvalidOperationException(
                    CoreStrings.PoolingContextCtorError(typeof(TContext).ShortDisplayName()));
            }
        }
        /// <summary>
        /// 创建激活器:参数是上下文配置类
        /// </summary>
        private static Func<DbContext> CreateActivator(DbContextOptions<TContext> options)
        {
            //DeclaredConstructors:获取当前类型声明的构造函数的集合
            var constructors = typeof(TContext).GetTypeInfo().DeclaredConstructors
                                    .Where(c => !c.IsStatic && c.IsPublic)
                                    .ToArray();

            if (constructors.Length == 1)
            {
                var parameters = constructors[0].GetParameters();

                if (parameters.Length == 1 && (parameters[0].ParameterType == typeof(DbContextOptions)
                    || parameters[0].ParameterType == typeof(DbContextOptions<TContext>)))
                {
                    //Expression:构造一个新的System.Linq.Expressions.Expression实例
                    //Lambda:创建一个System.Linq.Expressions.Expression`1,其中在编译时知道委托类型,并带有参数表达式数组。
                    //Compile:将表达式树描述的lambda表达式编译为可执行代码,并生成代表lambda表达式的委托。
                    return Expression.Lambda<Func<TContext>>(Expression.New(constructors[0], Expression.Constant(options))).Compile();
                }
            }

            return null;
        }

        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        public virtual IDbContextPoolable Rent()
        {
            if (_pool.TryDequeue(out var context))
            {
                Interlocked.Decrement(ref _count);

                Check.DebugAssert(_count >= 0, $"_count is {_count}");

                return context;
            }

            context = _activator();

            context.SnapshotConfiguration();

            return context;
        }

        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        public virtual void Return(IDbContextPoolable context)
        {
            if (Interlocked.Increment(ref _count) <= _maxSize)
            {
                context.ResetState();

                _pool.Enqueue(context);
            }
            else
            {
                PooledReturn(context);
            }
        }

        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        public virtual async ValueTask ReturnAsync(IDbContextPoolable context, CancellationToken cancellationToken = default)
        {
            if (Interlocked.Increment(ref _count) <= _maxSize)
            {
                await context.ResetStateAsync(cancellationToken).ConfigureAwait(false);

                _pool.Enqueue(context);
            }
            else
            {
                PooledReturn(context);
            }
        }

        private void PooledReturn(IDbContextPoolable context)
        {
            Interlocked.Decrement(ref _count);

            Check.DebugAssert(_maxSize == 0 || _pool.Count <= _maxSize, $"_maxSize is {_maxSize}");

            context.ClearLease();
            context.Dispose();
        }

        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        public virtual void Dispose()
        {
            _maxSize = 0;

            while (_pool.TryDequeue(out var context))
            {
                context.ClearLease();
                context.Dispose();
            }
        }

        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        public virtual async ValueTask DisposeAsync()
        {
            _maxSize = 0;

            while (_pool.TryDequeue(out var context))
            {
                context.ClearLease();
                await context.DisposeAsync().ConfigureAwait(false);
            }
        }
    }
}
View Code

 

DbContextPool类被EntityFrameworkServiceCollectionExtensions类的AddDbContextPool方法用到

        public static IServiceCollection AddDbContextPool<TContextService, TContextImplementation>(
            [NotNull] this IServiceCollection serviceCollection,
            [NotNull] Action<IServiceProvider, DbContextOptionsBuilder> optionsAction,
            int poolSize = 128)
            where TContextImplementation : DbContext, TContextService
            where TContextService : class
        {
            Check.NotNull(serviceCollection, nameof(serviceCollection));
            Check.NotNull(optionsAction, nameof(optionsAction));

            AddPoolingOptions<TContextImplementation>(serviceCollection, optionsAction, poolSize);

            serviceCollection.TryAddSingleton<IDbContextPool<TContextImplementation>, DbContextPool<TContextImplementation>>();
            serviceCollection.AddScoped<IScopedDbContextLease<TContextImplementation>, ScopedDbContextLease<TContextImplementation>>();

            serviceCollection.AddScoped<TContextService>(
                sp => sp.GetRequiredService<IScopedDbContextLease<TContextImplementation>>().Context);

            return serviceCollection;
        }

        public static IServiceCollection AddPooledDbContextFactory<TContext>(
            [NotNull] this IServiceCollection serviceCollection,
            [NotNull] Action<IServiceProvider, DbContextOptionsBuilder> optionsAction,
            int poolSize = 128)
            where TContext : DbContext
        {
            Check.NotNull(serviceCollection, nameof(serviceCollection));
            Check.NotNull(optionsAction, nameof(optionsAction));

            AddPoolingOptions<TContext>(serviceCollection, optionsAction, poolSize);
            /* 如果尚未注册,则将TImplementation中指定的指定TService作为Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton
             * 服务实现类型添加到集合中。
             */
            serviceCollection.TryAddSingleton<IDbContextPool<TContext>, DbContextPool<TContext>>();
            serviceCollection.TryAddSingleton<IDbContextFactory<TContext>, PooledDbContextFactory<TContext>>();

            return serviceCollection;
        }
View Code

 

DbContextPool类的方法被DbContextLease结构用到

        public DbContextLease([NotNull] IDbContextPool contextPool, bool standalone)
        {
            _contextPool = contextPool;
            _standalone = standalone;

            var context = _contextPool.Rent();
            Context = context;

            context.SetLease(this);
        }

        public void Release()
        {
            if (Release(out var pool, out var context))
            {
                pool.Return(context);
            }
        }

        public ValueTask ReleaseAsync() => Release(out var pool, out var context) ? pool.ReturnAsync(context) : new ValueTask();

        private bool Release(out IDbContextPool pool, out IDbContextPoolable context)
        {
            pool = _contextPool;
            context = Context;
            _contextPool = null;
            Context = null;

            return pool != null;
        }
View Code

 

ToList

ef 三表join,三表left join

GroupBy
posted @ 2020-06-25 18:34  日积月累码农  阅读(811)  评论(0编辑  收藏  举报