EFCore的数据仓储模式

EntityFramworkCore 配置种子数据中有降到如何配置种子数据,接下里就讲一讲数据仓储模式。

目录

1.动机

1.1 时下数据驱动应用是如何访问数据库

1.2Repository Design Pattern

2.实现

2.1具体仓储的实现

2.2泛型仓储


1.动机

1.1 时下数据驱动应用是如何访问数据库

传统的数据库应用结构如下:

从上图可以看出,对雇员的CURD操作是直接通过controller访问EFcore 的data context,EF将来自上层的CURD操作转化为SQL语句。那这样做有什么缺点呢:当然上述方法肯定是行之有效的,但是这样我们会把对数据的查询,修改,删除操作深度绑定到Controller这一层,这样的设计或者实现会造成代码冗余,且不利于后续维护,一旦数据访问逻辑发生了一点点小改动都需要对Controller进行修改。

1.2Repository Design Pattern

所以,这里就引入了数据仓储模式,这也是成为业界通用的做法,引入数据仓储模式后的架构如下:

简而言之就是我们在领域层和数据映射层之间加了一层,这一层类似于集合访问接口,专门用于领域层访问数据。换句话说,Repository Design Pattern在数据访问层和其它应用层之间扮演者一个中间人,应用获取数据的所有操作都交给这个中间人。好处是自然而然的将上层应用和低层数据访问隔离开来,当你改动一端时,不用修改另一端。而且测试Controller也会变得简单,你可以模拟一个内存数据库,而不用真的配置一个真实的数据库。

2.实现

2.1具体仓储的实现

为了方便依赖注入,我们首先定义一个仓库的接口:

简单起见我就定义下面几个方法(你可以根据具体的业务添加):

using RepositoryPatternStudy.Models;

namespace RepositoryPatternStudy.Repository
{
    public interface IEmployeeRepository
    {
        IEnumerable<Employee> GetAll();
        Employee GetById(int id);
        void Insert(Employee employee);
        void Update(Employee employee);
        void Delete(Employee employee);
        void Save();
    }
}

下面添加具体的实现:

public class EmployeeRepository : IEmployeeRepository,IDisposable
    {
        private readonly EmployeeDbContext _context;
        public EmployeeRepository(EmployeeDbContext context)
        {
            _context = context;
        }

        public void Delete(int employeeID)
        {
            Employee employee = _context.Employees.Find(employeeID);
            _context.Employees.Remove(employee);
        }

        public IEnumerable<Employee> GetAll()
        {
            return _context.Employees.ToList();
        }

        public Employee? GetById(int id)
            => _context.Employees.Find(id);

        public void Insert(Employee employee)
            =>_context.Employees.Add(employee);

        public void Save()
            =>_context.SaveChanges();

        public void Update(Employee employee)
            =>_context.Entry(employee).State=EntityState.Modified;

        public bool disposed=false;
        protected virtual void Dispose(bool disposing)
        {
            if(!disposed)
            {
                if(disposing)
                    _context.Dispose();
            }
            disposed=true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

    }

为了验证我们的正确性,我们可以新建一个EmployeeController:

 public class EmployeeController : Controller
    {
        private readonly IEmployeeRepository _employeeRepository;

        public EmployeeController(IEmployeeRepository employeeRepository)
            =>_employeeRepository = employeeRepository;


        [HttpGet]
        public List<Employee> Index()
        {
            var model=_employeeRepository.GetAll();
            return model.ToList();
        }
}

(在这里用了依赖注入,所以要记得在配置文件中添加服务:)

builder.Services.AddTransient<IEmployeeRepository,EmployeeRepository>();

运行程序打开Employee对于的页面:

 可以看到成功的拿到了数据库的数据。至此具体的仓储就实现完成了!

当然,你可以添加相应的视图,使得显示效果更更好:

@model IEnumerable<RepositoryUsingEFinMVC.DAL.Employee>
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
    @Html.ActionLink("Add Employee", "AddEmployee")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Gender)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Salary)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Dept)
        </th>
        <th></th>
    </tr>

    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Gender)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Salary)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Dept)
            </td>
            <td>
                @Html.ActionLink("Edit", "EditEmployee", new { EmployeeId = item.EmployeeID }) |
                @Html.ActionLink("Delete", "DeleteEmployee", new { EmployeeId = item.EmployeeID })
            </td>
        </tr>
    }
</table>

显示效果如下:

2.2泛型仓储

因为我们只有一个实体类型:Employee,但实际上的项目实体肯定不止一个,可能还有订单order,顾客Custome等,如果没有泛型仓储,我们可能需要为每个实体构建一个仓库,这是很麻烦的。

有了前面的基础,我们构建泛型仓库就简单很多,首先定义一个泛型仓库接口:

    public interface IGenericRepository<T> where T:class
    {
        IEnumerable<T> GetAll();
        T GetById(object id);
        void Insert(T obj);
        void Update(T obj);
        void Delete(object id);
        void Save();
    }

然后实现它:

public class GenericRepository<T> : IGenericRepository<T> where T : class
    {
        private readonly EmployeeDbContext _context;
        private readonly DbSet<T> table;

        public GenericRepository(EmployeeDbContext context)
        {
            _context = context;
            table = context.Set<T>();
        }
        
        public void Delete(object id)
        {
            T existing=table.Find(id);
            table.Remove(existing);
        }

        public IEnumerable<T> GetAll()
            =>table.ToList();

        public T GetById(object id)
            => table.Find(id);

        public void Insert(T obj)
            =>table.Add(obj);

        public void Save()
            =>_context.SaveChanges();

        public void Update(T obj)
        {
            table.Attach(obj);
            _context.Entry(obj).State = EntityState.Modified;
        }
    }

将泛型仓储注册到服务:

builder.Services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));

然后在Controller中将泛型仓储替换原来的仓储:

 private readonly IEmployeeRepository _employeeRepository;
        private readonly IGenericRepository<Employee> _repository;

        public EmployeeController(IEmployeeRepository employeeRepository, IGenericRepository<Employee> genericRepository)
        {
            _employeeRepository = employeeRepository;
            _repository = genericRepository;
        }


        [HttpGet]
        public ActionResult Index()
        {
            //var model=_employeeRepository.GetAll();
            var model = _repository.GetAll();
            return View(model);

可以得到相同的运行结果(我这里同时将两种类型的仓储都注册进去了)。

2.3 同时使用两种仓库

泛型仓储一般是比较通用的方法,也就是常见的CURD,一旦想定制化仓库,比扩展一些功能,则需要自行实现,但是如果已经有了泛型仓储,那么通过继承,我们可以只实现不同的部分,这样也带来了相当大的遍历。

操作如下:

    public interface IEmployeeRepositoryEx:IGenericRepository<Employee>
    {
        IEnumerable<Employee> GetEmployeesByGender(string gender);
        IEnumerable<Employee> GetEmployeesByDepartment(string Dept);
    }

上面是一个扩充了两个方法的新接口,其实现如下:

 public class EmployeeeRepositoryEx : GenericRepository<Employee>, IEmployeeRepositoryEx
    {
       // private readonly IGenericRepository<Employee> _repository;
      //  private readonly IEmployeeRepositoryEx _employeeRepositoryEx;
        private readonly EmployeeDbContext _context;
        public EmployeeeRepositoryEx(EmployeeDbContext context):base(context)
          //  IGenericRepository<Employee> genericRepository,IEmployeeRepositoryEx employeeRepositoryEx):base(context)
        {
          //  _repository = genericRepository;
          //  _employeeRepositoryEx = employeeRepositoryEx;
            _context = context;
        }
        public IEnumerable<Employee> GetEmployeesByDepartment(string Dept)
        {
            return _context.Employees.Where(x=>x.Dept == Dept);
        }

        public IEnumerable<Employee> GetEmployeesByGender(string gender)
        {
            return _context.Employees.Where(x=>x.Gender == gender);
        }
    }

将IEmployeeRepository注册,并替换后,可以同时使用泛型接口和新接口的方法,从而达到了定制化扩展,同样你可以放在EmployeeController中尝试。


到此为止,仓储模型就基本讲完了,

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值