You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

泛型仓储模式中不同层级模型的映射方案咨询及代码示例请求

泛型仓储模式下跨层模型映射的优化方案

针对你遇到的泛型仓储三层模型(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

火山引擎 最新活动