DynamicControl/DynamicField 例外的解决

本文介绍了一种解决DynamicField和DynamicControl在PostBack时出现异常的方法,通过继承并覆盖原有类,确保即使数据源未绑定也能避免错误,适用于各种绑定场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  在使用 DynamicField / DynamicControl 时,经常在PostBack时,会出现一个例外:

  The DynamicControl/DynamicField needs to exist inside a data control that is bound to a data source that supports Dynamic Data.

  中文为:

  绑定到支持动态数据的数据源的数据控件内必须存在 DynamicControl/DynamicField


  具体发生原因暂时不太清楚,估计是PostBack的事件流程和页面正常加载不一致。当PostBack后,数据控件(如DetailsView, GridView, ListView 等)在状态加载时, 会把所有的 Fileds或Columns初始化一次,而这一次,由于数据源没有绑定,所以找不到相关的 MetaTable。在这种情况下,DynamicControl 和 DynamicField 都是丢出上述的例外。

  不过,从应用上来看,如果数据绑定做的合理的话(比如在InitComplete以前绑定),是不会出现这个问题的。但是一旦出了这个问题,解决起来就比较麻烦了,因为常常有些人习惯于在Load中绑定数据,要修改的话,常常会牵连一大堆的代码。希望对这个问题比较了解的朋友多多指教。

  目前我采用的方法,可以不改变原有的绑定流程,可以跟以前的Eval、Bind等一样使用,可以让大家试试。在使用中如有发现有Bug,请发邮件告诉我。因为我最近几个月都在应用 DynamicFieldTemplates 来开发应用程序,对这些问题比较关心。

  使用这种方法,可以让一些程序经验不足的人,也能避开这个错误。

  我分别从DynamicControl 和 DynamicField 继承了新的类 DdControl 和 DdField,然后在初始化时,判断是否存在MetaTable,如果不存在,则不再初始化。然后,程序中原本所有采用 DynamicControl/DynamicField的地方,都换成DdControl/DdField。

  DdControl/DdField的源码如下:

namespace Common
{
    
public class DdField : DynamicField
    {
        
public override void InitializeCell(System.Web.UI.WebControls.DataControlFieldCell cell, System.Web.UI.WebControls.DataControlCellType cellType, System.Web.UI.WebControls.DataControlRowState rowState, int rowIndex)
        {
            
// HACK: Fix bug for: The DynamicControl/DynamicField needs to exist inside a data control that is bound to a data source that supports Dynamic Data.
            if (base.Control.FindMetaTable() == null)
                
return;

            
base.InitializeCell(cell, cellType, rowState, rowIndex);
        }

    }
}

 

namespace Common
{
    
public class DdControl : DynamicControl
    {
        
protected override void OnInit(EventArgs e)
        {
            
// HACK: Fix bug for: The DynamicControl/DynamicField needs to exist inside a data control that is bound to a data source that supports Dynamic Data.
            if (this.FindMetaTable() != null)
                
base.OnInit(e);
        }
    }
}


  使用时,只要在web.config中配置好前缀,就可以直接使用了。如:

<!-- 假设上述的程序生成 Common.dll -->
<add tagPrefix="asp" namespace="Common" assembly="Common"/>

 

<DetailsView >
  
<Fields>
    
<asp:DdField DataField="Name" />
    
  
</Fields>
</DetailsView>

 


namespace Common
{
    
public class DdControl : DynamicControl
    {
        
protected override void OnInit(EventArgs e)
        {
            
// HACK: Fix bug for: The DynamicControl/DynamicField needs to exist inside a data control that is bound to a data source that supports Dynamic Data.
            if (this.FindMetaTable() != null)
                
base.OnInit(e);
        }
    }
}
@GetMapping("/get-import-sdn-meter-template") @Operation(summary = "获得小口径水表批量导入模板") public void importSdnMeterTemplate(HttpServletResponse response) throws IOException, NotFoundException, CannotCompileException { List<EquipmentPropertyDO> equipmentPropertyList = equipmentPropertyService.getEquipmentPropertyList(); // 定义新字段 DynamicClassCreator.FieldDefinition newField = new DynamicClassCreator.FieldDefinition("dynamicField", "java.lang.String", "动态列"); // 创建动态子类 Class<?> dynamicClass = DynamicClassCreator.createDynamicSubclass( SdnMeterImportExcelVO.class, "com.example.DynamicChild_" + System.currentTimeMillis(), new DynamicClassCreator.FieldDefinition[]{newField} ); // 手动创建导出 demo List<SdnMeterImportExcelVO> list = Arrays.asList( SdnMeterImportExcelVO.builder().meterCode("cs000001").electronicModuleCode("mk000001").categoryId(3).meterDnId(1) .manufacturerId(1).standardId(1).quality("铁").withValve(true).meterRange(1).productionDate("2024-12-01") .batteryReplacementDate("2025-12-01").serviceLifetimeYears(1).sealNo("001") .withRemoteTransmission(true).communicationMethod(1).simNo("001").imei("001").imsi("001").iccid("001").serviceProvider("运营商1").build()); // 输出 ExcelUtils.write(response, "小口径水表批量导入模版.xlsx", "小口径水表", SdnMeterImportExcelVO.class, list); } 请修改上边代码,使得导出的excel用动态生成的类替换SdnMeterImportExcelVO类,并保持所有注解属性不变,其中类SdnMeterImportExcelVO的源代码为: package cn.iocoder.yudao.module.tdsw.controller.admin.equipment.vo.equipment; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.tdsw.convert.EquipmentstandardConvert; import cn.iocoder.yudao.module.tdsw.convert.ManufacturerConvert; import cn.iocoder.yudao.module.tdsw.convert.MeterDnConvert; import cn.iocoder.yudao.module.tdsw.convert.TdswEquipmentCategoryConvert; import cn.iocoder.yudao.module.tdsw.framework.excel.core.EquipmentStandardExcelColumnSelectFunction; import cn.iocoder.yudao.module.tdsw.framework.excel.core.ManufacturerExcelColumnSelectFunction; import cn.iocoder.yudao.module.tdsw.framework.excel.core.MeterDnExcelColumnSelectFunction; import cn.iocoder.yudao.module.tdsw.framework.excel.core.TdswEquipmentCategorySdnExcelColumnSelectFunction; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import static cn.iocoder.yudao.module.tdsw.enums.DictTypeConstants.*; /** * 用户 Excel 导入 VO */ @Data @Builder @AllArgsConstructor @NoArgsConstructor @Accessors(chain = false) // 设置 chain = false,避免导入有问题 public class SdnMeterImportExcelVO { @ExcelProperty("表编号") private String meterCode; @ExcelProperty("电子模块编号") private String electronicModuleCode; @ExcelProperty(value = "表类型", converter = TdswEquipmentCategoryConvert.class) @ExcelColumnSelect(functionName = TdswEquipmentCategorySdnExcelColumnSelectFunction.NAME) private Integer categoryId; @ExcelProperty(value = "表口径", converter = MeterDnConvert.class) @ExcelColumnSelect(functionName = MeterDnExcelColumnSelectFunction.NAME) private Integer meterDnId; @ExcelProperty(value = "厂商名称", converter = ManufacturerConvert.class) @ExcelColumnSelect(functionName = ManufacturerExcelColumnSelectFunction.NAME) private Integer manufacturerId; @ExcelProperty(value = "规格型号", converter = EquipmentstandardConvert.class) @ExcelColumnSelect(functionName = EquipmentStandardExcelColumnSelectFunction.NAME) private Integer standardId; @ExcelProperty("材质") private String quality; @ExcelProperty(value = "是否有阀门",converter = DictConvert.class) @DictFormat(TDSW_WITH_VALVE) @ExcelColumnSelect(dictType = TDSW_WITH_VALVE) private Boolean withValve; @ExcelProperty(value = "表量程",converter = DictConvert.class) @DictFormat(TDSW_METER_RANGE) @ExcelColumnSelect(dictType = TDSW_METER_RANGE) private Integer meterRange; @ExcelProperty("出厂日期") private String productionDate; @ExcelProperty("更换电池最后期限") private String batteryReplacementDate; @ExcelProperty("服务年限") private Integer serviceLifetimeYears; @ExcelProperty("铅封号") private String sealNo; @ExcelProperty(value = "是否有远程传输",converter = DictConvert.class) @DictFormat(TDSW_WITH_REMOTE_TRANSMISSION) @ExcelColumnSelect(dictType = TDSW_WITH_REMOTE_TRANSMISSION) private Boolean withRemoteTransmission; @ExcelProperty(value = "通讯方式",converter = DictConvert.class) @DictFormat(TDSW_COMMUNICATION_METHOD) @ExcelColumnSelect(dictType = TDSW_COMMUNICATION_METHOD) private Integer communicationMethod; @ExcelProperty("SIM卡号") private String simNo; @ExcelProperty("IMEI") private String imei; @ExcelProperty("IMSI") private String imsi; @ExcelProperty("ICCID") private String iccid; @ExcelProperty("运营商") private String serviceProvider; } 其中DynamicClassCreator的源代码为: package cn.iocoder.yudao.module.tdsw.utils; import javassist.*; import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.ClassFile; import javassist.bytecode.ConstPool; import javassist.bytecode.annotation.Annotation; import javassist.bytecode.annotation.StringMemberValue; import java.lang.reflect.Field; import java.util.Arrays; import com.alibaba.excel.annotation.ExcelProperty; public class DynamicClassCreator { public static Class<?> createDynamicSubclass(Class<?> superClass, String newClassName, FieldDefinition[] newFields) throws NotFoundException, CannotCompileException { // 1. 初始化ClassPool ClassPool pool = ClassPool.getDefault(); // 2. 创建新类并设置父类 CtClass ctChildClass = pool.makeClass(newClassName); ctChildClass.setSuperclass(pool.get(superClass.getName())); // 3. 复制父类所有字段(包括注解) copyFieldsFromParent(superClass, ctChildClass, pool); // 4. 添加新字段及注解 for (FieldDefinition fieldDef : newFields) { addFieldWithAnnotation(ctChildClass, fieldDef, pool); } // 5. 生成并返回Class对象 return ctChildClass.toClass(); } // 复制父类字段(保留注解) private static void copyFieldsFromParent(Class<?> superClass, CtClass ctChildClass, ClassPool pool) throws NotFoundException, CannotCompileException { // 遍历所有父类字段(包括私有字段) for (Field field : superClass.getDeclaredFields()) { try { CtField ctField = CtField.make( String.format("private %s %s;", field.getType().getName(), field.getName()), ctChildClass ); // 复制字段注解 ConstPool constPool = ctChildClass.getClassFile().getConstPool(); AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); Arrays.stream(field.getDeclaredAnnotations()) .forEach(ann -> { Annotation javassistAnn = new Annotation(ann.annotationType().getName(), constPool); // 这里可扩展:复制注解参数(简化版仅处理注解类型) attr.addAnnotation(javassistAnn); }); if (attr.numAnnotations() > 0) { ctField.getFieldInfo().addAttribute(attr); } ctChildClass.addField(ctField); } catch (Exception e) { throw new CannotCompileException("Failed to copy field: " + field.getName(), e); } } } // 添加带注解的新字段 private static void addFieldWithAnnotation(CtClass ctChildClass, FieldDefinition fieldDef, ClassPool pool) throws CannotCompileException { try { // 创建字段 CtField ctField = new CtField( pool.get(fieldDef.type), fieldDef.name, ctChildClass ); ctField.setModifiers(Modifier.PRIVATE); // 添加@ExcelProperty注解 ConstPool constPool = ctChildClass.getClassFile().getConstPool(); AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); Annotation excelProperty = new Annotation(ExcelProperty.class.getName(), constPool); // 设置注解参数(例如value) excelProperty.addMemberValue("value", new StringMemberValue(fieldDef.excelHeader, constPool)); attr.addAnnotation(excelProperty); ctField.getFieldInfo().addAttribute(attr); ctChildClass.addField(ctField); } catch (NotFoundException e) { throw new CannotCompileException("Invalid field type: " + fieldDef.type, e); } } // 新字段定义结构 public static class FieldDefinition { String name; String type; String excelHeader; // @ExcelProperty注解的header值 public FieldDefinition(String name, String type, String excelHeader) { this.name = name; this.type = type; this.excelHeader = excelHeader; } } }
最新发布
06-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值