项目中有个需求,需要一个列表联动九个其它列表,调试起来很费时间,也发现了EventHandler中的一些使用技巧,下面的内容大部分是转载,也有自己的心得,具体出处忘了,如果作者看到,请谅解。
1. 在ItemAdding事件或ItemUpdating事件中获取用户输入的值
通常我们都会遇到这样一个业务逻辑:只有当用户输入的值符合要求才允许用户添加记录,否则取消该操作;或者在用户添加一条记录时, 获取用户输入的值,再去进行其它操作。遇到这些业务,必须的是要在ItemAdding事件中获取到用户用户的值,那我们自然就会很想到一个属性properties.ListItem,这是正在添加的记录项,但恰恰这是添加前的事件,properties.ListItem的值为Null,并不能获取得到,就算在ItemUpdating事件中,properties.ListItem虽然是有值,但并不是用户输入的最新的值,而是原来的值(旧值),需要注意的是properties.BeforeProperties在Updating中也是无法取值的。
想在记录项添加时或修改时得到新的值,我们可以直接使用下面的语句
properties.AfterProperties["Title"].ToString();
如果字段为汉字名,那就要取InternalName了,如下
{
SPList List = web.Lists[properties.ListTitle] as SPList;
sNameTitle = List.Fields["所属部门"].InternalName;
Orgname = properties.AfterProperties[sNameTitle].ToString();
}
2. 在ItemAdding事件或ItemUpdating事件中修改该记录项的值
我们有时候在设计列表的时候,会添加一些对用户隐藏的栏,用于记录与该记录相关的信息,只能通过程序去改写此栏的值,那我们在添加新记录项或修改记录项时怎样修改该栏的值呢?
很自然,我们会写出下面的程序
public override void ItemUpdating(SPItemEventProperties properties)
{
SPListItem item = properties.ListItem;
item["Title"] = "这是我指定的值";
item.Update();
}
但很抱歉,运行这段的下场只有一个——出错!系统会报出“保存冲突”的错误信息。回头仔细看看代码,代码在记录项正在修改时,再次修改了该记录 项。与SQL相似,MOSS也会在进入ItemUpdating事件前,把该记录项上了锁,以防止该记录项在没完成本次修改前给再次修改,因此这样写注定 是失败的!如果是在ItemAdding事件更是如此,在前文说了,在ItemAdding事件时,properties.ListItem的值为Null,该记录项都还没生成,所以根本不可能也不应该通过properties.ListItem来修改某个栏上的值,因为properties.ListItem所表示的是一个记录项旧的值(新增前的旧值没是没有,null)
那通过什么属性来修改呢?MOSS一定会提供一个途径给我们做这事的!我们再来关注一下properties.AfterProperties这个属性,从字面意思可以很容易知道,这是记录项新的值,但很可惜,这个属性是只读的,我们也不能直接修改这个属性达到我们的目的。接着往下看,在properties.AfterProperties中还有一个属性ChangedProperties,连起来看,properties.AfterProperties.ChangedProperties字面意思很 像是该属性保存该字段改变了的值(SDK也是这样说的,Gets properties that have changed in the collection),这很容易就理解成MOSS会自动比较用户修改了记录的哪些值,开始的时候,我也是这样理解的,但很奇怪,无论我怎样修改记录的 值,在调试时,该属性值的Count属性始终是0,并不如我开始所想的!
那时在网上查了很多资料,都只是说奇怪MS为什么会提供ChangedProperties这样一个HashTable类型的属性,而且始终是没有值放到里面去的。在一次反编译MS的官方站点模板中的EventHandler事件程序时,我才恍然大悟!我们应该这样理解ChangedProperties这个HashTable,它是用于给我们修改栏值的一个集合,我觉得SDK的英文应该改成“Gets properties that will be changed in the collection”才对(个人见解),因为在ItemAdding事件或ItemUpdating事件中,记录项并没有真的生成或修 改,ListItem代表的是原来的值,AfterProperties代表的是这个记录项将要变成的值,共中的ChangedProperties提供再次修改的一次机会。所以,要在ItemAdding事件或ItemUpdating事件中修改该记录项的值得在这样写
{
SPList List = web.Lists[properties.ListTitle] as SPList; //取得当前修改的List
sNameTitle = List.Fields["组织编码"].InternalName; //取得字段的名字
string test01 = PItem["组织编码"].ToString();
properties.AfterProperties.ChangedProperties.Add(sNameTitle, PItem["组织编码"]); //将修改的值赋给当前eventhandler的item
}
注意事项:
在进行ItemUpdating,ItemAdding,ItemDeleting编程时,会有两个不同的动作,事件首先会锁定当前ListItem,就是说你甭想改了,然后在Afterproperties中存放将要提交的修改值,这时你可以通过ChangeProperties来修改这个将要提交的值,BeforeProperties这个比较奇怪,什么时候都取不到,基本上没用过。
DisableEventFiring()及EnableEventFiring(),这是一组必要的方法,特别是在Updating和Updated事件中,但也要视具体情况而定,如果涉及到联动其它列表的,一般会DisableEventFiring()
int Value1 = Convert.ToInt32(properties.AfterProperties[Field_InternalName].ToString());
int Value2 = Convert.ToInt32(properties.BeforeProperties[Field_InternalName].ToString());
properties.AfterProperties.ChangedProperties[Field_InternalName] = intInterval2;
尽量先确定前两个,再根据选择使用第三个. 此外如果前两点所述方法都报异常(在写ItemDeleting时会出现),那么则需要获得现在数据库里的值,可以这样做:
{
String swhere = SyList.Fields["身份证号"].InternalName;
String svalue = item["身份证号"].ToString();
SyQry = new SPQuery();
SyQry.Query = "<Where><Eq><FieldRef Name='" + swhere + "'/><Value Type='Text'>" + svalue + "</Value></Eq></Where>";
result = SyList.GetItems(SyQry);
return result;
}
catch (Exception ex)
{
log.Error(ex.ToString());
throw ex;
}
列表的事件调试是个力气活,特别是关联列表比较多的时候,做一个好的数据关系图,是一个良好的习惯,然后就是你需要一个良好的错误捕捉机制,多用log4net(见我上篇文章),在生产环境中可以很轻易的捕捉到错误来自哪里,否则你就哭去吧。