.NetCore WebApi实战2 仓储模式
仓储模式:一种设计模式,管理对象的持久化
数据访问层--抽象层===业务逻辑层
仓储接口==一组方法
仓储实现==EF Core
可以把数据库和数据防问层解耦,可以轻松切换底层数据库,不一定是EFCore,也可以是别的
1、创建文件夹Contracts(合约)
IRepositoryBase.cs仓库基础接口
public interface IRepositoryBase<T> { IQueryable<T> FindAll(); IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression); void Create(T entity); void Update(T entity); void Delete(T entity); }
2、再从Data文件夹下创建专属于EFCore的仓储文件夹Repositories
添加仓储基类RepositoryBase.cs,继承泛型接口IRepositoryBase<T>,拥有通用基础的增删改查,复杂的由各自服务类各自去实现
在构造函数里面创建自己的DbContext上下文
public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class { protected GameManagementDbContext GameDbContext { get; set; } protected RepositoryBase(GameManagementDbContext repositoryContext) { GameDbContext = repositoryContext; } public IQueryable<T> FindAll() { return GameDbContext.Set<T>().AsNoTracking(); } public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression) { return GameDbContext.Set<T>().Where(expression).AsNoTracking(); } public void Create(T entity) { GameDbContext.Set<T>().Add(entity); } public void Update(T entity) { GameDbContext.Set<T>().Update(entity); } public void Delete(T entity) { GameDbContext.Set<T>().Remove(entity); } }
3、创建各自的仓储服务类,不用写一句代码,就拥有基础的增删改查
ICharacterRepository接口 继承父泛型接口 IRepositoryBase,传入自己的类型
public interface ICharacterRepository : IRepositoryBase<Character> { }
实现服务类
CharacterRepository继承仓储基泛型类,传入自己的类型RepositoryBase<Character>,再实现自己的接口ICharacterRepository
public class CharacterRepository : RepositoryBase<Character>, ICharacterRepository { public CharacterRepository(GameManagementDbContext repositoryContext) : base(repositoryContext) { } }
二、仓储包装器
==》业务逻辑==》一个仓储里面要用到5-8个其它仓储,都实例化一遍?注入进来?不是很优雅
使用仓储包装器IRepositoryWrapper,一次性把所有的服务包括进来,注入只需要注入包装器就可以
可通过属性来暴露具体的接口
SaveChangesAsync来保存具体的修改
public interface IRepositoryWrapper { IPlayerRepository Player { get; } ICharacterRepository Character { get; } Task<int> SaveChangesAsync(); }
RepositoryWrapper.cs具体实现,不允许set,只允许get,如果为空则创建新的对象返回私有变量
调用上下文的SaveChangesAsync包装了一层,保存所有修改,
通过仓储包装器访问上下文,而非具体的服务类
因为上下文和EFCore绑定有点多,最好不要在业务方法里面使用上下文,再次解耦,放在仓储里面使用
如果再自己私有的方法里直接访问上下文,则就跟仓储无关了
用到哪个才会实例化哪个,并不是一上来全部实例化,并不会资源浪费,只有用到哪个才会get的时候实例化,按需实例化,第二次就不会再实例化了,用原有的数据了
public class RepositoryWrapper : IRepositoryWrapper { private readonly GameManagementDbContext _gameDbContext; private IPlayerRepository _player; private ICharacterRepository _character; public IPlayerRepository Player { get { return _player ??= new PlayerRepository(_gameDbContext); } } public ICharacterRepository Character { get { return _character ??= new CharacterRepository(_gameDbContext); } } public RepositoryWrapper(GameManagementDbContext gameDbContext) { _gameDbContext = gameDbContext; } public Task<int> SaveChangesAsync() { return _gameDbContext.SaveChangesAsync(); } }
注册仓储包装器服务
//直接注册仓储包装器就行了,不用再注册里面具体的实现类了 public static void ConfigureRepositoryWrapper(this IServiceCollection services) { //作用域,一个Http请求里面只会被实例化一次,请求完成就会销毁,多次请求就会多次实例化,确保每次请求只会有一个独立的仓储包装器 //包装器里面又确保了,只会有一个相同的上下文 services.AddScoped<IRepositoryWrapper, RepositoryWrapper>(); }
启动类注册
//注册仓储包装器服务 builder.Services.ConfigureRepositoryWrapper();
中间件最后往往才是路由,通过前面的处理认证、授权、跨域(也是属于授权的一种,对域名的授权),到了路由就可以直接进入控制器里面了
如果在进入控制器之前处理请求,就是过滤器的功能了,非中间件的功能了
//跨域策略应用
app.UseCors("AnyPolicy");
//授权
app.UseAuthorization();
//特性路由的中间件
app.MapControllers();
浙公网安备 33010602011771号