You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Dapper Extension实现双表通用类映射及仓储/控制器构建

基于Dapper Extensions实现双表映射、仓储及控制器方案

1. 实体类调整

由于MessageModel合并了两张表的字段,首先需要为每张表创建独立实体类,再通过联合查询组装成目标模型:

表对应实体类

// GuestMessageDetails表实体
public class GuestMessageDetail
{
    public long Id { get; set; }
    public string? CompanyName { get; set; }
    public string? FirstName { get; set; }
    public string? FamilyName { get; set; }
    public string? EmailAddress { get; set; } // 修正原拼写错误EmialAdress
    public long Telephone { get; set; }
    public string? Country { get; set; }
    public string? ArticleNumber { get; set; }
    public string? SoftwareVersion { get; set; }
    public string? SerialNumber { get; set; }
    public string? PurchaseDate { get; set; }
    public string? OrderNumber { get; set; }
}

// ProductTable表实体
public class Product
{
    public long Id { get; set; }
    public string? ArticleImage { get; set; }
    public string? ArticleNumber { get; set; }
}

2. Dapper Extensions映射配置

Dapper Extensions需要为每个实体配置表名、主键及字段映射(处理属性与列名不一致的情况):

映射类实现

// GuestMessageDetail映射
public class GuestMessageDetailMapper : ClassMapper<GuestMessageDetail>
{
    public GuestMessageDetailMapper()
    {
        Table("GuestMessageDetails");
        Map(x => x.Id).Key(KeyType.Identity); // 主键自增配置
        Map(x => x.EmailAddress).Column("EmialAdress"); // 适配数据库列名拼写
        AutoMap(); // 自动映射其余匹配属性
    }
}

// Product映射
public class ProductMapper : ClassMapper<Product>
{
    public ProductMapper()
    {
        Table("ProductTable");
        Map(x => x.Id).Key(KeyType.Identity);
        AutoMap();
    }
}

注册映射

在应用启动时(如Program.cs)注册映射程序集:

DapperExtensions.DapperExtensions.SetMappingAssemblies(new[] { typeof(GuestMessageDetailMapper).Assembly });

3. 通用仓储扩展

基于已有通用仓储基类,扩展实现双表查询及数据操作:

仓储接口定义

public interface IGuestMessageRepository : IGenericRepository<GuestMessageDetail>
{
    Task<IEnumerable<MessageModel>> GetAllWithProductInfoAsync();
    Task<MessageModel?> GetByIdWithProductInfoAsync(long id);
    Task<int> InsertMessageWithProductAsync(MessageModel model);
}

仓储实现

public class GuestMessageRepository : GenericRepository<GuestMessageDetail>, IGuestMessageRepository
{
    public GuestMessageRepository(IDbConnection connection) : base(connection)
    {
    }

    // 查询所有消息及关联产品信息
    public async Task<IEnumerable<MessageModel>> GetAllWithProductInfoAsync()
    {
        var sql = @"
            SELECT 
                gmd.*, pt.ArticleImage
            FROM GuestMessageDetails gmd
            LEFT JOIN ProductTable pt ON gmd.ArticleNumber = pt.ArticleNumber";
        
        return await Connection.QueryAsync<MessageModel>(sql);
    }

    // 根据ID查询单条消息及关联产品信息
    public async Task<MessageModel?> GetByIdWithProductInfoAsync(long id)
    {
        var sql = @"
            SELECT 
                gmd.*, pt.ArticleImage
            FROM GuestMessageDetails gmd
            LEFT JOIN ProductTable pt ON gmd.ArticleNumber = pt.ArticleNumber
            WHERE gmd.Id = @Id";
        
        return await Connection.QueryFirstOrDefaultAsync<MessageModel>(sql, new { Id = id });
    }

    // 新增消息及关联产品(事务处理)
    public async Task<int> InsertMessageWithProductAsync(MessageModel model)
    {
        using var transaction = Connection.BeginTransaction();
        try
        {
            // 插入消息详情
            var messageId = await InsertAsync(new GuestMessageDetail
            {
                CompanyName = model.companyname,
                FirstName = model.FirstName,
                FamilyName = model.FamilyName,
                EmailAddress = model.EmialAdress,
                Telephone = model.Telephone,
                Country = model.Country,
                ArticleNumber = model.ArticleNumber,
                SoftwareVersion = model.SoftwareVersion,
                SerialNumber = model.SerialNumber,
                PurchaseDate = model.PurchaseDate,
                OrderNumber = model.OrderNumber
            }, transaction);

            // 插入产品(仅当产品不存在时)
            var existingProduct = await Connection.QueryFirstOrDefaultAsync<Product>(
                "SELECT * FROM ProductTable WHERE ArticleNumber = @ArticleNumber", 
                new { model.ArticleNumber }, transaction);
            
            if (existingProduct == null && !string.IsNullOrEmpty(model.ArticleImage))
            {
                await Connection.ExecuteAsync(
                    "INSERT INTO ProductTable (ArticleImage, ArticleNumber) VALUES (@ArticleImage, @ArticleNumber)",
                    new { model.ArticleImage, model.ArticleNumber }, transaction);
            }

            transaction.Commit();
            return messageId;
        }
        catch
        {
            transaction.Rollback();
            throw;
        }
    }
}

4. 控制器实现

创建API控制器暴露业务接口:

[ApiController]
[Route("api/guest-messages")]
public class GuestMessageController : ControllerBase
{
    private readonly IGuestMessageRepository _repository;

    public GuestMessageController(IGuestMessageRepository repository)
    {
        _repository = repository;
    }

    [HttpGet]
    public async Task<IActionResult> GetAll()
    {
        var messages = await _repository.GetAllWithProductInfoAsync();
        return Ok(messages);
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetById(long id)
    {
        var message = await _repository.GetByIdWithProductInfoAsync(id);
        if (message == null)
        {
            return NotFound();
        }
        return Ok(message);
    }

    [HttpPost]
    public async Task<IActionResult> Create([FromBody] MessageModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var messageId = await _repository.InsertMessageWithProductAsync(model);
        return CreatedAtAction(nameof(GetById), new { id = messageId }, model);
    }
}

5. 依赖注入注册

在Program.cs中注册仓储及数据库连接:

builder.Services.AddScoped<IDbConnection>(sp => 
    new SqlConnection(builder.Configuration.GetConnectionString("YourConnectionString")));
builder.Services.AddScoped<IGuestMessageRepository, GuestMessageRepository>();

内容的提问来源于stack exchange,提问作者Kudiyarasu M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 23:30:53