Dapper映射带私有setter的DDD域对象时报构造函数匹配错误如何解决
问题原因
- Dapper实例化对象时会优先匹配参数数量最多的构造函数,要求构造函数参数名与查询返回的列名(不区分大小写)一一对应,你当前的
Bundle类构造函数不符合匹配规则:- 现有构造函数接收
Company类型的参数,但查询返回的是CompanyID、CompanyName两个独立列,Dapper默认不会自动将多个列拼接为复杂对象传入构造函数 - 你的查询未返回
Status字段,无法满足现有构造函数的BundleStatus status入参要求 - 构造函数参数名与查询列名不匹配,比如构造函数第一个参数为
warehouse,但错误提示中查询返回的对应列名为DestinationWarehouseID
- 现有构造函数接收
- Dapper.FluentMap的列映射规则仅作用于属性赋值阶段,构造函数匹配阶段不会读取该配置,因此你配置的嵌套属性映射不会解决构造函数匹配失败的问题
解决方案
推荐优先使用第一种方案,对DDD域对象的封装性破坏最小:
- 方案1:为
Bundle类添加私有无参构造函数
直接在Bundle类中添加代码:
Dapper支持调用私有构造函数实例化对象,实例化完成后会自动按照你配置的FluentMap规则,给所有私有setter属性(包括嵌套的private Bundle() { // 给ORM反射实例化用,不对外暴露,不破坏DDD封装规则 this.Packages = new HashSet<BundlePackages>(); }Company属性)赋值,无需其他额外修改。 - 方案2:新增适配查询列的构造函数
增加一个与查询返回列匹配的构造函数,内部自行构造Company对象:
注意构造函数参数名必须与查询返回的列名完全一致(不区分大小写)。internal Bundle(int id, DateTime createdOn, int createdBy, string destinationWarehouseID, int companyId, string companyName, BundleStatus status = default) { this.Id = id; this.CreatedOn = createdOn; this.CreatedBy = createdBy; this.WarehouseID = destinationWarehouseID; this.Company = new Company(companyId, companyName); this.Status = status; this.Packages = new HashSet<BundlePackages>(); } - 方案3:使用Dapper多映射手动构建对象
放弃自动映射,使用QueryAsync的多映射重载,拆分查询返回的字段手动构建Bundle和Company对象,该方案无需修改域对象,但查询代码复杂度更高。
额外注意事项
你现有代码存在两处笔误需要修复:
Packages属性返回时调用的是shipments.ToList(),但你定义的私有字段名为packages,会导致运行时报错- 反射给
packages字段赋值时,dataResponse.Read<BundlePackages>()返回的是IEnumerable<BundlePackages>类型,需要先转成HashSet<BundlePackages>再赋值,否则会抛出类型不匹配异常
内容的提问来源于stack exchange,提问作者Svetlozar Stoykov
相关产品推荐
相关产品推荐

