• EntityFrameworkCore


    参考

    官方文档

    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()
    • 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
    如有错误,欢迎您指出。
    本文版权归作者和博客园共有,欢迎转载,但必须在文章页面给出原文链接,否则保留追究法律责任的权利。
  • 相关阅读:
    Android SQLite 建立多表间的主外键关系 Binary
    Android——扩大ImageButton的点击区域 Binary
    Android——刷新媒体库 Binary
    Oracle数据导入导出imp/exp命令 10g以上expdp/impdp命令 转自:南水江的鸽子窝
    在同一应用中混合使用ASP.NET窗体和ASP.NET MVC 转自:geez的个人空间
    ASP.NET WebApplication 发布部署
    Oracle 导出索引Sequence!!
    获取ORACLE 表字段,表名,以及主键之类等等的信息
    FlexGrid 控件的使用 摘自:大道至简
    整合 DZNT到自己网站
  • 原文地址:https://www.cnblogs.com/qingyunye/p/13192539.html
Copyright © 2020-2023  润新知