报表开发框架
报表开发流程
报表开发基本工艺路线
DataCommand
- 应用设计器设计DataComand模型
- 生成代码,添加取数逻辑。
- 调试
报表模板
- 导入DataCommand生成缺省报表模板。
- 应用报表设计器设计报表模板
- 报表模板发布
报表UI设计
- 报表方案管理设计(公共)
- 报表Form设计(结果Form绑定报表模板)
- 报表格式/数据处理策略。(应用报表元数据服务)
- 客户端展现。
DataCommand开发
说明
- 当报表数据查询逻辑复杂到无法用SQL或OQL实现时需要自定义数据查询。
- ReportServer是报表主要服务器,UBF报表用自定义扩展类接管U9报表的所有数据读写功能。
- 数据查询扩展物理上与ReportServer运行于同一进程。
- 自定义的扩展类必须继承BaseReportDataCommand(基类提供接口的默认实现)。
用DataCommand实现复杂报表的设计步骤
1.创建专用的工程,引用下列报表服务提供的组件
UFSoft.UBF.Report.Base.dll
UFSoft.UBF.Report.Interface.dll
2.继承BaseDataCommand类创建业务逻辑实现类,确定业务逻辑中需要的查询参数。测试保证类能正确执行,特别是当参数无值是应按默认的参数值输出;
3.编译工程,并将动态库注册到报表设计器。
4.在报表设计器中创建新报表,创建数据查询,将数据来源映射到业务逻辑实现类中(设计器提供特定的方法)。
5.创建查询参数(查询参数的数量和名称应该在业务逻辑中已事先确定),将查询参数映射到报表参数。
6.执行查询取得结果集合的Schema之后设计报表格式。
7.发布动态库到报表服务器并注册。
8.在设计器中调试报表,确定成功后发布报表。
继承BaseReportDataCommand实现报表复杂业务逻辑
BaseReportDataCommand基本编程模式
BaseReportDataCommand用于支持服务器端业务逻辑编程模型,该类即实现了对报表数据逻辑的扩展又实现了向下的编程接口,任何复杂业务逻辑都必须继承该类。
- BaseReportDataCommand编程接口说明
属性: | 说明 | |
DefaultConnectString | 默认的数据库连接字符串 | |
QueryContext | 用于报表查询的上下文,运行环境中报表上下文将包含UBF系统上下文。 | |
Parameters | 报表查询参数集合 | |
Groups | 分组查询参数集合 | |
Orders | 排序参数集合 | |
Select | 反映客户端栏目的变化 | |
可覆盖方法: | ||
IDataReader ExecuteDataReader() | 执行业务逻辑返回数据集 | |
- BaseReportDataCommand生存周期
使用查询参数
查询参数从基类的Parameters属性中获取,查询参数只用于业务逻辑中的数据过滤操作。类接口详细说明如下表:
类/接口 | 属性/方法 | 说明 | |
IQueryParameter/QueryParameter | 查询参数描述 | ||
Name | 参数名称 | ||
Expression | 条件表达式:如果关联了报表参数:则按SQL条件语法返回当前条件项的条件表达式:如cDepCode =’01’ | ||
DataType | 参数类型:字符、数值、日期、逻辑 | ||
Operator | 如果关联了报表参数:则返回界面输入比较方式,如>,=,<,<>,between等否则默认为等于 | ||
Values | 返回参数值集合:如报表条件输入Between 10 to 20 则 Values{value[0]=10,value[2] =20} ,如果输入值:in (10,20,30,40)则Values{value[0]=10,value[1]=20,value[2]=30} | ||
Value | 返回参数值集合的第一个元素,按参数类型转型。 | ||
QueryParameterCollection | |||
ToString():string | 将所有查询参数的条件表达式用 and 组织成不带Where子句的条件串 | ||
ToString(indexlist:int[]):string | 组织条件串,只用IndexList中枚举的查询参数 | ||
ToString(nameList:string[]):string | 组织条件串,只用nameList中枚举的查询参数 | ||
使用分组参数
类/接口 | 属性/方法 | 说明 |
IGroupParameter/GroupParameter | 分组参数项 | |
FieldExpression | 分组字段或表达式 | |
GroupParameterCollection | 分组参数集合 | |
ToString():string | 按SQL语法返回不带Group by关键字的分组子句 |
使用排序参数
类名 | 属性/方法 | 说明 |
IOrderParameter/OrderParameter | 排序参数项 | |
FieldExpression | 排序字段或表达式 | |
Order | True 升序,false降序 | |
OrderParameterCollection | 排序参数集合 | |
ToString() | 按SQL语句返回不带Order by关键字的排序子句 |
使用栏目参数
类名 | 属性/方法 | 说明 |
ISelectParameter/SelectParameter | 栏目参数项 | |
FieldName | 栏目字段 | |
SelectParameterCollection | 栏目参数集合 | |
所有方法都继承自List类 |
使用上下文
类名 | 属性/方法 | 说明 |
QueryContext | 上下文参数,用于传递运行环境中的参数,或其它需要从客户段收集的参数,所有用于传递的对象都必须可序列化。 | |
Add() | 增加上下文 | |
GetValue() | 取得上下文 | |
SetValue() | 设置上下文 | |
Contains() | 判断指定名称的上下文是否存在 | |
Clear() | 清除所有的上下文 | |
Remove() | 删除特定的上下文 | |
ToBase64String() | ||
FromBase64String() |
UFIDA.UBF.Report.App.Data 说明
DataCommand 功能
DataCommand 是利用报表服务传递过来的 报表参数,Selects,Groups ,Orders 等集合组织一个取数的逻辑。过程如下图所示:
3 步:给DataCommand设置数据源
4步:给DataCommand设置报表参数。
5步:给DataCommand设置上下文,包括Selects ,Groups,Orders等。
6步:调用DataCommand
7步:返回DataCommand的执行结果 IdataReader。
absDataCommand 实现的是上图中的第6步:执行具体的DataCommand执行取数逻辑。
该抽象类继承了报表引擎的 BaseReportDataCommand ,对该基类进行了简单封装,
所有DataCommand都必须继承此类。如下图:
在该类中提供了3中返回DataReader的方法:
A 重写 ExeDataReader 方法
对于简单报表可以重写该方法,不需要创建临时表,直接返回结果IdataReader
B 重写public virtual DataTable ExecuteDataTable() 方法。
对于需要从 DataTable返回数据的报表可以重写 该方法。
对于A,B 两种方式都需要自己处理ShowMode=0时,返回空集的情况,一般可以给结果Oql加一个 where 0=1 的限定条件。
C 实现 GetOqlString()中的 ProcessData() 方法就可以了,该方式总是需要一个结果临时表,处理完后把最终的数据放入该临时表,这里代码生成时临时表名称是
ResultTempTable 。
DataCommand 主要属性,方法说明
1 ShowMode 参数说明:
ShowMode=0:表示返回空的临时表,在报表第一次展现或者报表业务处理逻辑出现错误时,返回空的临时表。
ShowMode=1 ,执行正常的报表逻辑,该常数在AbsReportDataCommand的已经做过处理。
2 EntityViewQuery : viewQuery
该对象主要用来执行Oql返回数据,创建临时表
注意: 利用该对象执行Oql时,需要使用一个数据库连接,该连接的状态有基类 BaseReportDataCommand来维护,在报表子类中不要处理,在整个报表业务逻辑处理要确保使用一个EntityViewQuery,不要使用 New EntityViewQuery 来创建。
3 在DataCommand中 报表服务给报表传过来几个集合:
关于查询参数,分组参数,排序参数,上下文的说明,参见:上面介绍
Paramsters ,Selects ,Groups ,Orders ,QueryContext
Paramsters :中包含了报表UI给DataComand的传过来的查询条件,其中包含两部分:
控制参数 ControlParams
它主要是用来控制报表的格式,以及哪些条件,哪些栏目需要处理。一般不是作为报表的查询条件使用的(每个控制参数都需要特殊处理)
查询参数: 这是直接作为报表查询条件的,其中大部分条件可以直接使用,但是有小部分需要经过特殊处理后才能使用。(这是报表条件处理比较麻烦的一部分。)
GroupParameterCollection Groups
OrderParameterCollection Orders
SelectParameterCollection Select
Select:用户选择栏目
Groups :包含行分组,列分组
Orders:包含了哪些列需要进行排序,这个在DataCommand基类里已经做过处理。
4 List AllSelectFieldList
AllSelectFieldList : 包含了全部用户可以选择的列,一般情况Select 集合中只有用户选择的少量的列,而 AllSelectFieldList是全部列,应为DataCommand返回列总是固定的,所有只有Select集合中的列才需要取数,而其它列则取Null就可以了。
5 public override IDataReader ExecuteDataReader()
ReportService 调用DataCommand只需要返回一个dataReader ,该方法已经在基类里做了默认实现,在具体的DataCommand中就不需要实现了。在该方法中调用了报表取数的3中方式。
A 该方法用于简单的报表,不需要创建临时表的情况
public virtual IDataReader ExeDataReader()
{
return null;
} //end ExecuteDataReader
B //该方法用于返回DataTable的情况。
public virtual DataTable ExecuteDataTable()
{
return null;
} //
C protected internal abstract string GetOqlString();
代码生成时生成了GetOqlString()方法
该方法需要每个DataCommand中都去实现具体的逻辑,要根据设计文档去写。
public virtual string GetResultOql(string tableName, string showMode)
该方法是构造一个最终结果的Oql:从结果临时表上取数。
ShowMode=0 时:返回空的结果临时表
Select * from TempTable where 0=1
ShowMode=1 时返回正常的结果。
6 其它集合
FilterDefines FilterDefines
该集合是报表参数集合
FieldCollection FieldCollection
该集合是结果字段集合
List DefaultColumnList
该集合是DataCommand导入时,生成DataTable列的集合,如果该集合为空,则使用DataCommand返回的所有列。
以上3个集合是在代码生成时自动生成。并且在Extend文件中留有手工添加子项的方法块。
DataSource基类的说明
属性: | 说明 | |
_util | 用于拼装结果Oql语句的工具类 | |
Selection | Select 字段集合类 | |
Conditions | Condition 集合类 | |
Groups | 分组集合类 | |
Orders | 排序集合类 | |
方法: | ||
AddSelect(exp : string) : void | 为结果Oql添加select 子项 | |
AddCondition(p:IQueryParameter) | 为结果Oql添加条件 子项 | |
AddGroup(exp : string) : void | 为结果Oql添加分组 子项 | |
AddOrder(exp : string, isAsc : bool) | 为结果Oql添加排序 子项 | |
GetOqlString() : String | 返回_util工具类拼装好的Oql | |
SetCustomerJoinInfo(List ) | 用于给数据源手工指定连接(该方法已经废弃) | |
RemoveSelect(aliasName:string) | 删除栏目 | |
RemoveCondition(aliasName:string) | 删除条件 | |
RemoveGroup(aliasName:string) | 删除分组 | |
RemoveOrder(aliasName:string) | 删除排序 |
说明: 该类主要是为DataCommand提供拼装Oql的功能 ,简化其复杂度。所有数据源都必须继承该类.
AddSelect ,AddCondition,AddGroup,AddOrder方法都提供了多个重载.
工作原理:
实体别名Map: 主要是给实体定义别名影射,方便使用
aliasMap.Add("INVTotalCostLine" , "UFIDA::U9::InvTrans::MonthClose::INVTotalCostLine");
aliasMap.Add("ItemCategoryCross" , "UFIDA::U9::CBO::SCM::Item::ItemCategoryCross");
字段别名Map :定义字段别名影射
map.Add(INVTotalCostLine_SOB , "INVTotalCostLine.SOB");
map.Add(INVTotalCostLine_CostField , "INVTotalCostLine.CostField");
定义了实体Map,字段Map后,你可以使用 DataSourde提供的
AddSelect ,AddCondition ,AddGroup ,AddHaving,AddOrder 等方法来提供Oql元素,最后调用 GetOqlString()来返回最终的Oql。
以上几个方法都提供了多种重载方法,请自己选择使用。
注意: 当你需要使用 Select Top 5 或者 Select distinct 的语句时 ,你需要先调用一下SetSelectKey(”select top 5 “),这样在返回Oql时会加上该语句。
SetCustomerJoinInfo(CustomerJoin) 该方法主要是用在 设置两个没有直接关联关系的两个实体建立连接的一种方式。
例如:
Ds.SetCustomerJoinInfo(new CustomerJoinInfo("INVTotalCostLine.INVTotal.ItemInfo.ItemID.ID", "ItemCategoryCross.Item", JoinType.LeftJoin));
调用该方法使 物料信息上的物料和物料交叉表通过物料的ID和物料交叉表的物料建立关联。你就可以使用物料交叉表的数据了。
说明: 该方法(SetCustomerJoinInfo)将要废弃 ,因为它始终要把指定的两个表进行连接,不管是否选取了 第二个实体的字段 ,这样会有效率问题。
现在推荐使用 如下方式 :
在 DataSource 中重写 GetCustomJoinMap() 方法
public override IDictionary GetCustomJoinMap()
{
CustomerJoinInfo test1 = new CustomerJoinInfo("INVTotalCostLine.INVTotal.ItemInfo.ItemID.ID", "ItemCategoryCross.Item", JoinType.LeftJoin);
IDictionary dic = new Dictionary();
CustomerJoinInfo test2 = new CustomerJoinInfo("INVTotalCostLine.LotInfo.LotMaster.EntityID", "LotMaster.ID", JoinType.LeftJoin);
//test2.ConditionString = MyDs.xxx + "=1";
dic.Add("UFIDA::U9::CBO::SCM::Item::ItemCategoryCross", test1);
dic.Add("UFIDA::U9::Lot::LotMaster", test2);
return dic;
}
这样在 只有在Oql中使用了 物料分类,批号中的属性时才会关联该表。
工具类的说明
方法:(静态方法) | ||
String GetSql(string oql) | 将Oql翻译成Sql后返回 | |
String GetSql(string oql,EntityViewQuery q) | 将Oql翻译成Sql后返回(用于带有临时表的语句 |
方法:(静态方法) | |
CreateTempTableByOql | 通过Oql创建临时表 |
AppendNonQueryForTempTable | 将新的Oql返回的数据附加到指定的临时表中 |
GetTempTableDataSet(tempTable,strOql,q) | 通过Oql创建临时表,并将表中的数据作为DataSet返回。 |
GetTempTableDataSet(strOql,q) | 通过Oql返回DataSet,q 用来读取临时表的数据 注意: q 必须和创建临时表的q 是同一个. |
ExecuteDataReader(oql, viewQuery: EntityViewQuery) | 执行Oql从临时表中返回DataReader |
ExecuteScalar(oql, viewQuery: EntityViewQuery) | 执行Oql返回结果集的第一行,第一列的值 |
方法:(静态方法) | |
GetInString(List lnValues, bool inCludeSingleComma) | 返回由 List 子项构造的 In字符串 |
GetInString(DataSet ds,string columnName, bool inCludeSingleComma) | 返回由 DataSet中某列构造的 In字符串 |
UFIDA.UBF.Report.App.Data代码结构说明
关于DataCommand中的元素国际化问题:
定义一个资源管理类如下:
Public Class ResManager
{
Public static Test
{
Get
{
Return “Test”;
}
}
}
Oql使用说明
实体和数据库表的对应关系
实体属性有以下几种情况:
A 普通类型
实体中的简单属性直接对应表中的字段(名称一致,类型一致)
B 通用实体
实体中的通用实体属性对应表中的两个字段: 类型名称_EntityID(long),属性类型名称_EntityType(varchar(100))
关于通用实体实体类型如何取:(对于通用实体只有EntityID,EntityType )
它会在本表中生成两个字段: 属性名_EntityID,属性名_EntityType
Oql:select SOSourceEntityKey.EntityID , SOSourceEntityKey.EntityType
from UFIDA::U9::SD::SaleOrder::SOShipline
Sql select A.[SOSourceEntityKey_EntityID], A.[SOSourceEntityKey_EntityType] from SD_SOShipline as A
C 属性类型
实体中的一个属性类型对应表中的n个字段,格式:属性类型名称_属性类型下的属性名(类型一致)
(有几个属性就会在表中产生几个字段,如果该字段是国际化字段,该字段会产生在对应的国际化表中,
取这种字段时会关联国际化表)
关于属性类型的取值方式(不能直接使用属性类型,只能使用它下面的字段)
例子:
Oql:
select ShipmentPlanQtyTradeUOM.Amount ,ShipmentPlanQtyTradeUOM.UOM.Name
from UFIDA::U9::SD::SaleOrder::SOShipline
Sql:
select A.[ShipmentPlanQtyTradeUOM_Amount], A2.[Name]
from SD_SOShipline as A
left join [Base_UOM] as A1 on (A.[ShipmentPlanQtyTradeUOM_UOM] = A1.[ID])
left join [Base_UOM_Trl] as A2 on (A2.SysMlFlag = 'zh-CN') and (A1.[ID] = A2.[ID])
这个ShipmentPlanQtyTradeUOM.UOM.Name通过本表中的ShipmentPlanQtyTradeUOM_UOM 再和UOM([Base_UOM])关联,
因为Name是国际化字段,所以要和[Base_UOM_Trl] 关联.
// ProvideSupplier 是属性类型
Oql:
select ProvideSupplier.SupplierName from UFIDA::U9::SD::SaleOrder::SOShipline
Sql:
select A1.[ProvideSupplier_SupplierName]
from SD_SOShipline as A
left join [SD_SOShipline_Trl] as A1 on (A1.SysMlFlag = 'zh-CN') and (A.[ID] = A1.[ID])
因为ProvideSupplier 是属性类型 所以ProvideSupplier.SupplierName 在本表(SD_SOShipline)中会有ProvideSupplier_SupplierName 又因为ProvideSupplier_SupplierName 是国际化字段,
所以要在SD_SOShipline 对应的国际化表中找。
D 实体类型
实体中的实体类型在本表中只保留一个Long型的字段(名称和属性名称一致)
当需要取该字段下的其他属性时(如:Name ) ,此时会产生和该实体的关联来取该实体的字段
报表开发框架
本文2024-08-20 18:26:48发表“u9cloud知识”栏目。
本文链接:https://wenku.my7c.com/article/yonyou-u9cloud-1188.html