ASP.NET Core 中的 ORM 之 Dapper

目录

Dapper 简介

Dapper是.NET的一款轻量级ORM工具(GitHub),也可称为简单对象映射器。在速度方面拥有微型ORM之王的称号。
它是半自动的,也就是说实体类和SQL语句都要自己写,但它提供自动对象映射。是通过对IDbConnection接口的扩展来操作数据库的。

优点

  • 轻量,只有一个文件
  • 性能高,Dapper的速度接近与IDataReader,取列表的数据超过了DataTable。
  • 支持多种数据库。Dapper可以在所有Ado.net Providers下工作,包括sqlite, sqlce, firebird, oracle, MySQL, PostgreSQL and SQL Server
  • 使用Dapper可以自动进行对象映射,通过Emit反射IDataReader的序列队列,来快速的得到和产生对象

使用 Dapper

下面简单创建一个Web API应用并通过Dapper访问MySQL数据。

  1. 创建MySQL测试数据

    CREATE SCHEMA `ormdemo` ;
    
    CREATE TABLE `ormdemo`.`category` (
      `Id` INT NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(45) NOT NULL,
      PRIMARY KEY (`Id`));
    
    CREATE TABLE `ormdemo`.`product` (
      `Id` INT NOT NULL AUTO_INCREMENT,
      `Name` VARCHAR(45) NOT NULL,
      `Price` DECIMAL(19,2) NULL,
      `Quantity` INT NULL,
      `CategoryId` INT NOT NULL,
      PRIMARY KEY (`Id`),
      INDEX `fk_product_category_idx` (`CategoryId` ASC),
      CONSTRAINT `fk_product_category`
        FOREIGN KEY (`CategoryId`)
        REFERENCES `ormdemo`.`category` (`Id`)
        ON DELETE CASCADE
        ON UPDATE NO ACTION);  
    
    INSERT INTO `ormdemo`.`category` (`Name`) VALUES("Phones");
    INSERT INTO `ormdemo`.`category` (`Name`) VALUES("Computers");
    
    INSERT INTO `ormdemo`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("iPhone8",4999.99,10,1);
    INSERT INTO `ormdemo`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("iPhone7",2999.99,10,1);
    INSERT INTO `ormdemo`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("HP750",6000.00,5,2);
    INSERT INTO `ormdemo`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("HP5000",12000.00,10,2);
  2. 创建Web API应用并添加NuGet引用
    Install-Package MySql.Data
    Install-Package Dapper
  3. 新建一个Product类
    public class Category
    {
        public int Id { get; set; }
    
        public string Name { get; set; }
    }
    
    public class Product
    {
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        public int Quantity { get; set; }
    
        public decimal Price { get; set; }
    
        public int CategoryId { get; set; }
    
        public virtual Category Category { get; set; }
    }
  4. 新建一个DBConfig类用于创建并返回数据库连接
    using MySql.Data.MySqlClient;
    using System.Data;
    using System.Configuration;
    
    public class DBConfig
    {
        //ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
        private static string DefaultSqlConnectionString = @"server=127.0.0.1;database=ormdemo;uid=root;pwd=Open0001;SslMode=none;";
    
        public static IDbConnection GetSqlConnection(string sqlConnectionString = null)
        {
            if (string.IsNullOrWhiteSpace(sqlConnectionString))
            {
                sqlConnectionString = DefaultSqlConnectionString;
            }
            IDbConnection conn = new MySqlConnection(sqlConnectionString);
            conn.Open();
            return conn;
        }
    }
  5. 创建简单的仓储接口和类
    public interface IProductRepository
    {
        Task<bool> AddAsync(Product prod);
        Task<IEnumerable<Product>> GetAllAsync();
        Task<Product> GetByIDAsync(int id);
        Task<bool> DeleteAsync(int id);
        Task<bool> UpdateAsync(Product prod);
    }
    public class ProductRepository : IProductRepository
    {
        public async Task<IEnumerable<Product>> GetAllAsync()
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.QueryAsync<Product>(@"SELECT Id
                                                ,Name
                                                ,Quantity
                                                ,Price
                                                ,CategoryId
                                            FROM Product");
            }
        }
    
        public async Task<Product> GetByIDAsync(int id)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                string sql = @"SELECT Id
                                    ,Name
                                    ,Quantity
                                    ,Price
                                    ,CategoryId
                                FROM Product
                                WHERE Id = @Id";
                return await conn.QueryFirstOrDefaultAsync<Product>(sql, new { Id = id });
            }
        }
    
        public async Task<bool> AddAsync(Product prod)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                string sql = @"INSERT INTO Product
                                (Name
                                ,Quantity
                                ,Price
                                ,CategoryId)
                            VALUES
                                (@Name
                                ,@Quantity
                                ,@Price
                                ,@CategoryId)";
                return await conn.ExecuteAsync(sql, prod) > 0;
            }
        }
    
        public async Task<bool> UpdateAsync(Product prod)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                string sql = @"UPDATE Product SET
                                    Name = @Name
                                    ,Quantity = @Quantity
                                    ,Price= @Price
                                    ,CategoryId= @CategoryId
                               WHERE Id = @Id";
                return await conn.ExecuteAsync(sql, prod) > 0;
            }
        }
    
        public async Task<bool> DeleteAsync(int id)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                string sql = @"DELETE FROM Product
                                WHERE Id = @Id";
                return await conn.ExecuteAsync(sql, new { Id = id }) > 0;
            }
        }
    }

    在Startup ConfigureServices方法里面配置依赖注入

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IProductRepository, ProductRepository>();
    
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }
  6. 在Controller里面调用仓储方法
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly IProductRepository _productRepository;
        public ProductController(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }
    
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            var data = await _productRepository.GetAllAsync();
            return Ok(data);
        }
    
        [HttpGet("{id}")]
        public async Task<IActionResult> Get(int id)
        {
            var data = await _productRepository.GetByIDAsync(id);
            return Ok(data);
        }
    
        [HttpPost]
        public async Task<IActionResult> Post([FromBody] Product prod)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
    
            await _productRepository.AddAsync(prod);
            return NoContent();
        }
    
        [HttpPut("{id}")]
        public async Task<IActionResult> Put(int id, [FromBody] Product prod)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
    
            var model = await _productRepository.GetByIDAsync(id);
            model.Name = prod.Name;
            model.Quantity = prod.Quantity;
            model.Price = prod.Price;
            await _productRepository.UpdateAsync(model);
    
            return NoContent();
        }
    
        [HttpDelete("{id}")]
        public async Task<IActionResult> Delete(int id)
        {
            await _productRepository.DeleteAsync(id);
            return NoContent();
        }
    }
  7. 测试API是否可以正常工作
  8. Dapper对存储过程和事务的支持

    存储过程

    using (var connection = My.ConnectionFactory())
    {
        connection.Open();
    
        var affectedRows = connection.Execute(sql,
            new {Kind = InvoiceKind.WebInvoice, Code = "Single_Insert_1"},
            commandType: CommandType.StoredProcedure);
    
        My.Result.Show(affectedRows);
    }

    事务

    using (var connection = My.ConnectionFactory())
    {
        connection.Open();
    
        using (var transaction = connection.BeginTransaction())
        {
            var affectedRows = connection.Execute(sql, new {CustomerName = "Mark"}, transaction: transaction);
    
            transaction.Commit();
        }
    }
  9. Dapper对多表映射的支持
    var selectAllProductWithCategorySQL = @"select * from product p
        inner join category c on c.Id = p.CategoryId
        Order by p.Id";
    var allProductWithCategory = connection.Query<Product, Category, Product>(selectAllProductWithCategorySQL, (prod, cg) => { prod.Category = cg; return prod; });

使用 Dapper Contrib 或其他扩展

Dapper Contrib扩展Dapper提供了CRUD的方法

  • Get
  • GetAll
  • Insert
  • Update
  • Delete
  • DeleteAll
  1. 添加NuGet引用Dapper.Contrib

    Install-Package Dapper.Contrib
  2. 为Product类添加数据注解
    [Table("Product")]
    public class Product
    {
        [Key]
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        public int Quantity { get; set; }
    
        public decimal Price { get; set; }
    
        public int CategoryId { get; set; }
    
        public virtual Category Category { get; set; }
    }
  3. 增加一个新的仓储类继承
    public class ContribProductRepository : IProductRepository
    {
        public async Task<bool> AddAsync(Product prod)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.InsertAsync(prod) > 0;
            }
        }
    
        public async Task<IEnumerable<Product>> GetAllAsync()
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.GetAllAsync<Product>();
            }
        }
    
        public async Task<Product> GetByIDAsync(int id)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.GetAsync<Product>(id);
            }
        }
    
        public async Task<bool> DeleteAsync(int id)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                var entity = await conn.GetAsync<Product>(id);
                return await conn.DeleteAsync(entity);
            }
        }
    
        public async Task<bool> UpdateAsync(Product prod)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.UpdateAsync(prod);
            }
        }
    }

    修改Startup ConfigureServices方法里面配置依赖注入

    services.AddTransient<IProductRepository, ContribProductRepository>();

    测试,这样可以少写了不少基本的SQL语句。

  4. 其他一些开源的Dapper扩展
    类库 提供的方法
    Dapper.SimpleCRUD Get GetList GetListPaged Insert Update Delete DeleteList RecordCount
    Dapper Plus Bulk Insert Bulk Delete Bulk Update Bulk Merge Bulk Action Async Bulk Also Action Bulk Then Action
    Dapper.FastCRUD Get Find Insert Update BulkUpdate Delete BulkDelete Count
    Dapper.Mapper Multi-mapping

引入工作单元 Unit of Work

仓储模式往往需要工作单元模式的介入来负责一系列仓储对象的持久化,确保数据完整性。网上关于工作单元模式的实现方式有多种,但其本质都是工作单元类通过创建一个所有仓储共享的数据库上下文对象,来组织多个仓储对象。

网上的一些实现方式:

  • Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application
    微软之前给出的一个示例,仓储类做为工作单元的变量,并通过工作单元传入一致的context参数创建。

    public class UnitOfWork : IDisposable
    {
        private SchoolContext context = new SchoolContext();
        private GenericRepository<Department> departmentRepository;
        private GenericRepository<Course> courseRepository;
    
        public GenericRepository<Department> DepartmentRepository
        {
            get
            {
    
                if (this.departmentRepository == null)
                {
                    this.departmentRepository = new GenericRepository<Department>(context);
                }
                return departmentRepository;
            }
        }
    
        public GenericRepository<Course> CourseRepository
        {
            get
            {
    
                if (this.courseRepository == null)
                {
                    this.courseRepository = new GenericRepository<Course>(context);
                }
                return courseRepository;
            }
        }
    
        public void Save()
        {
            context.SaveChanges();
        }
    
        private bool disposed = false;
    
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    context.Dispose();
                }
            }
            this.disposed = true;
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
  • DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践
    博客园一位大神的总结,最终采用的方案是仓储类负责查询,工作单元类负责增删改等数据持久化操作。

优缺点不作讨论,适合自己的就是最好的,这里采用了另外一种实现方式:

  • 定义DapperDBContext

    public interface IContext : IDisposable
    {
        bool IsTransactionStarted { get; }
    
        void BeginTransaction();
    
        void Commit();
    
        void Rollback();
    }
    
    public abstract class DapperDBContext : IContext
    {
        private IDbConnection _connection;
        private IDbTransaction _transaction;
        private int? _commandTimeout = null;
        private readonly DapperDBContextOptions _options;
    
        public bool IsTransactionStarted { get; private set; }
    
        protected abstract IDbConnection CreateConnection(string connectionString);
    
        protected DapperDBContext(IOptions<DapperDBContextOptions> optionsAccessor)
        {
            _options = optionsAccessor.Value;
    
            _connection = CreateConnection(_options.Configuration);
            _connection.Open();
    
            DebugPrint("Connection started.");
        }
    
        #region Transaction
    
        public void BeginTransaction()
        {
            if (IsTransactionStarted)
                throw new InvalidOperationException("Transaction is already started.");
    
            _transaction = _connection.BeginTransaction();
            IsTransactionStarted = true;
    
            DebugPrint("Transaction started.");
        }
    
        public void Commit()
        {
            if (!IsTransactionStarted)
                throw new InvalidOperationException("No transaction started.");
    
            _transaction.Commit();
            _transaction = null;
    
            IsTransactionStarted = false;
    
            DebugPrint("Transaction committed.");
        }
    
        public void Rollback()
        {
            if (!IsTransactionStarted)
                throw new InvalidOperationException("No transaction started.");
    
            _transaction.Rollback();
            _transaction.Dispose();
            _transaction = null;
    
            IsTransactionStarted = false;
    
            DebugPrint("Transaction rollbacked and disposed.");
        }
    
        #endregion Transaction
    
        #region Dapper Execute & Query
    
        public async Task<int> ExecuteAsync(string sql, object param = null, CommandType commandType = CommandType.Text)
        {
            return await _connection.ExecuteAsync(sql, param, _transaction, _commandTimeout, commandType);
        }
    
        public async Task<IEnumerable<T>> QueryAsync<T>(string sql, object param = null, CommandType commandType = CommandType.Text)
        {
            return await _connection.QueryAsync<T>(sql, param, _transaction, _commandTimeout, commandType);
        }
    
        public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, CommandType commandType = CommandType.Text)
        {
            return await _connection.QueryFirstOrDefaultAsync<T>(sql, param, _transaction, _commandTimeout, commandType);
        }
    
        public async Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TReturn>(string sql, Func<TFirst, TSecond, TReturn> map, object param = null, string splitOn = "Id", CommandType commandType = CommandType.Text)
        {
            return await _connection.QueryAsync(sql, map, param, _transaction, true, splitOn, _commandTimeout, commandType);
        }
    
        #endregion Dapper Execute & Query
    
        public void Dispose()
        {
            if (IsTransactionStarted)
                Rollback();
    
            _connection.Close();
            _connection.Dispose();
            _connection = null;
    
            DebugPrint("Connection closed and disposed.");
        }
    
        private void DebugPrint(string message)
        {
    #if DEBUG
            Debug.Print(">>> UnitOfWorkWithDapper - Thread {0}: {1}", Thread.CurrentThread.ManagedThreadId, message);
    #endif
        }
    }
  • 定义UnitOfWork
    public interface IUnitOfWork : IDisposable
    {
        void SaveChanges();
    }
    
    public class UnitOfWork : IUnitOfWork
    {
        private readonly IContext _context;
    
        public UnitOfWork(IContext context)
        {
            _context = context;
            _context.BeginTransaction();
        }
    
        public void SaveChanges()
        {
            if (!_context.IsTransactionStarted)
                throw new InvalidOperationException("Transaction have already been commited or disposed.");
    
            _context.Commit();
        }
    
        public void Dispose()
        {
            if (_context.IsTransactionStarted)
                _context.Rollback();
        }
    }
  • 定义UnitOfWorkFactory
    public interface IUnitOfWorkFactory
    {
        IUnitOfWork Create();
    }
    
    public class DapperUnitOfWorkFactory : IUnitOfWorkFactory
    {
        private readonly DapperDBContext _context;
    
        public DapperUnitOfWorkFactory(DapperDBContext context)
        {
            _context = context;
        }
    
        public IUnitOfWork Create()
        {
            return new UnitOfWork(_context);
        }
    }
  • 定义服务扩展
    public class DapperDBContextOptions : IOptions<DapperDBContextOptions>
    {
        public string Configuration { get; set; }
    
        DapperDBContextOptions IOptions<DapperDBContextOptions>.Value
        {
            get { return this; }
        }
    }
    
    public static class DapperDBContextServiceCollectionExtensions
    {
        public static IServiceCollection AddDapperDBContext<T>(this IServiceCollection services, Action<DapperDBContextOptions> setupAction) where T : DapperDBContext
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
    
            if (setupAction == null)
            {
                throw new ArgumentNullException(nameof(setupAction));
            }
    
            services.AddOptions();
            services.Configure(setupAction);
            services.AddScoped<DapperDBContext, T>();
            services.AddScoped<IUnitOfWorkFactory, DapperUnitOfWorkFactory>();
    
            return services;
        }
    }
  • 怎么使用
    1. 创建一个自己的Context并继承DapperDBContext。下面测试的TestDBContext是采用MySQL数据库并返回MySqlConnection。

      public class TestDBContext : DapperDBContext
      {
          public TestDBContext(IOptions<DapperDBContextOptions> optionsAccessor) : base(optionsAccessor)
          {
          }
      
          protected override IDbConnection CreateConnection(string connectionString)
          {
              IDbConnection conn = new MySqlConnection(connectionString);
              return conn;
          }
      }
    2. 仓储类里面添加DapperDBContext引用
      public class UowProductRepository : IProductRepository
      {
          private readonly DapperDBContext _context;
          public UowProductRepository(DapperDBContext context)
          {
              _context = context;
          }
      
          public async Task<Product> GetByIDAsync(int id)
          {
              string sql = @"SELECT Id
                                  ,Name
                                  ,Quantity
                                  ,Price
                                  ,CategoryId
                              FROM Product
                              WHERE Id = @Id";
              return await _context.QueryFirstOrDefaultAsync<Product>(sql, new { Id = id });
          }
      
          public async Task<bool> AddAsync(Product prod)
          {
              string sql = @"INSERT INTO Product
                              (Name
                              ,Quantity
                              ,Price
                              ,CategoryId)
                          VALUES
                              (@Name
                              ,@Quantity
                              ,@Price
                              ,@CategoryId)";
              return await _context.ExecuteAsync(sql, prod) > 0;
          }
      }
    3. Startup里面注册服务
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDapperDBContext<TestDBContext>(options => {
              options.Configuration = @"server=127.0.0.1;database=ormdemo;uid=root;pwd=password;SslMode=none;";
          });
      
          services.AddTransient<IProductRepository, UowProductRepository>();
          services.AddTransient<ICategoryRepository, UowCategoryRepository>();
      
          services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
      }
    4. Controller调用
      public class ProductController : ControllerBase
      {
          private readonly IUnitOfWorkFactory _uowFactory;
          private readonly IProductRepository _productRepository;
          private readonly ICategoryRepository _categoryRepository;
      
          public ProductController(IUnitOfWorkFactory uowFactory, IProductRepository productRepository, ICategoryRepository categoryRepository)
          {
              _uowFactory = uowFactory;
              _productRepository = productRepository;
              _categoryRepository = categoryRepository;
          }
      
          [HttpGet("{id}")]
          public async Task<IActionResult> Get(int id)
          {
              var data = await _productRepository.GetByIDAsync(id);
              return Ok(data);
          }
      
          [HttpPost]
          public async Task<IActionResult> Post([FromBody] Product prod)
          {
              if (!ModelState.IsValid)
              {
                  return BadRequest(ModelState);
              }
      
              //await _productRepository.AddAsync(prod);
      
              using (var uow = _uowFactory.Create())
              {
                  await _productRepository.AddAsync(prod);
      
                  uow.SaveChanges();
              }
      
              return NoContent();
          }
      }

源代码

Github

参考

来源:https://www.cnblogs.com/royzshare/p/9522127.html

原文地址:https://www.cnblogs.com/frank0812/p/12199707.html

时间: 2024-10-06 19:51:09

ASP.NET Core 中的 ORM 之 Dapper的相关文章

Asp.net core中的websocket

Websocket是html5后的产物,对于asp.net core中也得到了支持,首先在NuGet中添加Microsoft.AspNetCore.WebSockets的引用(现在是1.0.1版本,2017年3月7日发布的). 首先在Configure中添加中间件 //添加websocket中间件 app.UseWebSockets(); 接下来就要定义自己处理websocket的中间件了,代码如下: using Microsoft.AspNetCore.Http; using System;

ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【解读ServiceCallSite 】

通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的.ServiceProvider最终采用何种方式提供我们所需的服务实例取决于最终选择了怎样的ServiceCallSite,而服务注册是采用的ServiceDescriptor有决定了ServiceCallSite类型的选择.我们将众多不同类型的ServiceCallSite大体分成两组,一组用来创建最终的服务实例,另一类则与生命周

如何在ASP.NET Core中应用Entity Framework

注:本文提到的代码示例下载地址> How to using Entity Framework DB first in ASP.NET Core 如何在ASP.NET Core中应用Entity Framework 首先为大家提醒一点,.NET Core和经典.NET Framework的Library是不通用的,包括Entity Framework! 哪怎么办? 别急,微软为.NET Core发布了.NET Core版本的Entity Framework,具体配置方法与经典.NET Framew

如何在ASP.NET Core中实现一个基础的身份认证

注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ASP.NET终于可以跨平台了,但是不是我们常用的ASP.NET, 而是叫一个ASP.NET Core的新平台,他可以跨Windows, Linux, OS X等平台来部署你的web应用程序,你可以理解为,这个框架就是ASP.NET的下一个版本,相对于传统ASP.NET程序,它还是有一些不同的地方的,比

ASP.NET Core中使用xUnit进行单元测试

单元测试的功能自从MVC的第一个版本诞生的时候,就是作为一个重要的卖点来介绍的,通常在拿MVC与webform比较的时候,单元测试就是必杀底牌,把webform碾压得一无是处. 单元测试的重要性不用多说了,有单元测试的做兜底的项目,好比给开发人员买了份保险,当然这个保险的质量取决于单元测试的质量,那些一路Mock的单元测试,看起来很美,但是什么都cover不到.目前工作中的一个老项目,有2万多个单元测试用例,其中不少是用心之作,真正落实到了业务逻辑,开发人员可以放心的去修改代码,当然一切都必须按

ASP.NET Core中的依赖注入(2):依赖注入(DI)

参考页面: http://www.yuanjiaocheng.net/ASPNET-CORE/project-layout.html http://www.yuanjiaocheng.net/ASPNET-CORE/projectjson.html http://www.yuanjiaocheng.net/ASPNET-CORE/core-configuration.html http://www.yuanjiaocheng.net/ASPNET-CORE/core-middleware.htm

NLog在asp.net core中的应用

Asp.net core中,自带的Log是在当selfhost运行时,在控制台中输出,不便于查阅,如果用一个log架框,把日志持久化,便于查询. NLog是一个免费的日志记录框架,专门为.net平台下的框架提供日志功能,本文主要说明asp.net core下怎么使用NLog. 首先用Nuget安装NLog.Extensions.Logging和NLog.Web.AspNetCore两个类库. 修改project.json,在publishOptions中添加"nlog.config节点"

ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】

到目前为止,我们定义的ServiceProvider已经实现了基本的服务提供和回收功能,但是依然漏掉了一些必需的细节特性.这些特性包括如何针对IServiceProvider接口提供一个ServiceProvider对象,何创建ServiceScope,以及如何提供一个服务实例的集合. 一.提供一个ServiceProvider对象 我们知道当将服务类型指定为IServiceProvider接口并调用ServiceProvider的GetService方法是,ServiceProvider对象本

ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】

本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还对这个依赖注入系统底层的实现原理具有好奇心,可以继续阅读这一节的内容. 目录一.ServiceCallSite 二.Service 三.ServiceEntry 四.ServiceTable 五.ServiceProvider 作为DI容器的体现,ServiceProvider是ASP.NET Co