.NET 仓储模式入门指南
一、仓储模式是什么?
仓储模式就像是一个"数据管理员":
-
它负责帮你管理所有与数据存取相关的工作
-
你的业务代码不需要知道数据是存在数据库、文件还是网络服务中
-
你只需要告诉仓储"我要什么数据",它就会帮你取来
二、为什么要用仓储模式?
想象你在餐厅点餐:
-
传统方式:你直接去厨房告诉厨师怎么做菜(业务逻辑直接操作数据库)
-
仓储模式:你只需要告诉服务员(仓储)你想吃什么,服务员去和厨房沟通
好处:
-
你不需要关心厨房怎么运作(数据如何存取)
-
更换厨师(更换数据库)不会影响你点餐
-
更容易测试(可以用假服务员模拟)
三、实现一个简单的仓储模式
需要安装nuget包
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
在appsettings.json中添加链接数据库链接
"ConnectionStrings": { "DefaultConnection": "server=.;database=BingDbContext;uid=sa;pwd=123456;TrustServerCertificate=True;" }
1. 项目结构准备
ProductManagement/
├── Controllers/ # WebAPI控制器
├── Models/ # 数据模型
├── Repositories/ # 仓储接口和实现
├── Services/ # 业务逻辑服务(可选)
└── Data/ # 数据库上下文(Entity Framework Core)
2. 创建模型(Model)
// Models/Product.cs public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } public string Description { get; set; } }
3. 设置数据库上下文
// Data/ApplicationDbContext.cs using Microsoft.EntityFrameworkCore; using ProductManagement.Models; public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Product> Products { get; set; } }
4. 创建仓储接口
// Repositories/IRepository.cs using System.Collections.Generic; using System.Threading.Tasks; public interface IRepository<T> where T : class { Task<IEnumerable<T>> GetAllAsync(); Task<T> GetByIdAsync(int id); Task AddAsync(T entity); Task UpdateAsync(T entity); Task DeleteAsync(int id); Task<bool> ExistsAsync(int id); }
5. 实现通用仓储
// Repositories/Repository.cs using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ProductManagement.Data; public class Repository<T> : IRepository<T> where T : class { protected readonly ApplicationDbContext _context; protected readonly DbSet<T> _dbSet; public Repository(ApplicationDbContext context) { _context = context; _dbSet = context.Set<T>(); } public async Task<IEnumerable<T>> GetAllAsync() { return await _dbSet.ToListAsync(); } public async Task<T> GetByIdAsync(int id) { return await _dbSet.FindAsync(id); } public async Task AddAsync(T entity) { await _dbSet.AddAsync(entity); await _context.SaveChangesAsync(); } public async Task UpdateAsync(T entity) { _context.Entry(entity).State = EntityState.Modified; await _context.SaveChangesAsync(); } public async Task DeleteAsync(int id) { var entity = await GetByIdAsync(id); if (entity != null) { _dbSet.Remove(entity); await _context.SaveChangesAsync(); } } public async Task<bool> ExistsAsync(int id) { var entity = await GetByIdAsync(id); return entity != null; } }
6. 创建产品特定仓储接口(可选)
// Repositories/IProductRepository.cs using System.Collections.Generic; using System.Threading.Tasks; using ProductManagement.Models; public interface IProductRepository : IRepository<Product> { Task<IEnumerable<Product>> GetProductsByPriceRangeAsync(decimal min, decimal max); }
7. 实现产品特定仓储(可选)
// Repositories/ProductRepository.cs using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ProductManagement.Data; using ProductManagement.Models; public class ProductRepository : Repository<Product>, IProductRepository { public ProductRepository(ApplicationDbContext context) : base(context) { } public async Task<IEnumerable<Product>> GetProductsByPriceRangeAsync(decimal min, decimal max) { return await _context.Products .Where(p => p.Price >= min && p.Price <= max) .ToListAsync(); } }
8. 配置依赖注入
// 在Program.cs中(对于.NET 6+) builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // 注册通用仓储 builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); // 注册特定产品仓储(如果使用了特定仓储) builder.Services.AddScoped<IProductRepository, ProductRepository>();
9. 创建控制器
// Controllers/ProductsController.cs using Microsoft.AspNetCore.Mvc; using ProductManagement.Models; using ProductManagement.Repositories; using System.Collections.Generic; using System.Threading.Tasks; [Route("api/[controller]")] [ApiController] public class ProductsController : ControllerBase { private readonly IRepository<Product> _productRepository; public ProductsController(IRepository<Product> productRepository) { _productRepository = productRepository; } // GET: api/Products [HttpGet] public async Task<ActionResult<IEnumerable<Product>>> GetProducts() { return Ok(await _productRepository.GetAllAsync()); } // GET: api/Products/5 [HttpGet("{id}")] public async Task<ActionResult<Product>> GetProduct(int id) { var product = await _productRepository.GetByIdAsync(id); if (product == null) { return NotFound(); } return product; } // PUT: api/Products/5 [HttpPut("{id}")] public async Task<IActionResult> PutProduct(int id, Product product) { if (id != product.Id) { return BadRequest(); } if (!await _productRepository.ExistsAsync(id)) { return NotFound(); } await _productRepository.UpdateAsync(product); return NoContent(); } // POST: api/Products [HttpPost] public async Task<ActionResult<Product>> PostProduct(Product product) { await _productRepository.AddAsync(product); return CreatedAtAction("GetProduct", new { id = product.Id }, product); } // DELETE: api/Products/5 [HttpDelete("{id}")] public async Task<IActionResult> DeleteProduct(int id) { if (!await _productRepository.ExistsAsync(id)) { return NotFound(); } await _productRepository.DeleteAsync(id); return NoContent(); } }
10. 数据库迁移和应用启动
创建迁移:
dotnet ef migrations add InitialCreate
nuget控制台:
add-migration 添加描述
应用迁移:
dotnet ef database update
nuget控制台迁移
update-database