zoukankan      html  css  js  c++  java
  • 同时支持EF+Dapper的混合仓储,助你快速搭建数据访问层

    背景

    17年开始,公司开始向DotNet Core转型,面对ORM工具的选型,当时围绕Dapper和EF发生了激烈的讨论。项目团队更加关注快速交付,他们主张使用EF这种能快速开发的ORM工具;而在线业务团队对性能有更高的要求,他们更希望使用能直接执行Sql语句的Dapper,这样可控性更高。而对于架构团队来说,满足开发团队的各种需求,提高他们的开发效率是最核心的价值所在,所以当时决定做一个混合型的既支持EF又支持dapper的数据仓储。

    为什么选择EF+Dapper

    目前来说EF和Dapper是.NET平台最主流的ORM工具,团队成员的接受程度很高,相关的资料非常齐全,学习成本很低,各种坑也最少。

    介绍

    1. 它不是一个ORM工具,它不做任何关于数据底层的操作
    2. 它是一个简易封装的数据库仓储和工作单元模型
    3. 能帮助你快速的构建项目的数据访问层
    4. 经过了2年多时间,10个项目组,大小近100多个线上项目的考验
    5. 支持EF和Dapper,可以在项目中随意切换使用
    6. 支持工作单元模式,也支持传统事务
    7. 支持Mysql和Mssql
    8. 支持同步和异步操作,推荐使用异步

    PS: 简单操作使用EF,复杂sql操作使用Dapper是快速开发的秘诀。

    使用方法

    引入nuget

    <PackageReference Include="Leo.Chimp" Version="2.1.1" />
    

    创建实体对象,继承IEntity

    public class School : IEntity
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }
    

    创建仓储接口和实现类,分别继承IRepository和EfCoreRepository

    public interface ISchoolRepository : IRepository<School>
    {
    }
    public class SchoolRepository: EfCoreRepository<School>,ISchoolRepository
    {
        public SchoolRepository(DbContext context) : base(context)
        {
        }
    }
    

    创建上下文,继承BaseDbContext,如果你不需要操作上下文可以不用做这一步

    public class ChimpDbContext : BaseDbContext
    {
        public ChimpDbContext(DbContextOptions options) : base(options)
        {
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //your code
        }
    }
    

    注入服务

    services.AddChimp<ChimpDbContext>(
                    opt =>
                    opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123")
    );
    

    如果你没有创建上下文

    services.AddChimp(
                    opt =>
                    opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123")
    );
    

    在Controller中使用

    public class ValuesController : ControllerBase
    {
        private readonly ISchoolRepository _schoolRepository;
        private readonly IUnitOfWork _unitOfWork;
        public ValuesController(ISchoolRepository schoolRepository, IUnitOfWork unitOfWork)
        {
            _schoolRepository = schoolRepository;
            _unitOfWork = unitOfWork;
        }
    }
    

    详细使用说明

    查询

    //根据主键查询
    _schoolRepository.GetById(Id)
    
    //不带追踪的查询,返回数据不能用于更新或删除操作,性能快
    schoolRepository.TableNoTracking.First(x => x.Id == Id);
    
    //带追踪的查询,返回数据可以用于更新或删除操作,性能稍慢
    schoolRepository.Table.First(x => x.Id == Id);
    
    //分页查询
    _schoolRepository.TableNoTracking.ToPagedList(1,10);
    
    //sql语句查询
    _unitOfWork.QueryAsync<School>("select * from school");
    
    
    //sql分页查询
    _unitOfWork.QueryPagedListAsync<School>(1, 10, "select * from school order by id");
    

    关于查询,暴露了返回IQueryable的TableNoTracking、Table这两个属性,让开发人员自己组装Lambda表达式进行查询操作

    新增

    //新增,支持批量新增
    _schoolRepository.Insert(school);
    await _unitOfWork.SaveChangesAsync();
    
    //sql语句新增
    await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",school);
    

    编辑

    //编辑,支持批量编辑
    var school = await _schoolRepository.GetByIdAsync(Id);
    school.Name="newschool";
    _schoolRepository.Update(school);
    await _unitOfWork.SaveChangesAsync();
    
    //编辑,不用先查询
    var school = new School
    {
        Id = "xxxxxx",
        Name = "newschool"
    };
    _schoolRepository.Update(school, x => x.Name);
    await _unitOfWork.SaveChangesAsync();
    
    //sql语句编辑
    await _unitOfWork.ExecuteAsync("update school set name=@Name where id=@Id",school);
    

    删除

    //删除,支持批量删除
    _schoolRepository.Delete(school);
    await _unitOfWork.SaveChangesAsync();
    
    //根据lambda删除
    _schoolRepository.Delete(x => x.Id == Id);
    await _unitOfWork.SaveChangesAsync();
    

    事务

    //工作单元模式使用事务
    await _schoolRepository.InsertAsync(school1);
    await _schoolRepository.InsertAsync(school2);
    await _unitOfWork.SaveChangesAsync();
    
    //dapper使用事务
    using (var tran = _unitOfWork.BeginTransaction())
    {
        try
        {
            await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
                school1,tran);
            await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
                school2,tran);
            tran.Commit();
        }
        catch (Exception e)
        {
            tran.Rollback();
        }
    }
    
    //dapper+ef混合使用事务
    using (var tran = _unitOfWork.BeginTransaction())
    {
        try
        {
            await _schoolRepository.InsertAsync(school1);
            await _unitOfWork.SaveChangesAsync();
    
            await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
                school2);
            tran.Commit();
        }
        catch (Exception e)
        {
            tran.Rollback();
        }
    }
    

    高级用法

    //通过GetConnection可以使用更多dapper扩展的方法
    await _unitOfWork.GetConnection().QueryAsync("select * from school");
    

    写在最后

    Chimp核心是基于EF和Dapper的,所以EF和Dapper的功能都可以使用。比如导航属性,字段映射等等。这个库是线上项目核心依赖,会长期更新维护,希望大家能提出更好的意见。
    QQ群:687800650 有问题可以加群交流

    项目地址

    数据库脚本在根目录的sqlscript文件夹里面
    github地址

  • 相关阅读:
    Redis可视化客户端管理及监控工具TreeNMS
    Memcache分布式锁 转发 https://www.cnblogs.com/li150dan/p/9529090.html
    MemCache在.NET中使用Memcached.ClientLibrary详解 转发 https://www.cnblogs.com/li150dan/p/9529112.html
    C# Memcache分布式缓存简单入门
    ASP.NET cache缓存的用法
    .net core redis 驱动推荐,为什么不使用 StackExchange.Redis 转发 https://www.cnblogs.com/kellynic/p/9325816.html
    StackExchange.Redis通用封装类分享
    Windows下Redis缓存服务器的使用 .NET StackExchange.Redis Redis Desktop Manager 转发非原创
    .Net使用Redis详解之ServiceStack.Redis(七) 转载https://www.cnblogs.com/knowledgesea/p/5032101.html
    redis的过期时间和过期删除机制
  • 原文地址:https://www.cnblogs.com/longxianghui/p/11635928.html
Copyright © 2011-2022 走看看