关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复170或者20151109可方便获取本文,同时可以在第一时间得到我发布的最新的博文信息,follow me!
我们知道Dynamics CRM中本身也有类似复制记录的功能,比如报价单的升版,就是复制出来整个报价单,然后版本号加一。我以前在项目中做过复制记录,就是类似升版本的功能,具体是审批设置有个升级版本的功能。现在项目中碰到一个问题就是,复制记录的时候要求把四个标准字段,就是创建者(CreatedBy),创建时间(CreatedOn),修改者(ModifiedBy),修改时间(ModifiedOn) 四个标准字段也复制出来,这个怎么办呢?能否利用overriddencreatedon,createdonbehalfby,modifiedonbehalfby 这几个可能覆盖标准字段的值来做?没有可能覆盖modifiedby字段值得字段怎么办?这就是本博文要讲述的如何克服困难也要上的方法。
大家知道这创建者(CreatedBy),创建时间(CreatedOn),修改者(ModifiedBy),修改时间(ModifiedOn) 这四个标准字段是由CRM自己写入的,我们创建记录或者更新记录都不会直接给这几个字段赋值,而且就算你赋值了,也会没有效果。那怎么办呢?我还是那句话,创造条件也要上。我这里以复制注释(Annotation实体)为例,这个实体有个特殊的地方就是不能够新增字段,所以还涉及到如何将现在记录的标准字段信息传递到复制产生的记录的问题。为了方便演示,我这里利用客户实体的一条记录,并且创建了一个注释内容如下:这里得表扬下微软Dynamics CRM 2013以来可以在页面的标题显示一个图片,你看我这里就将我家特产店的Logo放上去了。
附带提一下,注释显示的是用户名称及时间,应该是这个注释的修改者和修改时间,不是记录的负责人及记录创建时间,这个最好记住。我们来稍微证实下,我用crmadmin账号修改下,然后可以看到的用户名称变成了我刚才修改记录的用户crmadmin,时间变成了我修改记录的时间:
为了好演示,我还是用销售人员一这个账号将其再修改一下,我自制的注释显示页面(请参考我的博文自定义一个注释显示页面:在Dynamics CRM中自定义一个通用的查看编辑注释页面 )可以清楚的看到这个注释如下:
下面我用代码来复制下这条记录。首先我需要为注释实体创建一个插件,注册在Create消息的Pre阶段,使用的代码如下:注意,我使用了一个标准字段importsequencenumber来标记这是一个因为复制而产生的记录,在实际的其它情况下,可能会新增一个自定义字段来判断。
protected void ExecutePreAnnotationCreate(LocalPluginContext localContext) { if (localContext == null) { throw new ArgumentNullException("localContext"); } // TODO: Implement your custom Plug-in business logic. IPluginExecutionContext context = localContext.PluginExecutionContext; if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) { Entity currentEntity = (Entity)context.InputParameters["Target"]; //我用这个字段来判断是不是复制注释,如果包含值就认为是复制记录。 if (currentEntity.Contains("importsequencenumber")) { var ticksDiff = currentEntity.GetAttributeValue<int>("importsequencenumber"); //这个时间要和复制记录时候用来做时间差别计算的时间一样,我这里以2014年1月1日为例 var referenceDate = new DateTime(2014, 1, 1); //切记要把ticksDiff变量先转换为long类型,否则的话,乘以一百万会变成负数,因为相乘以后超过了int变量类型所能容纳的数字大小 currentEntity["modifiedon"] = new DateTime(Convert.ToInt64(ticksDiff) * 1000000 + referenceDate.Ticks); //修改者的ID我用主键字段annotationid传递过来的,因为注释实体没有办法增加字段,只有利用现有字段 currentEntity["modifiedby"] = new EntityReference("systemuser", currentEntity.GetAttributeValue<Guid>("annotationid")); currentEntity["createdby"] = new EntityReference("systemuser", Guid.ParseExact(currentEntity.GetAttributeValue<string>("stepid"), "N")); //这里为主键annotationid设置成一个新的Guid,不能设置为固定值或者空置null currentEntity["annotationid"] = Guid.NewGuid(); //因为不是导入记录,最好还是将这个字段设置为空值null currentEntity["importsequencenumber"] = null; //注意stepid字段的值要清空,不清空的话在标准的注释界面会看不到这条注释,囧 currentEntity["stepid"] = null; } } }
我使用如下的代码来测试,复制创建记录的特别之处是字段annotationid 、importsequencenumber 和 stepid 字段被特别赋值,我分别用来传递被复制记录的 修改者、修改时间 和 创建者。对于标准的CreatedOn 使用overriddencreatedon字段来处理即可,但是标准的CreatedBy字段的赋值使用字段createdonbehalfby 则不起作用,所以我用了stepid来传递被复制记录的stepid字段的值。还有标准的ModifiedBy字段使用标准的modifiedonbehalfby字段来赋值也不起作用,囧。
static void Main(string[] args) { try { var service = GetOrganizationService(); var annotationId = Guid.Parse("d8d1ead8-9686-e511-8101-000d3a805da0"); var tempDate = new DateTime(2014, 1, 1); var item = service.Retrieve("annotation", annotationId, new ColumnSet("annotationid", "subject", "notetext", "filename", "documentbody", "mimetype", "isdocument", "objectid", "createdon", "createdby", "modifiedby", "modifiedon", "filesize", "ownerid")); var annotationEntity = new Entity("annotation"); if (item.Contains("subject")) { annotationEntity["subject"] = item.GetAttributeValue<string>("subject"); } if (item.Contains("notetext")) { annotationEntity["notetext"] = item.GetAttributeValue<string>("notetext"); } if (item.Contains("isdocument") && item.GetAttributeValue<Boolean>("isdocument")) { annotationEntity["isdocument"] = true; if (item.Contains("filename")) { annotationEntity["filename"] = item["filename"]; } if (item.Contains("documentbody")) { annotationEntity["documentbody"] = item["documentbody"]; } if (item.Contains("mimetype")) { annotationEntity["mimetype"] = item["mimetype"]; } if (item.Contains("filesize")) { annotationEntity["filesize"] = item["filesize"]; } } annotationEntity["ownerid"] = item["ownerid"]; annotationEntity["overriddencreatedon"] = item["createdon"];//创建时间使用overriddencreatedon字段覆盖 annotationEntity["createdonbehalfby"] = item["createdby"];//创建者使用createdonbehalfby字段覆盖,奇怪的是对于注释实体没有效果 annotationEntity["stepid"] = item.GetAttributeValue<EntityReference>("createdby").Id.ToString("N");//stepid字段用来传递被复制记录的createdby字段的值 var modifiedOn = item.GetAttributeValue<DateTime>("modifiedon").ToLocalTime();//记得要转换为本地时间 var tempTicks = (modifiedOn.Ticks - tempDate.Ticks) / 1000000; annotationEntity["importsequencenumber"] = (int)tempTicks;//利用importsequencenumber来传递修改时间 annotationEntity["annotationid"] = item.GetAttributeValue<EntityReference>("modifiedby").Id;//利用annotationid字段来传递修改者 annotationEntity["objectid"] = item["objectid"]; service.Create(annotationEntity); Console.WriteLine("程序运行完成!"); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine("程序运行遇到异常." + ex.Message); Console.ReadKey(); } }
我们来看看效果,如下复制效果:
如果通过我的自定义显示附件页面来看,会看的更加清楚点:
到 Annotation 视图里面来看,除了理所当然会不同的主键AnnotationId 和VersionNumber 不一样以外,会发现复制产生的记录的字段 overriddencreatedon 字段有值,记录的是记录真正创建时间的UTC时间。
下面我再稍微测试下,创建记录的时候指定三个可能能改写标准字段值的三个字段,代码如下:
static void Main(string[] args) { try { var service = GetOrganizationService(); var testEntity = new Entity("new_test"); testEntity["new_name"] = "测试复制记录"; testEntity["overriddencreatedon"] = DateTime.Today; testEntity["createdonbehalfby"] = new EntityReference("systemuser", new Guid("746BF37B-7C4F-E511-80D2-000D3A802FAC")); testEntity["modifiedonbehalfby"] = new EntityReference("systemuser", new Guid("746BF37B-7C4F-E511-80D2-000D3A802FAC")); service.Create(testEntity); Console.WriteLine("程序运行完成!"); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine("程序运行遇到异常." + ex.Message); Console.ReadKey(); } }
测试结果也表明只有指定的overriddencreatedon字段的值会复制到createdon字段,其余两个字段就算指定也没有用处,应该属于废弃字段之列。而overriddencreatedon的值依然是忠实记录创建记录的时间。
select createdby,createdonbehalfby,createdon,overriddencreatedon,ModifiedBy,ModifiedOn,modifiedonbehalfby
from new_test where new_name= N'测试复制记录'
如果我导入记录的时候选择了另外一个用户呢?如下图:
在界面上可以看到的是负责人的确变成了导入时候指定的人,其余四个标准字段还是操作者和操作时间的信息。
从数据库里面的记录来看,导入的记录会自动写入字段importsequencenumber值,应该可以用这个来判断是否导入的记录。