泛型仓储模式中不同层级模型的映射方案咨询及代码示例请求
泛型仓储模式下跨层模型映射的优化方案
针对你遇到的泛型仓储三层模型(APIModel/DALModel/EFModel)映射问题,场景2的思路其实更符合关注点分离的设计原则——各层只持有自身需要的模型,避免不必要的跨层依赖。下面我会给出一套落地的实现方案,解决映射代码放置的问题,同时保持各层的隔离性。
核心设计原则
- 各层仅依赖直接相邻层的模型,不暴露跨层模型细节
- 映射逻辑放在相邻层的边界处,由依赖下层的那一层负责转换
- 用泛型基类封装通用CRUD和映射逻辑,具体业务类只实现专属逻辑
具体实现示例
1. 先定义各层的模型基类(可选,但能提升泛型通用性)
// EF层模型基类:封装EF实体通用字段 public abstract class BaseEFModel { public int Id { get; set; } public DateTime CreateTime { get; set; } = DateTime.Now; } // DAL层模型基类:封装数据访问层通用字段 public abstract class BaseDALModel { public int Id { get; set; } } // API层模型基类:封装接口层通用字段 public abstract class BaseAPIModel { public int Id { get; set; } }
2. 分层泛型基类改造与映射实现
我们用接口隔离来隐藏下层模型细节,确保上层看不到不必要的模型定义:
DAL层:负责DALModel ↔ EFModel的映射
先定义DAL层的通用接口,隐藏EFModel细节:
public interface IBaseRepository<TDALModel> where TDALModel : BaseDALModel { Task<TDALModel> GetByIdAsync(int id); Task<TDALModel> AddAsync(TDALModel model); // 可扩展其他通用CRUD方法 }
然后实现泛型仓储基类,处理DALModel和EFModel的转换(示例用AutoMapper简化,也可手动实现映射):
public class BaseRepository<TDALModel, TEFModel> : IBaseRepository<TDALModel> where TDALModel : BaseDALModel where TEFModel : BaseEFModel, new() { private readonly DbContext _dbContext; private readonly IMapper _mapper; public BaseRepository(DbContext dbContext, IMapper mapper) { _dbContext = dbContext; _mapper = mapper; } public virtual async Task<TDALModel> GetByIdAsync(int id) { var efEntity = await _dbContext.Set<TEFModel>().FindAsync(id); return efEntity == null ? null : _mapper.Map<TDALModel>(efEntity); } public virtual async Task<TDALModel> AddAsync(TDALModel dalModel) { var efEntity = _mapper.Map<TEFModel>(dalModel); _dbContext.Set<TEFModel>().Add(efEntity); await _dbContext.SaveChangesAsync(); return _mapper.Map<TDALModel>(efEntity); } }
服务层:负责APIModel ↔ DALModel的映射
同样先定义服务层通用接口,隐藏DALModel细节:
public interface IBaseService<TAPIModel> where TAPIModel : BaseAPIModel { Task<TAPIModel> GetByIdAsync(int id); Task<TAPIModel> AddAsync(TAPIModel model); // 可扩展其他通用业务方法 }
实现泛型服务基类,处理APIModel和DALModel的转换:
public class BaseService<TAPIModel, TDALModel> : IBaseService<TAPIModel> where TAPIModel : BaseAPIModel where TDALModel : BaseDALModel { protected readonly IBaseRepository<TDALModel> _repository; protected readonly IMapper _mapper; public BaseService(IBaseRepository<TDALModel> repository, IMapper mapper) { _repository = repository; _mapper = mapper; } public virtual async Task<TAPIModel> GetByIdAsync(int id) { var dalModel = await _repository.GetByIdAsync(id); return dalModel == null ? null : _mapper.Map<TAPIModel>(dalModel); } public virtual async Task<TAPIModel> AddAsync(TAPIModel apiModel) { var dalModel = _mapper.Map<TDALModel>(apiModel); var createdDalModel = await _repository.AddAsync(dalModel); return _mapper.Map<TAPIModel>(createdDalModel); } }
API层:仅依赖APIModel和服务接口
实现泛型控制器基类,完全不需要知道DAL/EF层的模型:
public class BaseController<TAPIModel> : ApiController where TAPIModel : BaseAPIModel { protected readonly IBaseService<TAPIModel> _service; public BaseController(IBaseService<TAPIModel> service) { _service = service; } [HttpGet("{id}")] public virtual async Task<IHttpActionResult> Get(int id) { var apiModel = await _service.GetByIdAsync(id); return apiModel == null ? NotFound() : Ok(apiModel); } [HttpPost] public virtual async Task<IHttpActionResult> Post(TAPIModel apiModel) { if (!ModelState.IsValid) return BadRequest(ModelState); var createdModel = await _service.AddAsync(apiModel); return CreatedAtRoute("DefaultApi", new { id = createdModel.Id }, createdModel); } }
3. 具体业务模块示例(以用户模块为例)
// EF层实体 public class UserEFModel : BaseEFModel { public string Username { get; set; } public string Email { get; set; } } // DAL层模型 public class UserDALModel : BaseDALModel { public string Username { get; set; } public string Email { get; set; } } // API层模型 public class UserAPIModel : BaseAPIModel { public string Username { get; set; } public string Email { get; set; } } // 具体仓储实现 public class UserRepository : BaseRepository<UserDALModel, UserEFModel> { public UserRepository(DbContext dbContext, IMapper mapper) : base(dbContext, mapper) { } // 可添加用户专属的DAL方法 } // 具体服务实现 public class UserService : BaseService<UserAPIModel, UserDALModel>, IBaseService<UserAPIModel> { public UserService(UserRepository repository, IMapper mapper) : base(repository, mapper) { } // 可添加用户专属的业务逻辑 } // 具体控制器实现 public class UsersController : BaseController<UserAPIModel> { public UsersController(UserService service) : base(service) { } // 可添加用户专属的API接口 }
4. 映射配置(AutoMapper示例)
在WebAPI启动类中配置模型映射关系:
var mapperConfig = new MapperConfiguration(cfg => { // API ↔ DAL 映射 cfg.CreateMap<UserAPIModel, UserDALModel>().ReverseMap(); // DAL ↔ EF 映射 cfg.CreateMap<UserDALModel, UserEFModel>().ReverseMap(); // 其他业务模块的映射配置 }); // 注册AutoMapper到依赖注入容器 mapperConfig.AssertConfigurationIsValid(); container.RegisterInstance<IMapper>(mapperConfig.CreateMapper());
方案优势
- 完全隔离各层模型:API层只知道APIModel,服务层只知道APIModel和DALModel,DAL层只知道DALModel和EFModel,无跨层模型暴露
- 泛型复用性高:基类封装了通用CRUD和映射逻辑,具体业务类只需实现专属逻辑
- 映射职责清晰:每一层只处理与相邻层的模型转换,避免映射逻辑分散或跨层依赖
如果不想用AutoMapper,也可以在基类中定义虚方法(比如protected virtual TDALModel MapToDAL(TAPIModel model)),由具体子类重写实现手动映射。
内容的提问来源于stack exchange,提问作者Dheeraj Kumar




