siebel
siebel编程
1.从哪里开始?
流程:
要求:在最需要的地方写最少的代码,且只写一次
(1)能配置的不要通过代码来开发;
(2)决定代码放在哪个对象上:
共用代码写在BS/应用程序级,建议使用BS,因为BS可以被工作流进程调用,但工作流进程不能调用自定义的应用程序级别的方法。
如果某个方法处理的是特定BC上的数据,那么写在这个BC上,这样可以避免在使用该BC的Applet上多次复制类似代码。
applet Server script的使用:查询、插入、更新和删除操作;访问本条记录之外其他记录的数据。
applet browser script的使用:与用户交互;与桌面应用程序交互;对当前记录的验证与处理。
(3)定代码放在哪个事件上:
对BS而言有两大类事件,分别是Pre-event类事件和reactive类事件。
Pre-event的使用:只是想执行某些验证,不会更改除了当前记录以外的任何记录的值,如PreQuery, PreSetFieldValue, PreWriteRecord等;
reactive的使用:记录更改后还想做进一步操作,如writeRecord;
对于applet而言,大部分applet浏览器端代码一般写在Applet_PreInvokeMethod事件中,在这个事件中开发人员可以捕获applet触发的方法,如用于和用户的桌面、用户、applet外观交互的代码。但不建议将所有方法的代码都写在Applet_PreInvokeMethod事件中,而是用来创建自定义方法,并调用这些自定义方法。
(4)添加错误处理模版。在撰写任何代码前先在合适的地方添加错误/异常处理策略,主要是添加错误处理模版应用于事件中。
(5)添加方法主体。将方法主体代码添加在错误处理框架中,这样你就能捕获任何运行时错误。
(6)测试。
2.编码命名规范:
(1)数据类型命名规范
数据类型名 变量命名前缀
Integer 整型 I
String 字符形 S
Boolean 布尔型 B
Float 浮点型 f
(2)不同作用域范围变量的修饰符
作用域 修饰符
全局作用域(仅适用于Siebel VB) G_
模块/实例范围 M_ 或者 i_
函数或者局部作用域 不需要修饰符或者l_
3.一定要为代码添加必要的注释
4.将代码放在合适的事件中:
级别 事件 常用用途
应用程序级 BusComp_PreSetFieldValue 字段级别的验证
应用程序级 BusComp_PreWriteRecord 记录级别的验证
应用程序级 BusComp_PreQuery 代码用来控制SearchSpecs
伴随事件 BusComp_SetFieldValue 字段触发的动作
伴随事件 BusComp_WriteRecord 记录触发的动作
5.在频繁触发的事件处理器中使用快速代码
6.使用运行时BS还是编译后的BS
(1)运行时BS:不会编译到.srf文件中,代码保存在数据库中,可以随时更改它,由于运行时都是使用最新的BS定义,适合于编写逻辑经常变化的BS代码或需要在不发布新.srf文件的情况下发布BS。缺陷是任何具有访问商务服务管理员视图的人都可以看见代码,这可能会导致安全性问题。
(2)编译后的BS:在Siebel Tools中定义的,适用于需要更高安全性和不怎么更改的功能。需要编译,并且通过一个新的.srf来应用更改。优点是具有更高的安全性,且装载的速度更快。
7.使用合适的调试技巧
(1)使用alert(不会停止代码的执行)或RaiseErrorText(会停止代码的执行)方法来弹出消息框;
(2)使用Trace或者自定义方法将信息写入文件;
(3)使用Siebel的Debugger功能;
(4)使用对象管理器级别的日志功能(object manager level logging)。
8.从存储库中移除没有用的代码
(1)移除下面这些代码:被注释的代码;设置为inactive (禁用)的代码;从不调用的代码。
(2)移除空方法
9.在所有的代码中包括错误处理
Try 块:包括你要执行的逻辑;
Catch块:捕获错误;
Finally块:执行任何清理工作,比如销毁对象引用。
10.合适的使用RaiseError 和 RaiseErrorText
MsgBox只能用于Siebel 6.x或更早版本。
RaiseError 和 RaiseErrorText会产生一个服务器端代码异常,使代码的执行在此处终止。
所以,任何必须执行的代码必须放到调用这些方法的前面。
11.使用异常信息:
(1)异常包括两种信息:
错误描述。描述哪里错了。
堆栈跟踪信息。按照执行顺序列出的直到产生异常地方的方法名称。
(2)异常处理的目的:
捕获异常;
使程序更加稳定;
记录异常信息或者通知用户发生了什么事;
可能,重新抛出异常;
可能,设置返回代码。
(3)在eScript中,异常对象通过以下属性(方法)存储错误信息:
errText属性:exception.errText
当系统在运行时碰到一个错误,或者开发人员使用RaiseErrorText来引发一个异常时,该属性就会包含错误信息。
toString()方法:exception. toString()
从COM对象或者开发人员通过throw方法抛出的异常中异常信息。
12.将return语句放到合适的地方
finally块中的return语句将会覆盖掉产生的异常信息或者别人抛给它的异常信息,这些异常信息不会传出方法外面。
当finally块中的代码产生异常信息时,这样的异常信息会传到方法外面,但是之前的异常信息却丢掉了。
function illustrateReturnStatement()
{
var myVar;
try
{
//executable code goes here
myVar = 1;
}
cache(e)
{
if(defined(e.errText))
{
TheApplication().RaiseErrorText("An exception occurred in the "+" of the "+this.Name()+" object. "+"ERROR: "+e.errText+"“STACK:"+e.toString());
}
else
{
TheApplication.RaiseErrorText("An exception occurred in the "+" of the "+this.Name()+" object. "+"ERROR:"+e.errText+"STACK:"+e.toString());
}
}//end catch
finally
{
//cleanup code goes here
}
return myVar;
}
13.使用Top对象来集中浏览器端代码
在客户端代码中,top是到顶层文档的捷径。使用top对象,开发人员可以撰写一个浏览器端代码函数一次,然后从其他的浏览器端对象中调用它。
14.在服务器端代码中何时使用当前上下文,何时使用新的上下文?
当前上下文处理的对象是系统创建用来支持对用户来说可用的数据;
bc = this.BusObject().GetBusComp(“Contact”);
bc=TheApplication().ActiveBusObject().GetBusComp(“Contact”);
bc=this;
而新上下文是在代码中初始化的一系列对象,它与用户当前看到的对象或者数据没有什么关系。
bo = TheApplication.GetBusObject(“Contact”);
bc = bo.GetBusComp(“Contact”);
使用当前上下文:访问用户正在处理的数据;执行的处理(此处应指处理结果)让用户可见。
使用新的上下文:对一个可见的BC执行不可见的查询;在其他的BO上下文中使用一个BC。
15.为变量使用最小的作用域
创建实例级别的变量:
经常会违反封装的原则;
代码的执行会有交叉;
没有事件保证这些对象会被销毁;
理解这类变量的作用域或状态比较困难,因为他们是在一个方法中初始化的,而在另外的方法也可以访问。
更好的做法是:当需要的时候声明和使用对象,然后将他们作为参数传递给其他方法。
16.只在需要时初始化对象,创建没有使用的对象实例会影响系统性能
评估条件—–>创建对象—–>使用对象
17.当对象变量不再使用时要销毁它
(1)对象引用包括:COM对象,属性集 (Property Sets),BS,BC,BO,Applet
(2)应按照程序创建应用对象顺序的逆序来释放对象,先释放子对象,再释放父对象。
Pick/Associate/MVG BC 在父BC之前释放;
BC在BO之前释放;
属性集和BS因为他们是相互独立创建的,所以先释放哪个都没有关系。
(3)应该将对象销毁语句放到错误处理语句中的finally块中,这样不管方法是执行成功还是执行失败后退出,对象都会被销毁的。
finally
{
ChildObject = null;
ParentObject = null;
}
18.使用条件块来运行代码
function BusComp_PreSetFieldValue(FieldName, FieldValue)
{
switch(FieldName)
{
case “Status”:
//do something
break;
…
}
…
}
19.检查返回的对象是不是你想要的
(1)ActiveBusObject方法。该方法返回的是活动Applet对应的BC所对应的BO。
当BC是多个BO的子对象时,那一定要检查返回的BO是不是你想要的。
ActiveBusObject只有在应用程序级代码或者BS中调用才有意义,而在Applet或者BC中你应该使用:this.BusObject() 。
代码示例:
if(TheApplication().ActiveBusObject().Name() == “some name”)
{
//code here
}
(2)ActiveBusComp方法。该方法返回的是与活动Applet相关联的BC。
如果是在BC之外调用该方法,应该检查该方法返回的BC是不是你想要的。
(3)ParentBusComp方法。该方法是根据链接(link)中的子BC返回父BC。总是要检查该方法返回的BC是有效的,且是你想要的。
下面两种情况可能会导致没有可用的引用:
BC是父BC且它没有父BC,比如在Account BO中的Account BC;
在BO中建立的父子关系的link已经被移除了或者更改了。
如果ParentBusComp方法没有返回引用,也不会抛出异常。如果在一个为null的对象上调用方法或者引用属性将会引发运行错误。
var lBC_parent = this.ParentBusComp();
if(lBC_parent != null && lBC_parent.Name() == “some name”)
{
//code
}
20.在使用字段之前检查字段是活动的
在非活动字段上调用GetFieldValue 或者SetFieldValue方法会导致数据丢失或者逻辑混乱。
只能在ExecuteQuery执行成功后使用ActivateField方法。作为一个独立语句,ActivateField不会暗中激活一个非活动字段。
ActivateField告诉系统在BC上下次执行的SQL语句时包括这个列。
ActivateField(<Name>);
ExecuteQuery(ForwardOnly);
GetFieldValue(<Name>);
(1)只要满足下面条件之一,那么服务器端代码字段就是活动的:
系统(保留)字段,如Id, Created, Created By, Updated, Updated By
BC的LinkSpec属性被设置为TRUE ;
BC的Force Active属性被设置为TRUE;
在活动视图中包含了一个Applet定义;
在活动Applet中该字段被用于计算字段的计算公式中;
使用方法BusComp.ActivateField (strFldName) 显式激活。
(2)满足下面条件之一,那么浏览器端代码字段就是活动的:
Id 字段;
在用户界面上该字段可见。
21.Siebel 7 新方法:ctivateMultipleFields, GetMultipleFieldValues 和 SetMultipleFieldValues。
var lbc_account = this;
var lPS_FieldNames = TheApplication().NewPropertySet();//创建lPS_FieldNames 来保存字段名称
var lPS_FieldValues = TheApplication().NewPropertySet();//创建lPS_FieldValues 在保存字段值
var ls_account_products;
var ls_agreement_name;
var ls_project_name;
var ls_description;
var ls_name;
//使用SetProperty方法设置值以后,调用方法ActivateMultipleFields,Siebel应用程序就会以参数形式传递属性集。
//set up the property set which will be used in all three methods to hold the field names.
lPS_FieldNames.SetProperty("Account Products", "");
lPS_FieldNames.SetProperty("Agreement Name", "");
lPS_FieldNames.SetProperty("Project Name", "");
lPS_FieldNames.SetProperty("Description", "");
lPS_FieldNames.SetProperty("Name", "");
//activate the fields using the property set which has the field names
lbc_account.ActivateMultipleFields(lPS_FieldNames);
lbc_account.ExecuteQuery(ForwardOnly);
if (lbc_account.FirstRecord())
{
//当执行ExecuteQuery以后,调用方法GetMultipleFieldValues 来获取字段值
//retrieve the values. This method acts sort of like a BS in that there is an input property set and an output property set.
//The field values will be in the second property set passed in.
lbc_account.GetMultipleFieldValues(lPS_FieldNames,lPS_FieldValues);
//loop through property set to get values.
ls_account_products = lPS_FieldValues.GetProperty("Account Products");
ls_agreement_name = lPS_FieldValues.GetProperty("Agreement Name");
ls_project_name = lPS_FieldValues.GetProperty("Project Name");
ls_description = lPS_FieldValues.GetProperty("Description");
ls_name = lPS_FieldValues.GetProperty("Name");
}
//now set new values in the property set
lPS_FieldNames.SetProperty("Account Products", "All My Products");
lPS_FieldNames.SetProperty("Agreement Name", "Siebel Agreement");
lPS_FieldNames.SetProperty("Project Name", "Siebel Project #2");
lPS_FieldNames.SetProperty("Description", "This is the description");
lPS_FieldNames.SetProperty("Name", "Joey Joe Joe Junior Shabbidoo");
//set the field values
lbc_account.SetMultipleFieldValues(lPS_FieldNames);
//commit the data
lbc_account.WriteRecord();
22.为查询使用合适的视图模式
with (bcAcct)
{
SetViewMode(this.GetViewMode());
}
23.只查询索引列
24.使用ForwardOnly游标模式
25.在执行查询后检查是否有合法的记录存在
bcContact.ClearToQuery();
bcContact.SetSearchSpec(“Id”, sContactId);
bcContact.ExecuteQuery(ForwardOnly);
//Check to see that a record was actually returned by examining the return of FirstRecord().
if (bcContact.FirstRecord())
{
//okay to perform data processing…
}
26.使用Switch(eScript)或者Select Case语句(Siebel VB)
当需要将某个表达式的值和多个可能性进行比较的时候,最快和最具可读性的方法就是使用switch。
因为表达式只被求值一次,然后和不同的值进行比较,所以效率更高;
比一大堆嵌套的if…else if 语句更具可读性,使用switch或者select case语句往往能把多页代码压缩成一页。
27.在If/Else If语句中比较相同的条件
var ls_first = “first”;
var ls_second = “second”;
if (ls_first == “first”)
{
//do something……
}
else if (ls_second == “second”)
{
//do something else………
}
28.使用Associate方法创建中间表(intersection table)记录
var lBC_mvg = this.GetMVGBusComp(“Sales Rep”);
var lBC_associate = lBC_mvg.GetAssocBusComp();
with(lBC_associate)
{
ClearToQuery();
SetSearchSpec(“Id”, SomeRowId);
ExecuteQuery(ForwardOnly);
if(FirstRecord()) Associate(NewAfter);
}
29.使用动态值
最好使用动态值代替将值硬编码到代码中。这对于那些经常更改的值来说特别重要,因为如果你硬编码的话,编码的任何变化都要重新编译和发布。
代码中经常要用到的值都可以通过TheApplication.GetProfileAttr() 函数来获取。
此外,还可以将值存储在List Of Values表或其他表中,在需要的时候查询,用于在代码中要查询的只有一个动态值或较短的值列表时。
//下面代码中,LOV值是从List Of Values这个BC中查询出来的,然后将他们组合成一个查询过滤表达式。
var statusList;
var moreRecords;
var boLOV;
var bcLOV;
boLOV = TheApplication().GetBusObject("List Of Values");
bcLOV = boLOV.GetBusComp("List Of Values");
bcLOV.ClearToQuery();
bcLOV.SetSearchSpec("Type","SR_STATUS");
bcLOV.SetSearchSpec("Active","Y");
bcLOV.ExecuteQuery(ForwardOnly);
if (bcLOV.FirstRecord())
{
statusList = bcLOV.GetFieldValue("Name");
moreRecords = bcLOV.NextRecord();
while (moreRecords != 0)
{
statusList = statusList + " OR ";
statusList = statusList + bcLOV.GetFieldValue("Name");
moreRecords = bcLOV.NextRecord();
}
}
else
{
statusList = "";
}
30.使用逻辑常量还是字面量
最好使用逻辑常量,它使代码更加易读且更加容易升级,使用字面量有可能给升级带来问题。
类型 逻辑常量 字面量
游标模式 ForwardBackward 0 (默认值)
ForwardOnly 1
视图模式 SalesRep View 0
ManagerView 1
PersonalView 2
AllView 3
OrganizationView 5
ContactView 6
GroupView 7
CatalogView 8
SubOrganizationView 9
新记录位置 NewBefore 0 (默认值)
NewAfter 1
NewBeforeCopy 2
NewAfterCopy 3
31.避免使用Exit Function 和 Exit Sub
仅针对Siebel VB,应避免使用Exit Function 和 Exit Sub。
32.将GotoView放到代码的结尾
GotoView语句不会立即退出代码的执行并导航到指定的视图,而是将该语句挂起来直到其他的代码执行完毕,所以最好将该语句放到代码的结尾处。
33.合适的使用DeleteRecord
DeleteRecord 会隐式的将记录指针移到记录集中的下一行。在DeleteRecord 后调用 NextRecord 将会使记录指针移动两次,
也就是说Siebel应用程序会跳过记录集中的一条记录,如果你在一个循环中这样删除记录,Siebel应用程序将会隔行删除记录。
在循环中删除记录的方法(Siebel VB):
While(BC.FirstRecord <> 0)
BC.DeleteRecord
Wend