.Net Core 2.2 教程:DI、EF Core与三层架构项目搭建指南
笔记:
近两年.Net Core发展的很快,目前最新版为3.0预览版,之前在网上买了一本1.1版书籍都还没来得及看呢,估计现在拿出来看也毫无意义了。已多年.net工作经验,看书不如直接实际上手来得快,遇到问题再度娘吧。正好最近公司不忙时,抽空亲手搭建.Net Core项目熟悉一下,说起.net那最自豪的就是VS编译器了,强大的辅助功能很多中小型项目只需要下一步就可以创建完成。这里我们还需要简单封装一下,使用仓储模式对数据访问层封装和Service层封装,通过.net自带DI依赖注入进行创建对象。对于初学者的我只能简单的封装一下,接下来我会一一讲解框架的思路,如有更好的方案或不明的地方欢迎留言。转载请备注来源:https://www.cnblogs.com/han1982/p/11058788.html
下面是已搭建好的框架结构:
第一步:创建解决方案
使用Visual Studio 2019编译器创建解决方案,默认安装vs2019自带的.NET Core 2.1,创建.NET Core 2.2版需要下载SDK安装。
https://dotnet.microsoft.com/download/visual-studio-sdks?utm_source=getdotnetsdk&utm_medium=referral
接下来可以创建项目了,首先创建的是数据访问层,我们命名为common.Core,另外给他创建一个接口层common.Interface。
(注意所有程序集创建必须为.Net Core版,为以后发布垮平台考虑)
第二步:创建Model层
封装仓储层之前先来创建数据Model层,Nuget添加EF Core相关引用,工具 - NuGet包管理器 - 程序包管理器控制台,默认项目选择Model程序集依次安装以下组件包。
Install-Package Microsoft.EntityFrameworkCore -version 2.2.4
Install-Package Microsoft.EntityFrameworkCore.SqlServer -version 2.2.4
Install-Package Microsoft.EntityFrameworkCore.Tools -version 2.2.4
也可以在项目中找到依赖项,右键管理NuGet管理包方式进行添加。
Microsoft.EntityFrameworkCore.Tools中包含了Microsoft.EntityFrameworkCore.Design依赖包,不需要单独安装了。
这里我使用的是Database First模式,使用工具Scaffold-DbContext(数据库上下文脚手架)来生成model类文件和DbContext。
执行以下命令:-o (OutputDir) 指定用于输出类的目录 -f (Force) 生成时覆盖现有文件 -Context 指定生成的DbContext类的名称,省略的话按数据库名称生成DbContext类文件。
Scaffold-DbContext "server=.;database=ConCard;uid=sa;pwd=123123;" Microsoft.EntityFrameworkCore.SqlServer -O Models -F
出现错误:VS2019有个小小BUG,默认项目选中以后最终执行的不是被选中程序集,这里需要把model程序集设为启动项目,再次执行。
自动生成所有类模型文件,ConCardContext.cs数据库上下文也都帮你创建好了,这样就省去了我们手动写DBSet时间。
第三步:封装数据访问层
数据访问层主要封装仓储Repository和工作单元UnitOfWork,我把这两个合并到一个类中实现,通过简单工厂方式创建实例对象。
我们直接把ConCardContext.cs这个类复制到common.Core程序集中,把命名空间修改为common.Core。
这时应该报错,因为common.Core项目中没有引用EFCore依赖包,按之前Model层添加一样,使用Install-Package命令为common.Core添加依赖包,Tools可以不用安装。
依赖项中,右键添加引用,把Model和common.Interface项目引用,ConCardContext.cs中using model就不会报错了。
接下来修改下ConCardContext:
重写SaveChanges()方法
public override int SaveChanges() { return base.SaveChanges(true); }
删除OnConfiguring()方法,因为我们不需要在这里配置数据库连接,后面通过读取配置方式设置。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) { optionsBuilder.UseSqlServer("server=.;database=ConCard;uid=sa;pwd=123123;"); } }
common.Interface程序集中创建IconcardContext接口,ConCardContext类中继承自这个接口。(主要用来后期使用DI依赖注入使用,不用接口也可以用DbContext代替)
ConCardContext类:
using System; using com.Synjones.Model.Models; using common.Interface; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; namespace common.Core { public partial class ConCardContext : DbContext, IconcardContext { public ConCardContext() { } public ConCardContext(DbContextOptions<ConCardContext> options) : base(options) { } public override int SaveChanges() { return base.SaveChanges(true); } public virtual DbSet<Admin> Admin { get; set; } public virtual DbSet<User> User { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); modelBuilder.Entity<Admin>(entity => { entity.Property(e => e.PassWord).HasMaxLength(50); entity.Property(e => e.UserId).HasMaxLength(50); }); modelBuilder.Entity<User>(entity => { entity.Property(e => e.Name).HasMaxLength(50); entity.Property(e => e.Phone).HasMaxLength(50); }); } } }
开始继续创建Repository.cs仓储类,它是一个泛型类,并且拥有一个带有参数的构造方法,通过构造方法获得当前DbContext上下文对象,泛型类为指定Model类型,通过DbContext.Set<T>()方法最终得到相应的DbSet<T>对象来操作工作单元。
当然我们也要给他定义一个接口IRepository接口:
using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Linq.Expressions; using System.Text; namespace common.Interface { public interface IRepository<T> : IDisposable where T : class { /// <summary> /// 显式开启数据上下文事务 /// </summary> /// <param name="isolationLevel">指定连接的事务锁定行为</param> void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified); /// <summary> /// 提交事务的更改 /// </summary> void Commit(); /// <summary> /// 显式回滚事务,仅在显式开启事务后有用 /// </summary> void Rollback(); /// <summary> /// 提交当前单元操作的更改 /// </summary> int SaveChanges(); /// <summary> /// 获取 当前实体类型的查询数据集,数据将使用不跟踪变化的方式来查询,当数据用于展现时,推荐使用此数据集,如果用于新增,更新,删除时,请使用<see cref="TrackEntities"/>数据集 /// </summary> IQueryable<T> Entities { get; } /// <summary> /// 获取 当前实体类型的查询数据集,当数据用于新增,更新,删除时,使用此数据集,如果数据用于展现,推荐使用<see cref="Entities"/>数据集 /// </summary> IQueryable<T> TrackEntities { get; } /// <summary> /// 插入 - 通过实体对象添加 /// </summary> /// <param name="entity">实体对象</param> /// <param name="isSave">是否执行</param> /// /// <returns></returns> T Add(T entity, bool isSave = true); /// <summary> /// 批量插入 - 通过实体对象集合添加 /// </summary> /// <param name="entitys">实体对象集合</param> /// <param name="isSave">是否执行</param> void AddRange(IEnumerable<T> entitys, bool isSave = true); /// <summary> /// 删除 - 通过实体对象删除 /// </summary> /// <param name="entity">实体对象</param> /// <param name="isSave">是否执行</param> void Delete(T entity, bool isSave = true); /// <summary> /// 批量删除 - 通过实体对象集合删除 /// </summary> /// <param name="entitys">实体对象集合</param> /// <param name="isSave">是否执行</param> void Delete(bool isSave = false, params T[] entitys); /// <summary> /// 删除 - 通过主键ID删除 /// </summary> /// <param name="id">主键ID</param> /// <param name="isSave">是否执行</param> void Delete(object id, bool isSave = true); /// <summary> /// 批量删除 - 通过条件删除 /// </summary> /// <param name="where">过滤条件</param> /// <param name="isSave">是否执行</param> void Delete(Expression<Func<T, bool>> @where, bool isSave = true); /// <summary> /// 修改 - 通过实体对象修改 /// </summary> /// <param name="entity">实体对象</param> /// <param name="isSave"></param> void Update(T entity, bool isSave = true); /// <summary> /// 批量修改 - 通过实体对象集合修改 /// </summary> /// <param name="entitys">实体对象集合</param> /// <param name="isSave"></param> void Update(bool isSave = true, params T[] entitys); /// <summary> /// 是否满足条件 /// </summary> /// <param name="where">过滤条件</param> /// <returns></returns> bool Any(Expression<Func<T, bool>> @where); /// <summary> /// 返回总条数 /// </summary> /// <returns></returns> int Count(); /// <summary> /// 返回总条数 - 通过条件过滤 /// </summary> /// <param name="where">过滤条件</param> /// <returns></returns> int Count(Expression<Func<T, bool>> @where); /// <summary> /// 返回第一条记录 /// </summary> /// <param name="where">过滤条件</param> /// <returns></returns> T FirstOrDefault(Expression<Func<T, bool>> @where); /// <summary> /// 返回第一条记录 - 通过条件过滤 /// </summary> /// <typeparam name="TOrder">排序约束</typeparam> /// <param name="where">过滤条件</param> /// <param name="order">排序条件</param> /// <param name="isDesc">排序方式</param> /// <returns></returns> T FirstOrDefault<TOrder>(Expression<Func<T, bool>> @where, Expression<Func<T, TOrder>> order, bool isDesc = false); /// <summary> /// 去重查询 /// </summary> /// <param name="where">过滤条件</param> /// <returns></returns> IQueryable<T> Distinct(Expression<Func<T, bool>> @where); /// <summary> /// 条件查询 /// </summary> /// <param name="where">过滤条件</param> /// <returns></returns> IQueryable<T> Where(Expression<Func<T, bool>> @where); /// <summary> /// 条件查询 - 支持排序 /// </summary> /// <typeparam name="TOrder">排序约束</typeparam> /// <param name="where">过滤条件</param> /// <param name="order">排序条件</param> /// <param name="isDesc">排序方式</param> /// <returns></returns> IQueryable<T> Where<TOrder>(Expression<Func<T, bool>> @where, Expression<Func<T, TOrder>> order, bool isDesc = false); /// <summary> /// 条件分页查询 - 支持排序 /// </summary> /// <typeparam name="TOrder">排序约束</typeparam> /// <param name="where">过滤条件</param> /// <param name="order">排序条件</param> /// <param name="pageIndex">当前页码</param> /// <param name="pageSize">每页记录条数</param> /// <param name="count">返回总条数</param> /// <param name="isDesc">是否倒序</param> /// <returns></returns> IEnumerable<T> Where<TOrder>(Func<T, bool> @where, Func<T, TOrder> order, int pageIndex, int pageSize, out int count, bool isDesc = false); /// <summary> /// 条件分页查询 - 支持排序 - 支持Select导航属性查询 /// </summary> /// <typeparam name="TOrder">排序约束</typeparam> /// <param name="where">过滤条件</param> /// <param name="order">排序条件</param> /// <param name="pageIndex">当前页码</param> /// <param name="pageSize">每页记录条数</param> /// <param name="count">返回总条数</param> /// <param name="isDesc">是否倒序</param> /// <returns></returns> IQueryable<T> Where<TOrder>(Expression<Func<T, bool>> @where, Expression<Func<T, TOrder>> order, int pageIndex, int pageSize, out int count, bool isDesc = false); /// <summary> /// 获取所有数据 /// </summary> /// <returns></returns> IQueryable<T> GetAll(); /// <summary> /// 获取所有数据 - 支持排序 /// </summary> /// <typeparam name="TOrder">排序约束</typeparam> /// <param name="order">排序条件</param> /// <param name="isDesc">排序方式</param> /// <returns></returns> IQueryable<T> GetAll<TOrder>(Expression<Func<T, TOrder>> order, bool isDesc = false); /// <summary> /// 根据ID查询 /// </summary> /// <typeparam name="Ttype">字段类型</typeparam> /// <param name="id">主键ID</param> /// <returns></returns> T GetById<Ttype>(Ttype id); /// <summary> /// 获取最大值 /// </summary> /// <typeparam name="Ttype">字段类型</typeparam> /// <param name="column">字段条件</param> /// <returns></returns> Ttype Max<Ttype>(Expression<Func<T, Ttype>> column); /// <summary> /// 获取最大值 /// </summary> /// <typeparam name="Ttype">字段类型</typeparam> /// <param name="column">字段条件</param> /// <param name="where">过滤条件</param> /// <returns></returns> Ttype Max<Ttype>(Expression<Func<T, Ttype>> column, Expression<Func<T, bool>> @where); /// <summary> /// 获取最小值 /// </summary> /// <typeparam name="Ttype">字段类型</typeparam> /// <param name="column">字段条件</param> /// <returns></returns> Ttype Min<Ttype>(Expression<Func<T, Ttype>> column); /// <summary> /// 获取最小值 /// </summary> /// <typeparam name="Ttype">字段类型</typeparam> /// <param name="column">字段条件</param> /// <param name="where">过滤条件</param> /// <returns></returns> Ttype Min<Ttype>(Expression<Func<T, Ttype>> column, Expression<Func<T, bool>> @where); /// <summary> /// 获取总数 /// </summary> /// <typeparam name="TType">字段类型</typeparam> /// <param name="selector">字段条件</param> /// <param name="where">过滤条件</param> /// <returns></returns> TType Sum<TType>(Expression<Func<T, TType>> selector, Expression<Func<T, bool>> @where) where TType : new(); } }
Repository类,实现了CRUD基本功能的封装:
using common.Interface; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Linq.Expressions; using System.Text; namespace common.Core { public class Repository<T> : IRepository<T> where T : class { private ConCardContext _dbContext; private readonly DbSet<T> _dbSet; private readonly string _connStr; public Repository(IconcardContext mydbcontext) { this._dbContext = mydbcontext as ConCardContext; this._dbSet = _dbContext.Set<T>(); this._connStr = _dbContext.Database.GetDbConnection().ConnectionString; } public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { if (this._dbContext.Database.CurrentTransaction == null) { this._dbContext.Database.BeginTransaction(isolationLevel); } } public void Commit() { var transaction = this._dbContext.Database.CurrentTransaction; if (transaction != null) { try { transaction.Commit(); } catch (Exception) { transaction.Rollback(); throw; } } } public void Rollback() { if (this._dbContext.Database.CurrentTransaction != null) { this._dbContext.Database.CurrentTransaction.Rollback(); } } public int SaveChanges() { return this._dbContext.SaveChanges(); } public IQueryable<T> Entities { get { return this._dbSet.AsNoTracking(); } } public IQueryable<T> TrackEntities { get { return this._dbSet; } } public T Add(T entity, bool isSave = true) { this._dbSet.Add(entity); if (isSave) { this.SaveChanges(); } return entity; } public void AddRange(IEnumerable<T> entitys, bool isSave = true) { this._dbSet.AddRange(entitys); if (isSave) { this.SaveChanges(); } } public void Delete(T entity, bool isSave = true) { this._dbSet.Remove(entity); if (isSave) { this.SaveChanges(); } } public void Delete(bool isSave = true, params T[] entitys) { this._dbSet.RemoveRange(entitys); if (isSave) { this.SaveChanges(); } } public void Delete(object id, bool isSave = true) { this._dbSet.Remove(this._dbSet.Find(id)); if (isSave) { this.SaveChanges(); } } public void Delete(Expression<Func<T, bool>> @where, bool isSave = true) { T[] entitys = this._dbSet.Where<T>(@where).ToArray();