ASP.NET Core中Dapper自定义映射配置的正确初始化方式
问题描述
我在付费在线课程的作业中开发了一个三层架构的小程序,近期将表现层从控制台替换为ASP.NET Core。数据层中我为Dapper编写了自定义映射类:
namespace CarRental.Data.Managers { public class DapperConfigurationManager { // METHODS public void ConfigureGuidToStringMapping() { SqlMapper.AddTypeHandler(new GuidToStringTypeHandler()); SqlMapper.RemoveTypeMap(typeof(Guid)); } public void SetCustomMappingForEntities() { CustomPropertyTypeMap carMap = new CustomPropertyTypeMap(typeof(Car), PropertyInformation); CustomPropertyTypeMap customerMap = new CustomPropertyTypeMap(typeof(CustomerTemp), PropertyInformation); CustomPropertyTypeMap dealMap = new CustomPropertyTypeMap(typeof(Deal), PropertyInformation); CustomPropertyTypeMap inspectionMap = new CustomPropertyTypeMap(typeof(Inspection), PropertyInformation); CustomPropertyTypeMap repairMap = new CustomPropertyTypeMap(typeof(Repair), PropertyInformation); CustomPropertyTypeMap mechanicMap = new CustomPropertyTypeMap(typeof(Mechanic), PropertyInformation); SqlMapper.SetTypeMap(typeof(Car), carMap); SqlMapper.SetTypeMap(typeof(CustomerTemp), customerMap); SqlMapper.SetTypeMap(typeof(Deal), dealMap); SqlMapper.SetTypeMap(typeof(Inspection), inspectionMap); SqlMapper.SetTypeMap(typeof(Repair), repairMap); SqlMapper.SetTypeMap(typeof(Mechanic), mechanicMap); } public static PropertyInfo PropertyInformation(Type type, string attribName) { // BECAUSE THE METHOD IS STRONGLY TYPED, I CANNOT MOVE IT IN FROM THE OUTSIDE. Dictionary<string, string> columnProperties = new Dictionary<string, string> { { "carCarId", "CarId" }, { "carVinCode", "VinCode" }, { "carNumberPlate", "NumberPlate" }, { "carBrand", "Brand" }, { "carModel", "Model" }, { "carPrice", "Price" }, { "carNumberOfSeats", "NumberOfSeats" }, { "carNumberOfDoors", "NumberOfDoors" }, { "carMileage", "Mileage" }, { "carMaxFuelCapacity", "MaxFuelCapacity" }, { "carCurrentFuel", "CurrentFuel" }, { "carYear", "Year" }, { "carIsFitForUse", "IsFitForUse" }, { "carEngine", "Engine" }, { "carTransmission", "Transmission" }, { "carInterior", "Interior" }, { "carWheels", "Wheels" }, { "carLights", "Lights" }, { "carSignal", "Signal" }, { "carColor", "Color" }, { "carStatusId", "Status" }, { "userIdNumber", "IdNumber" }, { "userFirstName", "FirstName" }, { "userLastName", "LastName" }, { "userDateOfBirth", "DateOfBirth" }, { "userUserName", "UserName" }, { "userPassword", "Password" }, { "userPassportNumber", "PassportNumber" }, { "userDrivingLicenseNumber", "DrivingLicenseNumber" }, { "userBasicDiscount", "BasicDiscount" }, { "userCategory", "Category" }, { "dealId", "Id" }, { "dealCarId", "CarId" }, { "dealVinCode", "VinCode" }, { "dealCustomerId", "CustomerId" }, { "dealPrice", "Price" }, { "dealDealType", "DealType" }, { "dealName", "Name" }, { "inspectionInspectionId", "InspectionId" }, { "inspectionCarId", "CarId" }, { "inspectionInspectorId", "InspectorId" }, { "inspectionInspectionDate", "InspectionDate" }, { "inspectionStatusId", "Result" }, { "repairId", "Id" }, { "repairDate", "Date" }, { "repairCarId", "CarId" }, { "repairMechanicId", "MechanicId" }, { "repairIsSuccessfull", "IsSuccessfull" }, { "repairTotalCost", "TotalCost" }, { "repairTechnicalInfo", "TechnicalInfo" }, { "mechanId", "Id" }, { "mechanName", "Name" }, { "mechanSurename", "Surename" }, { "mechanYear", "Year" } }; if (columnProperties.ContainsKey(attribName)) { return type.GetProperty(columnProperties[attribName]); } return type.GetProperty(attribName); } } }
我尝试在Program.cs中按如下方式实现初始化:
namespace CarRental.WebApi { public class Program { public static void Main(string[] args) { // TO CONFIGURE ORM FOR Car-CLASS. var carServiceManager = new ServiceManager(); carServiceManager.InitializeManagment(); carServiceManager.SupplementData.DapperConfigs.ConfigureGuidToStringMapping(); carServiceManager.SupplementData.DapperConfigs.SetCustomMappingForEntities(); var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddTransient<ServiceManager>(x => new ServiceManager()); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); } } }
但老师指出该实现存在问题(手动创建的ServiceManager实例可能长期存活等),却未告知正确方案。我仅具备C#基础,不清楚在ASP.NET Core中如何正确初始化该Dapper自定义映射,作业即将截止,且没有该映射无法从远程数据库获取数据。另外,默认模板中大量使用var导致我难以理解代码类型。
请问在ASP.NET Core中正确实现Dapper自定义映射配置初始化的方式是什么?
解决方案
核心思路
Dapper的自定义映射(类型处理器、类型映射)是全局生效的,只需要在应用启动时执行一次即可,不需要依赖ServiceManager的实例,也不需要放到DI容器里重复创建。
步骤1:简化初始化逻辑,脱离ServiceManager
把Dapper的配置逻辑独立出来,不需要依赖ServiceManager。可以直接在DapperConfigurationManager里添加一个静态方法,一键完成所有配置:
namespace CarRental.Data.Managers { public class DapperConfigurationManager { // 新增静态方法,一键完成所有Dapper配置 public static void InitializeAllDapperConfigurations() { ConfigureGuidToStringMappingStatic(); SetCustomMappingForEntitiesStatic(); } private static void ConfigureGuidToStringMappingStatic() { SqlMapper.AddTypeHandler(new GuidToStringTypeHandler()); SqlMapper.RemoveTypeMap(typeof(Guid)); } private static void SetCustomMappingForEntitiesStatic() { CustomPropertyTypeMap carMap = new CustomPropertyTypeMap(typeof(Car), PropertyInformation); CustomPropertyTypeMap customerMap = new CustomPropertyTypeMap(typeof(CustomerTemp), PropertyInformation); CustomPropertyTypeMap dealMap = new CustomPropertyTypeMap(typeof(Deal), PropertyInformation); CustomPropertyTypeMap inspectionMap = new CustomPropertyTypeMap(typeof(Inspection), PropertyInformation); CustomPropertyTypeMap repairMap = new CustomPropertyTypeMap(typeof(Repair), PropertyInformation); CustomPropertyTypeMap mechanicMap = new CustomPropertyTypeMap(typeof(Mechanic), PropertyInformation); SqlMapper.SetTypeMap(typeof(Car), carMap); SqlMapper.SetTypeMap(typeof(CustomerTemp), customerMap); SqlMapper.SetTypeMap(typeof(Deal), dealMap); SqlMapper.SetTypeMap(typeof(Inspection), inspectionMap); SqlMapper.SetTypeMap(typeof(Repair), repairMap); SqlMapper.SetTypeMap(typeof(Mechanic), mechanicMap); } public static PropertyInfo PropertyInformation(Type type, string attribName) { // 原有的字典映射内容保持不变 Dictionary<string, string> columnProperties = new Dictionary<string, string> { { "carCarId", "CarId" }, { "carVinCode", "VinCode" }, { "carNumberPlate", "NumberPlate" }, { "carBrand", "Brand" }, { "carModel", "Model" }, { "carPrice", "Price" }, { "carNumberOfSeats", "NumberOfSeats" }, { "carNumberOfDoors", "NumberOfDoors" }, { "carMileage", "Mileage" }, { "carMaxFuelCapacity", "MaxFuelCapacity" }, { "carCurrentFuel", "CurrentFuel" }, { "carYear", "Year" }, { "carIsFitForUse", "IsFitForUse" }, { "carEngine", "Engine" }, { "carTransmission", "Transmission" }, { "carInterior", "Interior" }, { "carWheels", "Wheels" }, { "carLights", "Lights" }, { "carSignal", "Signal" }, { "carColor", "Color" }, { "carStatusId", "Status" }, { "userIdNumber", "IdNumber" }, { "userFirstName", "FirstName" }, { "userLastName", "LastName" }, { "userDateOfBirth", "DateOfBirth" }, { "userUserName", "UserName" }, { "userPassword", "Password" }, { "userPassportNumber", "PassportNumber" }, { "userDrivingLicenseNumber", "DrivingLicenseNumber" }, { "userBasicDiscount", "BasicDiscount" }, { "userCategory", "Category" }, { "dealId", "Id" }, { "dealCarId", "CarId" }, { "dealVinCode", "VinCode" }, { "dealCustomerId", "CustomerId" }, { "dealPrice", "Price" }, { "dealDealType", "DealType" }, { "dealName", "Name" }, { "inspectionInspectionId", "InspectionId" }, { "inspectionCarId", "CarId" }, { "inspectionInspectorId", "InspectorId" }, { "inspectionInspectionDate", "InspectionDate" }, { "inspectionStatusId", "Result" }, { "repairId", "Id" }, { "repairDate", "Date" }, { "repairCarId", "CarId" }, { "repairMechanicId", "MechanicId" }, { "repairIsSuccessfull", "IsSuccessfull" }, { "repairTotalCost", "TotalCost" }, { "repairTechnicalInfo", "TechnicalInfo" }, { "mechanId", "Id" }, { "mechanName", "Name" }, { "mechanSurename", "Surename" }, { "mechanYear", "Year" } }; if (columnProperties.ContainsKey(attribName)) { return type.GetProperty(columnProperties[attribName]); } return type.GetProperty(attribName); } } }
步骤2:在Program.cs中正确初始化
在应用启动的最开始(构建WebApplication之前)执行Dapper的全局配置,不需要手动创建ServiceManager,同时把var替换为具体类型方便理解:
namespace CarRental.WebApi { public class Program { public static void Main(string[] args) { // 第一步:初始化Dapper全局映射,仅执行一次 CarRental.Data.Managers.DapperConfigurationManager.InitializeAllDapperConfigurations(); // 第二步:构建Web应用(替换var为具体类型) WebApplicationBuilder builder = WebApplication.CreateBuilder(args); // 添加服务到DI容器 builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // 正确注册ServiceManager到DI容器,框架会自动管理其生命周期 builder.Services.AddTransient<ServiceManager>(); WebApplication app = builder.Build(); // 配置HTTP请求管道 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); } } }
为什么原来的实现有问题?
- 手动创建
ServiceManager实例后,又在DI容器中注册了新的实例,导致存在两个独立的对象,浪费资源且可能引发状态不一致。 - Dapper的映射是全局配置,不需要依赖
ServiceManager的生命周期,手动创建实例完全没必要,还可能因为实例长期存活导致内存占用问题。
内容的提问来源于stack exchange,提问作者Yaroslav Parkhomenko
相关产品推荐
相关产品推荐

