.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();

 

posted on 2025-03-09 22:51  张彦山  阅读(129)  评论(0)    收藏  举报