CRM 学习笔记(一)

一、插件

1.插件

(1)Fetch查找如果查找条件有int类型,其值也需要加上单引号‘’,不过对于ID来说,大括号倒是不影响。

(2)当有的插件逻辑涉及到了不让用户执行但是其他插件触发可以执行,那么可以考虑下使用Depth。

(3)对于一个实体来说,可以有许多一对多,多对多关系,但是只能有一个“父”(父类被分派,子类也被分派;父类被删除,子类也被删除)。若非父子关系,那么删除其中一个,另一个的查找字段上会自动变为空(这时会触发另一个实体上的updata操作);

(4)谨记!当数据库中对应字段为空的时候,就算你在字符串中指明要提取该字段,那返回的值里面也没有这个字段的Key值;

(5)从Entity中取出属性时,若想将其与其他Entity的属性对比,不可以直接==,需要先强制转换;

(6)针对从Entity中取出EntityReference的字段数据,必须用其ID做比较才准确,否则全都是不等于;

(7)记录创建的时候负责人默认是当前用户;

(8)系统中的浮点数默认的取出来的值为double类型而不是float类型;

(9)原来高级查询是可以直接查询拥有某个子表数据的某个主表记录的,就比如电脑——电脑软件——操作系统,可以直接通过“相关”一级一级关联下来选择到操作系统上,然后用“操作系统”这个属性选择具体记录,得到的查询结果就是这条记录的电脑;相应的也可以从小到大关联,比如操作系统中“相关”选择电脑软件,再相关选择电脑,就能搜索到某个电脑下的所有操作系统;

(10)fullname也是由lastname和firstname组成的;

(11)注册的插件反映在系统中就是SDK消息处理步骤,所以有时候看到插件不起作用,在plugin注册软件里面也一切正常,可以去这个里面看看消息步骤有没有被激活;

(12)某个记录在被分派的时候相当于改负责人字段,所以会触发前台的OnSave事件和后台的Update操作;

(13)+= 与 三元运算符连用的时候后者必须加括号;

(14)创建和删除的后期事件如果执行搜索是可以搜索到当前正在操作的记录的,但是要注意删除的后期事件中没有target(Entity),只有targetref(Entity Reference);

(15)有时候如果开了PluginRegistration的调试功能你又不知道的话,前台可能会报“无法获取前期/后期镜像”的错(当然前提是你有相应的throw语句,判断有没有前后期镜像的那种);

(16)创建产品时报了个错"The unit schedule id is missing",原因是有两个字段不能为空:

1.计价单位组:defaultuomscheduleid  对应实体:uomschedule

2.默认计价单位:defaultuomid  对应实体:uom

分别为这两个字段赋值之后,执行创建成功。

(17)同步插件有错的话会直接弹出弹框,但是异步插件不会,因为不在一个线程上;

(18)在一个实体的后期事件中再次触发同一插件不会陷入死循环;

(19)前期事件中,从上下文中取出来的target若想发生改变需要再回写到context里;

(20)用Double给Money类型的字段赋值,不会报错,但是字段上的值会存空,当想要修改该值时,会报错,而且就单纯地报一个“发成了错误”;

2.Action

(1)Action公用的函数可以抽出来写一个类不必非要再次写一个Action,只需要把service当作参数传递进来就好;

(2)Dictionary的应用于HashMap类似,但是前者是泛型的后者是非泛型的,按照我的理解就是按照Key取Value的时候,前者能给按照Value本身的属性给你,而后者只能给你个Object的类型。有文档说如果数据量比较大(千万级别)的时候后者比前者要快;

(3)想要在C#中调用Action,需要使用OrganizationRequest这个类,这个类是所有请求类的基类,基本上能实现所有对系统资源的请求,用法如下;

OrganizationRequest req = new OrganizationRequest("Action的名字");

OrganizationResponse response = service.Execute(req);

(4)Dictionary可以用foreach(KeyValuePare<key, value> in Dictionary)来遍历操作;

(5)(插件消息步骤)迁移解决方案时,最好先迁移实体,再迁移程序集和流程,最后迁移SDK消息步骤和Action;

(6)分配权限GrantAccessRequest/ModifyAccessRequest/RevokeAccessRequest(Revoke不存在的权限不报错,但Revoke前需要判断:Revokee不是Target的Owner,否则会报错:owner can revoke access to the owner);

二、脚本

1.按钮

(1)2016版的RibbonWorkbench似乎不给display规则就不会显示;

(2)在RibbonWorkbench中,每个实体对应的子网格编辑界面设置的不是这个实体下的子网格,而是说这个实体作为子网格添加到别的实体上的设置;

(3)在子网格的系统按钮上添加新规则,需要在系统按钮上右键Costumise Command才会出来原本的Command,否则直接Costumise Button按钮的Command栏只会为空,然后附上自己的Command原来的Command就直接被覆盖了;

原本规则被覆盖后,会发现再次登陆该解决方案的RibbonWorkbench时右键其与自定义的按钮无异,这时候不要多想,你只有俩选择,一个是删了实体重建,另一个就是对照其他实体的子网格中配置的Command规则手动配置一遍;

虽然可以配置,但是在某些时候你发现发布自己手动配置的“系统”Command到那些被玩坏的系统按钮上时发布不了,会提示报错说这些系统按钮还在引用你原来配置给他们的Command(我当时发现不对就直接删了)。解决方法是:先在被配置失败的那些按钮上右键同样Customise Command(然后啥变化也没有,但是个人感觉需要这么一步),然后建立一个跟原先一样名字的Command(就是你用来覆盖系统规则的那个Command)然后再赋给那些配置失败的按钮,发布一遍后再用你手动配置好的系统Command发布就可以了;

(4)想要控制子网格上面的“+”按钮的显示与隐藏,别到那个按钮上配置,去视图中的那几个添加按钮配置效果,会自动同步到“+”按钮上,“+”按钮本质上来说也是添加新建和添加现有;

(5)有时候网页各个文件的加载速度也要考虑进去:如果A文件调用了B文件的方法但是加载的时候先加载了A再加载了B就很有可能报错,虽然第一次加载后会有缓存来解决这个问题,但是对于用户体验来说也不是很友好。

2.窗体控制

(1)针对页面上元素的控制:用Xrm.Page.data.entity.attributes只是控制一些有数据的字段的显示与隐藏,但是碰到某些比较特殊的就不行了(比如说在循环的时候获取所有然后全部锁定),但是用Xrm.Page.ui.controls就可以得到包括选项卡、窗体、子网格在内的所有窗体上元素的控制权限(显示隐藏、可用性等等);

(2)选项集类型的字段如果想只显示某些选项的值,Xrm.Page里可以进行控制:原文链接

(3)阻止保存事件不能用return 要用上下文里的context.getEventArgs().preventDefault();

(4)网页JS Debug中也能用控制台查询参数,包括运行时的临时参数;

(5)如果在JS脚本上设置了,OnChange互相触发的操作,那么在保存时会保存两次,因为第一次保存时OnChange触发另一个OnChange被视为一次改动(多次触发也只多保存一次);

(6)当需要更改某一个字段之后锁定界面时,可以在OnSave中写上和OnLoad同样的判断,某字段等于某值时,锁定界面,不用非要刷新;

(7)有时候业务人员会把一些状态字段放在标题上,这个时候虽然在页面上去其逻辑名都会加上"header_",但实际上并没有。同样的,在一个窗体上有多个同一个字段,页面上(div)除了第一个添加的字段其他的会在显示其名字后面加了一个数字(比如:name2),这种时候就只能回窗体属性里面找他的名字了;

(8)若是需求要求需要隐藏某些字段或选项卡,那么隐藏的字段或者选项卡上的字段也必须也全部设置为非必填。另外设置是否必填set的括号里记得敲双引号,别只敲noneu或者required;

(9)Xrm.Page.ui.tabs.get("tab_3").sections.get("tab_3_section_3").setVisible(true);//隐藏节

(10)Xrm.Page.getAttribute("new_stockdate").getValue().toISOString(),转换成标准的ISO格式可以用来对比时间;

3.数据查询

(1)hdREST可以使用hdREST.get()方法直接查询数据,但是需要手动拼上“实体名+实体ID+查询条件”(OData);

(2)用Odata查询某条记录的lookup的值,返回的Json里面有lookup的Id和text,ID可以直接用键值取出来,但是text需要用JSON.parse(dataresult.responseText)["_vymp_countryid_value@OData.Community.Display.V1.FormattedValue"]来取值;

(3)CRM 2016中OData被封装成了WebAPI;

(4)用OData查询数据时,如果单纯的用ID查询一条记录和用条件查询记录返回的值responseText是不一样的,前者可以直接取值,后者则需要再深入到value中然后按照集合(value[i])的形式取值;

(5)对比两个字符串(尤其是ID)时,要记得都转换成小写或大写字母,在前端的时候还要记得去掉有时候自带的大括号"{}";

(6)JS获取某个查询结果中没有的属性时,会显示undefined;

(7)在如果想要在JS端传递一个Json给Action让其能被当作Dictionary被反序列化出来,那需要new一个Object,然后用Object[Key] = Value的方法来创建一个对象(然后序列化它);

(8)在使用Odata查询时,若想以某个查找字段"XXX"为条件,则需要在其逻辑字段名前加"_"、后加"_value"即"_XXX_value",然后附上查找的ID号(不用加单引号);

(9)使用Odata查询的时候,如果查询条件里面包含日期,那么日期的格式需满足"2018-12-05T16:00:00.000Z"(ISO格式,js中使用.toISOString()来转换),且使用的是"eq"而不是"on";

(10)窗体属性里面如果有两个同名对象,那么谁先加载完成就用谁;

(11)XmlHttpRequest和ajex都可以设置同步还是异步的方式;

(12)XmlHttpRequest中有两个状态:一是readyState,二是status,前者是整个请求的状态,后者是大的state下面具体的小的状态:原文链接

(13)XmlHttpRequest的onreadystatechange方法是请求后发生的,所以除非有专门的回调函数,否则无法获取方法里面返回的值(不管是同步还是异步),但其实可以不用写在里面,直接在send后面取request里面的内容也可以;

三、接口

1.控制台

(1)写控制台时有错误也不能抛出,但是当遇到重要节点数据无法获取的错误时,可以在try里面抛出后在catch中接到写日志,然后剩下的也不用执行了;

(2)当数据被批量导入的时候,数据的创建执行插件并不会太消耗性能,但是如果插件需要访问外部请求(异步),那就会直接卡死(请求被挂起);

(3)当批量创建或者更新数据导致短时间内不断触发异步插件不会导致卡死;

(4)同步插件有错的话会直接弹出弹框,但是异步插件不会,因为不在一个线程上;

(5)读取config文件的方法:ConfigurationManager.AppSettings["XXX"];

(6)写一个不停运行的控制台,循环访问组织服务或者SQL时需要sleep,否则太快了不停的请求SQL连接会是对资源的极大浪费;

2.接口

接口:接口就是两个系统之间数据互通的一种方法,所以从结构上来看可以分为(对方)数据提供、(自己)数据获取和(自己)数据存储。

1.提供:接口的提供方通常有两种提供数据的方式:

一是中间表,中间表就是提供方将其想要提供的数据放到一个中间的数据库中,这个中间的数据库的数据可以被多个其他系统调用和获取(但是没法增删改),至于获得的数据怎么处理那就是调用接口的系统的事了,这样的好处是兼容性高,但是数据不是实时的,且需要每隔一段时间更新一次。

二是web服务,web服务则是提供方自己封装一个方法来进行某种特定的数据操作,这种操作是直接作用在其自己的数据库上的,优点是数据是实时的,缺点是需要根据调用方需要的功能来开发相应的web服务(开发成本高)

2.获取:数据的获取通常也有两种方式:

一是abo.net。这种方式是绕过系统直接对底层的数据库进行操作,对于CRM系统而言,就是绕过了service,所以相应的一些插件什么的都没有办法触发(也就不能触发一些业务处理的逻辑),但是好处是速度快,绕开了那些杂七杂八的步骤,但是坏处也是因为绕开了那些步骤所以数据不安全,因此对于一些系统的标准实体一般不让用直接插入。

二是service。这种方式就是走系统封装好的方法来存储数据,数据安全固然能够保证,但是相应的速度就慢很多了。虽然速度慢,但是这也是对于系统标准实体插入的最好方法。

3.存储:存储严格来讲也可以算在获取里面,也分为两种方式:

一是有备用字段。获取的数据因为系统的差异通常是没办法直接用的,比如某个查找字段在CRM中存储的是GUID,但是在SAP中传过来的则是名称,因此就需要处理,这种时候有备用字段就会先直接把名称存在备用字段上,然后后期再查找CRM的表翻译过来。

二是无备用字段。无备用字段就不储存无法直接储存的属性,因此就需要在接受的时候就对这些属性进行处理(翻译),或者是抛出错误,或者是暂时记录下来,但是很难就存储在CRM的数据库里了。

(1)WebService部署成站点之后,如果在本地测试webservice可以运行,在远程却显示“测试窗体只能用于来自本地计算机的请求”或者"The test form is only available for requests from the local machine. ",那是因为没有开启远程访问的原因。

解决:

在web.config的<system.web></system.web>中间加入如下配置节内容:

<webServices>
    <protocols>
        <add name="HttpSoap"/>
        <add name="HttpPost"/>
        <add name="HttpGet"/>
        <add name="Documentation"/>
    </protocols>
</webServices>

原文链接:解决WebService 测试窗体只能用于来自本地计算机的请求 - 冬雨在路上 - 博客园

(2)当需要对多层次的数据进行整理汇总时,可以使用Dictionary进行,一层次对应一个Dictionary,核心逻辑:以某一层次的主键为Key,没有就添加,若有则深入到其下层去寻找是否存在相应的下一层。(注意:添加时使用dic["XX"] = XX ,而别用dic.Add(),因为如果已经有了Key会报错);

3.SQL语句

(1)SQL语句如果一个表关联的外键还是自己,做连接查询的时候可以不用顾忌,因为虽然查找的源头是一样的,但是走的是两条查找路线。但是需要注意的一点是需要搞清楚属性的一对多关系,否可能会报错“子查询返回多个结果”;

(2)批量插入可以是这么个写法:insert into XXX库名1(xxx属性名1,xxx属性名2)select xxx属性名1, xxx方法名1 from XXX库名2 where XXX条件;

(3)A left join B 是指在A的基础上进行B查找;

(4)获取行号:

select row_number()over(order by userid )as RowNum,*from OUM_User;

(5)将一个数字例如33,或1使用t-sql语句转换成033或001

以下是详细分析:

select power(10,3)--得到1000
select cast(1000+33 as varchar) --将1000转换类型
select right(100033,3) --从右边取3个字符得到033

最后结果是:

select right(cast(power(10,3) as varchar)+33,3)

原文链接:https://blog.csdn.net/finish_dream/article/details/76906596

(6)根据我的理解,左连接(A left join B on 条件A where 条件B)其实就是给表A附加上表B上的某些属性(列),在这个联系的过程中使用 on来作为连接的条件,where 作为A表的筛选条件;

(7)刷数据的时候如果想要自动序号可以使用行号;

(8)SQl语句中"!="是真的不等于,但是"not in"才是真的不包含;

(9)数据库连接两种方式,一种是连接不中断的,随时访问;另一种是将结果存储到本地缓存,运行后断开连接;

四、杂项

1.代码技巧

(1)导入导出解决方案后,ID可能会变,所以一般不要用常量ID作为判断标准;

(2)别管管理员可能会怎么瞎改,他没法瞎改才叫出事;

(3)如果同一段代码移植到别的项目中就报错了,那么记得先去看看有什么必要的配置没一块copy过来。就比如CrmHelp,需要在app,config里面把地址用户名密码等一些配置一块挪过来;

(4)控制台程序本身的登陆账户是写死了的,因此我们通常是用管理员账户去登陆,然后赋予其某个用户ID以模拟该用户能看到的数据。所以通常如果用控制台去汇总某个安全角色的某个数据,那么一般是先找到所有的该安全角色的用户ID,然后用foreach依次去汇总。而获取某安全角色的所有用户的方法,一是可以直接到数据库中在Role表查询出安全角色名称对应的所有ID(因为部门的关系,父部门创建的安全角色子部门也会自动生成),然后在SystemUserRoles表中找出对应的所有用户ID;二是直接在用户实体上用fetch查询,查询的时候用“安全角色”的“名称”(若是用“安全角色”则只会筛选出选中的安全角色的用户)就能查出所有的该安全角色的用户;

(5)异步执行的任务会反映在“系统作业”;

(6)无法直接在系统数据库中建表,因为对前端不可见,但是可以通过excel导入创建(目的是为了刷数据);

(7)continue不可以乱用,至少在你想要跳过某些数据的时候要写日志,否则最后排查起来也很困难;

(8)Ctrl+Shift+F是全局搜索(无效就按Shift切换);

2.业务操作

(1)字段设置为"可搜索"才能在视图中被找到;

(2)此条不可见。

(3)产品化的审批流导入到系统中,再加入到某个解决方案中用Ribbon Workbench打开会导致一些原本存在的东西自动消失。目前已经发现的有:js脚本的注册、Onload事件的注册;

(4)9999不是多行文本的最大长度;

3.工具

(1)XrmToolBox的插件显示不了:

先把显示隐藏文件勾上。

然后到下面这个位置把所有文件都备份到其他位置,然后删除这个目录下面的所有文件:C:\Users\你的用户\AppData\Roaming\MscrmTools\XrmToolBox

最后再双击你的xrmtoolbox,就正常了。

因为这个工具多个版本的的话,都会生成文件放到这个位置,dll会冲突导致程序运行异常;

(2)Chrome的A页面处于调试状态时,另外一个B页面也刷不出来;

(3)TFS如果出现“管理员取消数据库操作”,那是因为服务器内存紧张,这种时候就去找大佬问问吧。

(4)在服务器上调试的时候可以不用RibbonWorkbench(注册是必须要再这个软件上注册),调试的方法是:调试——附加到进程——w3wp.exe(这里注意,有三个同名的进程,一般注意后面的用户,附加到部署服务器的那个用户的进程上(NETWORK SERVICE),然后就可以调试了)。需要注意的是,注册的dll需要是在服务器上生成的;

(5)可以使用CRM REST Builder生成Odata查询语句,简单快捷省时间;

(6)Indent Guides C#垂直虚线插件;C# outline 2015 大括号(if for)收缩打开插件;

PS:本篇文章仅为学习笔记的整理,如有疑问,欢迎留言;如有错误,欢迎指正。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值