笔记:

  近两年.Net Core发展的很快,目前最新版为3.0预览版,之前在网上买了一本1.1版书籍都还没来得及看呢,估计现在拿出来看也毫无意义了。已多年.net工作经验,看书不如直接实际上手来得快,遇到问题再度娘吧。正好最近公司不忙时,抽空亲手搭建.Net Core项目熟悉一下,说起.net那最自豪的就是VS编译器了,强大的辅助功能很多中小型项目只需要下一步就可以创建完成。这里我们还需要简单封装一下,使用仓储模式对数据访问层封装和Service层封装,通过.net自带DI依赖注入进行创建对象。对于初学者的我只能简单的封装一下,接下来我会一一讲解框架的思路,如有更好的方案或不明的地方欢迎留言。转载请备注来源:

下面是已搭建好的框架结构:

.net三层架构介绍 .net core 三层_.net三层架构介绍

第一步:创建解决方案

使用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三层架构介绍 .net core 三层_Express_02

(注意所有程序集创建必须为.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

.net三层架构介绍 .net core 三层_.net三层架构介绍_03

也可以在项目中找到依赖项,右键管理NuGet管理包方式进行添加。

.net三层架构介绍 .net core 三层_Express_04

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

.net三层架构介绍 .net core 三层_Express_05

出现错误:VS2019有个小小BUG,默认项目选中以后最终执行的不是被选中程序集,这里需要把model程序集设为启动项目,再次执行。

.net三层架构介绍 .net core 三层_.net三层架构介绍_06

自动生成所有类模型文件,ConCardContext.cs数据库上下文也都帮你创建好了,这样就省去了我们手动写DBSet时间。

第三步:封装数据访问层

数据访问层主要封装仓储Repository和工作单元UnitOfWork,我把这两个合并到一个类中实现,通过简单工厂方式创建实例对象。

我们直接把ConCardContext.cs这个类复制到common.Core程序集中,把命名空间修改为common.Core。

这时应该报错,因为common.Core项目中没有引用EFCore依赖包,按之前Model层添加一样,使用Install-Package命令为common.Core添加依赖包,Tools可以不用安装。

.net三层架构介绍 .net core 三层_vs合并项目_07

依赖项中,右键添加引用,把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 options)
            : base(options)
        {
        }public override int SaveChanges()
        {return base.SaveChanges(true);
        }public virtual DbSet Admin { get; set; }public virtual DbSet User { get; set; }protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
            modelBuilder.Entity(entity =>
            {
                entity.Property(e => e.PassWord).HasMaxLength(50);
                entity.Property(e => e.UserId).HasMaxLength(50);
            });
            modelBuilder.Entity(entity =>
            {
                entity.Property(e => e.Name).HasMaxLength(50);
                entity.Property(e => e.Phone).HasMaxLength(50);
            });
        }
    }
}

开始继续创建Repository.cs仓储类,它是一个泛型类,并且拥有一个带有参数的构造方法,通过构造方法获得当前DbContext上下文对象,泛型类为指定Model类型,通过DbContext.Set()方法最终得到相应的DbSet对象来操作工作单元。

当然我们也要给他定义一个接口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 : IDisposable where T : class
    {/// /// 显式开启数据上下文事务/// /// 指定连接的事务锁定行为void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified);/// /// 提交事务的更改/// void Commit();/// /// 显式回滚事务,仅在显式开启事务后有用/// void Rollback();/// /// 提交当前单元操作的更改/// int SaveChanges();/// /// 获取 当前实体类型的查询数据集,数据将使用不跟踪变化的方式来查询,当数据用于展现时,推荐使用此数据集,如果用于新增,更新,删除时,请使用数据集/// 
        IQueryable Entities { get; }/// /// 获取 当前实体类型的查询数据集,当数据用于新增,更新,删除时,使用此数据集,如果数据用于展现,推荐使用数据集/// 
        IQueryable TrackEntities { get; }/// /// 插入 - 通过实体对象添加/// /// 实体对象/// 是否执行/// /// 
        T Add(T entity, bool isSave = true);/// /// 批量插入 - 通过实体对象集合添加/// /// 实体对象集合/// 是否执行void AddRange(IEnumerable entitys, bool isSave = true);/// /// 删除 - 通过实体对象删除/// /// 实体对象/// 是否执行void Delete(T entity, bool isSave = true);/// /// 批量删除 - 通过实体对象集合删除/// /// 实体对象集合/// 是否执行void Delete(bool isSave = false, params T[] entitys);/// /// 删除 - 通过主键ID删除/// /// 主键ID/// 是否执行void Delete(object id, bool isSave = true);/// /// 批量删除 - 通过条件删除/// /// 过滤条件/// 是否执行void Delete(Expressionbool>> @where, bool isSave = true);/// /// 修改 - 通过实体对象修改/// /// 实体对象/// void Update(T entity, bool isSave = true);/// /// 批量修改 - 通过实体对象集合修改/// /// 实体对象集合/// void Update(bool isSave = true, params T[] entitys);/// /// 是否满足条件/// /// 过滤条件/// bool Any(Expressionbool>> @where);/// /// 返回总条数/// /// int Count();/// /// 返回总条数 - 通过条件过滤/// /// 过滤条件/// int Count(Expressionbool>> @where);/// /// 返回第一条记录/// /// 过滤条件/// 
        T FirstOrDefault(Expressionbool>> @where);/// /// 返回第一条记录 - 通过条件过滤/// /// 排序约束/// 过滤条件/// 排序条件/// 排序方式/// 
        T FirstOrDefault(Expressionbool>> @where, Expression> order, bool isDesc = false);/// /// 去重查询/// /// 过滤条件/// 
        IQueryable Distinct(Expressionbool>> @where);/// /// 条件查询/// /// 过滤条件/// 
        IQueryable Where(Expressionbool>> @where);/// /// 条件查询 - 支持排序/// /// 排序约束/// 过滤条件/// 排序条件/// 排序方式/// 
        IQueryable Where(Expressionbool>> @where, Expression> order, bool isDesc = false);/// /// 条件分页查询 - 支持排序/// /// 排序约束/// 过滤条件/// 排序条件/// 当前页码/// 每页记录条数/// 返回总条数/// 是否倒序/// 
        IEnumerable Where(Funcbool> @where, Func order, int pageIndex, int pageSize, out int count, bool isDesc = false);/// /// 条件分页查询 - 支持排序 - 支持Select导航属性查询/// /// 排序约束/// 过滤条件/// 排序条件/// 当前页码/// 每页记录条数/// 返回总条数/// 是否倒序/// 
        IQueryable Where(Expressionbool>> @where, Expression> order, int pageIndex, int pageSize, out int count, bool isDesc = false);/// /// 获取所有数据/// /// 
        IQueryable GetAll();/// /// 获取所有数据 - 支持排序/// /// 排序约束/// 排序条件/// 排序方式/// 
        IQueryable GetAll(Expression> order, bool isDesc = false);/// /// 根据ID查询/// /// 字段类型/// 主键ID/// 
        T GetById(Ttype id);/// /// 获取最大值/// /// 字段类型/// 字段条件/// 
        Ttype Max(Expression> column);/// /// 获取最大值/// /// 字段类型/// 字段条件/// 过滤条件/// 
        Ttype Max(Expression> column, Expressionbool>> @where);/// /// 获取最小值/// /// 字段类型/// 字段条件/// 
        Ttype Min(Expression> column);/// /// 获取最小值/// /// 字段类型/// 字段条件/// 过滤条件/// 
        Ttype Min(Expression> column, Expressionbool>> @where);/// /// 获取总数/// /// 字段类型/// 字段条件/// 过滤条件/// 
        TType Sum(Expression> selector, Expressionbool>> @where) where TType : new();
    }
}

Repository类,实现了CRUD基本功能的封装:

.net三层架构介绍 .net core 三层_.net三层架构介绍_08

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 : IRepositorywhere T : class
    {private ConCardContext _dbContext;private readonly DbSet _dbSet;private readonly string _connStr;public Repository(IconcardContext mydbcontext)
        {this._dbContext = mydbcontext as ConCardContext;this._dbSet = _dbContext.Set();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 Entities
        {get { return this._dbSet.AsNoTracking(); }
        }public IQueryable 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 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(Expressionbool>> @where, bool isSave = true)
        {
            T[] entitys = this._dbSet.Where(@where).ToArray();if (entitys.Length > 0)
            {this._dbSet.RemoveRange(entitys);
            }if (isSave)
            {this.SaveChanges();
            }
        }public void Update(T entity, bool isSave = true)
        {var entry = this._dbContext.Entry(entity);if (entry.State == EntityState.Detached)
            {
                entry.State = EntityState.Modified;
            }if (isSave)
            {this.SaveChanges();
            }
        }public void Update(bool isSave = true, params T[] entitys)
        {var entry = this._dbContext.Entry(entitys);if (entry.State == EntityState.Detached)
            {
                entry.State = EntityState.Modified;
            }if (isSave)
            {this.SaveChanges();
            }
        }public bool Any(Expressionbool>> @where)
        {return this._dbSet.AsNoTracking().Any(@where);
        }public int Count()
        {return this._dbSet.AsNoTracking().Count();
        }public int Count(Expressionbool>> @where)
        {return this._dbSet.AsNoTracking().Count(@where);
        }public T FirstOrDefault(Expressionbool>> @where)
        {return this._dbSet.AsNoTracking().FirstOrDefault(@where);
        }public T FirstOrDefault(Expressionbool>> @where, Expression> order, bool isDesc = false)
        {if (isDesc)
            {return this._dbSet.AsNoTracking().OrderByDescending(order).FirstOrDefault(@where);
            }else
            {return this._dbSet.AsNoTracking().OrderBy(order).FirstOrDefault(@where);
            }
        }public IQueryable Distinct(Expressionbool>> @where)
        {return this._dbSet.AsNoTracking().Where(@where).Distinct();
        }public IQueryable Where(Expressionbool>> @where)
        {return this._dbSet.Where(@where);
        }public IQueryable Where(Expressionbool>> @where, Expression> order, bool isDesc = false)
        {if (isDesc)
            {return this._dbSet.Where(@where).OrderByDescending(order);
            }else
            {return this._dbSet.Where(@where).OrderBy(order);
            }
        }public IEnumerable Where(Funcbool> @where, Func order, int pageIndex, int pageSize, out int count, bool isDesc = false)
        {
            count = Count();if (isDesc)
            {return this._dbSet.Where(@where).OrderByDescending(order).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }else
            {return this._dbSet.Where(@where).OrderBy(order).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
        }public IQueryable Where(Expressionbool>> @where, Expression> order, int pageIndex, int pageSize, out int count, bool isDesc = false)
        {
            count = Count();if (isDesc)
            {return this._dbSet.Where(@where).OrderByDescending(order).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }else
            {return this._dbSet.Where(@where).OrderBy(order).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
        }public IQueryable GetAll()
        {return this._dbSet.AsNoTracking();
        }public IQueryable GetAll(Expression> order, bool isDesc = false)
        {if (isDesc)
            {return this._dbSet.AsNoTracking().OrderByDescending(order);
            }else
            {return this._dbSet.AsNoTracking().OrderBy(order);
            }
        }public T GetById(Ttype id)
        {return this._dbSet.Find(id);
        }public Ttype Max(Expression> column)
        {if (this._dbSet.AsNoTracking().Any())
            {return this._dbSet.AsNoTracking().Max(column);
            }return default(Ttype);
        }public Ttype Max(Expression> column, Expressionbool>> @where)
        {if (this._dbSet.AsNoTracking().Any(@where))
            {return this._dbSet.AsNoTracking().Where(@where).Max(column);
            }return default(Ttype);
        }public Ttype Min(Expression> column)
        {if (this._dbSet.AsNoTracking().Any())
            {return this._dbSet.AsNoTracking().Min(column);
            }return default(Ttype);
        }public Ttype Min(Expression> column, Expressionbool>> @where)
        {if (this._dbSet.AsNoTracking().Any(@where))
            {return this._dbSet.AsNoTracking().Where(@where).Min(column);
            }return default(Ttype);
        }public TType Sum(Expression> selector, Expressionbool>> @where) where TType : new()
        {object result = 0;if (new TType().GetType() == typeof(decimal))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressiondecimal>>);
            }if (new TType().GetType() == typeof(decimal?))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressiondecimal?>>);
            }if (new TType().GetType() == typeof(double))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressiondouble>>);
            }if (new TType().GetType() == typeof(double?))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressiondouble?>>);
            }if (new TType().GetType() == typeof(float))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressionfloat>>);
            }if (new TType().GetType() == typeof(float?))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressionfloat?>>);
            }if (new TType().GetType() == typeof(int))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressionint>>);
            }if (new TType().GetType() == typeof(int?))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressionint?>>);
            }if (new TType().GetType() == typeof(long))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressionlong>>);
            }if (new TType().GetType() == typeof(long?))
            {
                result = this._dbSet.AsNoTracking().Where(where).Sum(selector as Expressionlong?>>);
            }return (TType)result;
        }public void Dispose()
        {this._dbContext.Dispose();
        }
    }
}

.net三层架构介绍 .net core 三层_.net三层架构介绍_09

这样仓储模式就创建好了,接下来想办法通过DI创建实例,而不是直接在Service层new一个实例,但是Repository是泛型类,通过DI创建需要设置,所有不同model类都要声明一遍,这里只能使用简单工厂来处理下。

添加RepositoryFactory类和IRepositoryFactory接口,接口中定义IRepository CreateRepository(IconcardContext mydbcontext) where T : class; 通过实现CreateRepository方法来创建不同数据模型的工作单元。

IRepositoryFactory接口:

.net三层架构介绍 .net core 三层_.net三层架构介绍_08

.net三层架构介绍 .net core 三层_.net三层架构介绍_09

using System;using System.Collections.Generic;using System.Text;namespace common.Interface
{public interface IRepositoryFactory
    {
        IRepository CreateRepository(IconcardContext mydbcontext) where T : class;
    }
}

RepositoryFactory类:

using common.Interface;using System;using System.Collections.Generic;using System.Text;namespace common.Core
{public class RepositoryFactory : IRepositoryFactory
    {public IRepository CreateRepository(IconcardContext mydbcontext) where T : class
        {return new Repository(mydbcontext);
        }
    }
}

.net三层架构介绍 .net core 三层_Express_12

 第四步:创建Service层:

 老规矩,先添加新建项目Service和IService,一个是定义Service接口,另一个是它的实现,他们都需要引入Model层和Interface层,Service要引入IService层。

.net三层架构介绍 .net core 三层_.net三层架构介绍_13

添加BaseService类和IBaseService接口,接口中定义IRepository CreateService() where T : class, new();

IBaseService接口:

using common.Interface;using System;using System.Collections.Generic;using System.Text;namespace com.Synjones.IService
{public interface IBaseService
    {
        IRepository CreateService() where T : class, new();
    }
}

BaseService类:

using com.Synjones.IService;using common.Interface;using System;using System.Collections.Generic;using System.Text;namespace com.Synjones.Service
{public class BaseService : IBaseService
    {private IRepositoryFactory _repositoryFactory;private IconcardContext _mydbcontext;public BaseService(IRepositoryFactory repositoryFactory, IconcardContext mydbcontext)
        {this._repositoryFactory = repositoryFactory;this._mydbcontext = mydbcontext;
        }public IRepository CreateService() where T : class, new()
        {return _repositoryFactory.CreateRepository(_mydbcontext);
        }
    }
}

这里说明一下,BaseService类也是泛型类,也不需要通过DI方式创建,Service层中根据每个模块添加一个Service类,并且继承BaseService类,DI依赖注入模块Service中直接获取到指定模型的仓储进行操作。

添加User模块UserService类和IUserService接口,UserService类继承父类BaseService,生成构造函数。

.net三层架构介绍 .net core 三层_Express_14

public UserService(IRepositoryFactory repositoryFactory, IconcardContext mydbcontext) : base(repositoryFactory, mydbcontext)
{
}

下面我们简单举例对user表读取操作的业务层实现,定义接口List GetUsers(),实现GetUsers()方法,读取所有user表数据。

IUserService接口:

using com.Synjones.Model.Models;using System;using System.Collections.Generic;using System.Text;namespace com.Synjones.IService
{public interface IUserService
    {
        List GetUsers();
    }
}

UserService类:

using com.Synjones.IService;using com.Synjones.Model.Models;using common.Interface;using System;using System.Collections.Generic;using System.Text;using System.Linq;namespace com.Synjones.Service
{public class UserService : BaseService, IUserService
    {public UserService(IRepositoryFactory repositoryFactory, IconcardContext mydbcontext) : base(repositoryFactory, mydbcontext)
        {
        }public List GetUsers()
        {var service = this.CreateService();return service.GetAll().ToList();
        }
    }
}

 

.net三层架构介绍 .net core 三层_vs合并项目_15

第五步:UI创建并调用Service接口返回数据。

我这里创建了WebApi项目,依赖项中添加引用所有其他项目。

.net三层架构介绍 .net core 三层_.net三层架构介绍_16

配置数据库连接字符串,打开appsettings.json,添加

"ConnectionStrings": {"SchoolConnection": "server=.;database=ConCard;uid=sa;pwd=123123;"
  }

配置EF服务注册:

打开Startup.cs,ConfigureServices方法中添加services.AddDbContext指定数据库连接配置项,通过services.AddScoped添加DI依赖注入配置。

public void ConfigureServices(IServiceCollection services)
{
      services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);// 配置EF服务注册
      services.AddDbContext(options =>
      options.UseSqlServer(Configuration.GetConnectionString("SchoolConnection")));
      services.AddScoped();
      services.AddScoped();
      services.AddScoped();
}

修改下ValuesController控制器,添加构造函数和Get方法。

.net三层架构介绍 .net core 三层_.net三层架构介绍_17

WebApi项目设为启动项目,运行看结果。

 

.net三层架构介绍 .net core 三层_.net三层架构介绍_18