Microsoft Dynamics CRM - Jim Wang's technical blog [MVP]

Welcome to my blog: http://mscrm.cn [Chinese] & http://jianwang.blogspot.com [English]

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  16 随笔 :: 48 文章 :: 96 评论 :: 5 引用

2009年11月8日 #

     摘要: CRM 4.0 标准的N:N Lookup 视图里,被选择的对象只显示Primary Attribute(主属性),比如下图:这样的Lookup视图在很多情况下并不能满足对被选数据的过滤筛选。我做了一个改进,可以用标准的实体视图(比如 Active View, Lookup View, etc.)代替上面左边的视图,见下图:这个方法包含了2个实现部分:1. Lookup View:这是一个ASPX...  阅读全文
posted @ 2009-11-08 13:13 Jim Wang 阅读(337) | 评论 (2)编辑

2009年9月13日 #

这篇文章里我谈一下大多数开发者会使用的典型开发环境:在自己的机器上安装VPC,然后安装OS+AD+SQL+CRM+SharePoint… 总之是一个全面、独立的开发测试环境,然后在主机上安装Visual Studio。如果你也是用这样的环境作开发,我总结了一些方法供你参考,可以提高开发速度: 
  • 关于网卡的设置,我推荐使用3块网卡:一个做VPC内部使用;一个连接Internet(通过主机);一个使用Microsoft Loopback adapter。为什么使用Loopback 呢?因为它可以提供主机和VPC之间不间断的通信,而且你可以在主机的HOST(C:\WINDOWS\system32\drivers\etc\hosts)里面直接指明VPC的Lookback IP地址,而不用经常更改:比如可以设定成 R2     192.168.2.2,这样R2在你机器里的指向就是VPC的地址:192.168.2.2,而主机的Loopback IP当然可以设定成 192.168.2.1。当然连接Internet的第二块网卡也可以用于和VPC通信,但是这个IP可能会经常变动或者有些时候不可用,所以不方便。
  • 既然是CRM开发,你也可以把你的VPC里配置成On-Premise/IFD的访问方式,这样一来你可以在主机上测试你的代码是否也工作在IFD的部署模式下。
  • 关于Plug-in的调试问题参考SDK: http://msdn.microsoft.com/en-us/library/cc151088.aspx ; 关于Workflow Activity的调试问题参考SDK:http://msdn.microsoft.com/en-us/library/cc151143.aspx, 我这里说一下通过主机的Visual Studio来实现远程调试的技巧: 
    1. 由于VS安装在主机上,所以这种调试也算是一种远程调试了。方法是在VPC里运行VS的远程调试程序(只是一个.exe文件,可以从主机里拷贝到VPC) :Visual Studio Remote Debugging Monitor(msvsmon.exe)。
    2. 你需要在主机上保存一个用户名/密码,这样方便访问VPC。方法是在控制面板>>用户帐号>>高级>>管理密码,在这里填上VPC的服务器名,比如R2;用户名,比如Administrator和密码。
    3. 运行这个程序的用户名/密码必须要和主机上运行VS的用户名/密码相一致才可以,比如VPC里是 WIN2K3\Administrator ,密码 Password1 那么在主机上运行VS的用户名必须是Administrator,密码也必须是 Password1 域名不一致没有关系!
    4. 要调试Plug-in,你的dll文件必须部署到VPC里的文件目录下,比如C:\Program Files\Microsoft Dynamics CRM\Server\bin\assembly\ , 注册dll也要注册到文件目录,而不是数据库。
    5. 你可能发现部署的时候提示dll正在使用,除了手工在VPC上运行iisreset之类的回收功能;你还可以在VPC里设置部署前运行一个脚本,这个脚本的功能是回收VPC上CRMAppPool,然后部署就可以正常完成了。如果你主机上没有安装IIS,我这里提供一个PowerShell的脚本,供你调用:
$server="R2";

$co = new-object System.Management.ConnectionOptions;
$co.Authentication=[System.Management.AuthenticationLevel]::PacketPrivacy;
$co.EnablePrivileges=$true

$wmi = [WmiSearcher] "Select * From IIsApplicationPool";
$wmi.Scope.Path = """$server"root"microsoftiisv2";
$wmi.Scope.Options=$co

foreach($crmpool in $wmi.Get())
{
  
if($crmpool.name -eq "W3SVC/AppPools/CRMAppPool")
  {
    
$crmpool.recycle();
  }
}


如果是部署Workflow Activity,还需要重启MSCRMAsyncService服务,参考下图调用:


6. 部署/注册Plug-in/Workflow Activity之后,现在VPC上运行msvsmon.exe,然后在主机VS里选择"Debug">>"Attach to process...",在Qualifier里填入VPC的信息,比如: WIN2K3\Administrator@R2 ,然后Attach到VPC的w3wp.exe进程(Plug-Ins 调试);或者CrmAsyncService.exe进程(Workflow Activity调试),参考下图:


posted @ 2009-09-13 13:29 Jim Wang 阅读(197) | 评论 (0)编辑

2009年8月28日 #

我近期在微软英国参加了为期2Sure Step 的进阶讲座。之前介绍过Sure Step V1 是专门针对微软商务解决方案产品(Dynamics)的项目管理系统,它提供了一套项目管理方案和模版,大大缩减了项目管理的时间。Sure Step V2现在已经面向所有Microsoft Dynamics Partners下载了,遗憾的是目前还没有中文版本。

 

令人兴奋的是Sure Step V3将在今年年底发布,到时候可能会整合到SharePoint里面,而不是像现在这样单独的程序了。

posted @ 2009-08-28 10:22 Jim Wang 阅读(185) | 评论 (1)编辑

2009年8月9日 #

CRM 4.0Lookup的过滤功能是个缺憾,大多是收费的解决方案。网友韩建兴提出一个很普遍的问题

 

我有一個實體ShippingMark

1.它和AccountN:1

2.它和QuoteN:N

我的需求是Quote在新增已存在的ShiningMark時,

只顯示ShippingMark.AccountId=Quote.AccountId的資料,有什麽辦法嗎?

 

我之前写过三篇博文,是总结性的给出LookupSingle的过滤方案,在论坛里普遍被使用:

神秘的 CRM Lookup (I)

神秘的 CRM Lookup (II)

神秘的 CRM Lookup (III)

 

但并没有涉及到LookupMulti的问题,而这两者有很大的不同。

 

其实MVP Darren、刀客都有提出过利用PluginExecute方法来实现过滤的方案;Stunnware也有一个收费的解决方案。

我针对网友的需求给出代码,基本思想是Plugin代码通过JScript传送的参数来实现过滤的功能,这个方法的好处是不更改任何系统文件+免费。你可以根据你的需求来更改代码。

JScript部分直接调用了CRM的内部函数,所以此定制应该被视为unsupported customization(虽然是安全的).

 

第一部分,Plugin内容,需要注册成 Execute message on the Pre Stage/Synchronous/Server/Parent Pipeline.

/*
 * Microsoft Dynamics CRM Lookup Filter 
 * Plug-Ins: Execute message on the Pre Stage/Synchronous/Server/Parent Pipeline.
 * Jim Wang @ Aug 2009, 
http://jianwang.blogspot.comhttp://mscrm.cn
 * 
 
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using Microsoft.Crm.Sdk;

namespace CRMExecuteEvent
{
    
public class CRMExecuteEvent : IPlugin
    {     
        
string lookupId;

        
public void Execute(IPluginExecutionContext context)
        {

            lookupId 
= HttpContext.Current.Request.QueryString["id"== null ? null : HttpContext.Current.Request.QueryString["id"].ToString();

            
if (lookupId == null)   return;

            
try
            {
                
if (context.InputParameters.Contains("FetchXml"))
                {
                    
string beforeXml = (String)context.InputParameters["FetchXml"];

                    
if (beforeXml.Contains("<entity name=\"new_shippingmark\">"&& beforeXml.Contains("xml-platform"))
                    {
                        
//Customise the FetchXml query string
                        string afterXml =
                        
"<fetch version='1.0' page='1' count='100' output-format='xml-platform' mapping='logical'> " +
                            
"<entity name='new_shippingmark'> " +
                                
"<attribute name='new_shippingmarkid' /> " +
                                
"<attribute name='new_name' /> " +
                                
"<attribute name='createdon' /> " +
                                
"<order attribute='new_name' /> " +
                                
"<link-entity name='quote' to='new_accountid' from='customerid'> " +
                                    
"<filter type='and'> " +
                                        
"<condition attribute = 'customerid' operator='eq' value='" + lookupId + "'/> " +
                                    
"</filter> " +
                                
"</link-entity> " +
                                
"<filter type='and'> " +
                                    
"<condition attribute='statecode' operator='eq' value='0' /> " +
                                    
"<condition attribute='new_name' operator='like' value='%' /> " +
                                
"</filter> " +
                            
"</entity> " +
                        
"</fetch>";

                        
//Replace the FetchXml query string
                        context.InputParameters["FetchXml"= beforeXml.Replace(beforeXml, afterXml);

                    }
                }
            }

            
catch (System.Web.Services.Protocols.SoapException ex)
            {
                
throw new InvalidPluginExecutionException("An error occurred in the CRM plug-in.", ex);
            }
        }

    }
}


第二部分,JScript的内容,放在QuoteOnload里面,你需要修改relId


var relId = "new_new_shippingmark_quote";
var lookupId = crmForm.all.customerid;  

var lookupEntityTypeCode;
var navId = document.getElementById("nav" + relId);
if (navId != null)
{
    
var la = navId.onclick.toString();
    la 
= la.substring(la.indexOf("loadArea"), la.indexOf(";"));
    
    navId.onclick 
= function()
    {
        eval(la);

        
var areaId = document.getElementById("area" + relId + "Frame");
        
if(areaId != null)
        {
            areaId.onreadystatechange 
= function() 
            {
                
if (areaId.readyState == "complete"
                {  
                    
var frame = frames[window.event.srcElement.id];  
                    
var li = frame.document.getElementsByTagName("li");    

                    
for (var i = 0; i < li.length; i++)
                    {
                        
var action = li[i].getAttribute("action");
                        
if(action != null && action.indexOf(relId) > 1)
                        {
                            lookupEntityTypeCode 
= action.substring(action.indexOf("\(")+1, action.indexOf(","));
                            li[i].onclick 
= CustomLookup;
                            
break;
                        }
                    }
                }
            }
        }
    }
}

function CustomLookup()
{
    
var lookupSrc = "/" + ORG_UNIQUE_NAME + "/_controls/lookup/lookupmulti.aspx?class=&objecttypes=" + lookupEntityTypeCode + "&browse=0";
    
if(lookupId != null && lookupId.DataValue != null && lookupId.DataValue[0!= null)
    {
        lookupSrc 
= lookupSrc + "&id=" + lookupId.DataValue[0].id;
    }

    
var lookupItems = window.showModalDialog(lookupSrc, null);
    
if (lookupItems)
    {
        
if ( lookupItems.items.length > 0 )
        {
            AssociateObjects( crmFormSubmit.crmFormSubmitObjectType.value, crmFormSubmit.crmFormSubmitId.value, lookupEntityTypeCode, lookupItems, 
truenull, relId);  // This is the CRM internal JS funciton on \_static\_grid\action.js
        }
    }
}

posted @ 2009-08-09 10:33 Jim Wang 阅读(356) | 评论 (0)编辑

2009年7月30日 #

在论坛上看到网友“一千零一夜”提出的一个问题,在CRM窗体上被隐藏的字段却还出现在打印预览里,如果在打印的页面也可以隐藏掉这些字段就比较实际了,而且也符合用户期望。 


CRM 4.0
没有提供基于Field的安全机制,可能是因为会牵扯到性能的问题。不过我已经向微软提了建议,希望在下个版本里有所增强。但是在当前的版本里没有Supported的方法可以实现。我注意到这个打印预览窗体实际上是被窗体上的按钮触发的,所以window.opener 就是这个CRM窗体,这样就可以比较它上面的字段属性了,如果设定成 style.display="none"; 的形式,我们也可以在当前的打印预览窗体里隐藏对应的字段。同样也可以隐藏Tab,不过方法有些不同:因为打印预览里的Tab并没有id,所以根据className等来判断。
 

我提供这个方法可以解决CRM的打印需求,并没有真正实现了 field level security,比如在高级查找里还可以发现所有可搜索字段。 如果你的企业CRM应用有严格的安全考量,这个方法并不适合。

 

你需要修改的文件是  \CRMWeb\_forms\print\print.aspx 把下面的代码帖在 </html> 之上:

<!--
Field level security on Print form
author: Jim Wang @ July 
2009
http:
//jianwang.blogspot.com
http://mscrm.cn
-->

<script language="javascript">
var printFrame = document.getElementById("printMain");
var printWindow = document.frames["printMain"];
printFrame.onreadystatechange 
= function() 
{
    
if(window.opener && printWindow.document.readyState == "complete")
    {
        
//hide attributes
        var allFields = opener.document.getElementsByTagName("TD");
        
for (var i = 0; i < allFields.length; i++)
        {
            
var thisField = allFields[i];
            
if (thisField.style.display == "none")
            {
                printWindow.document.getElementById(thisField.id).style.display 
= "none";
            }
        }
        
        
//hide tabs
        var printTabs = printWindow.document.getElementsByTagName("DIV");
        
var openerTabs = opener.document.getElementsByTagName("LI");
        
for (var i = 0; i < openerTabs.length; i++)
        {
            
var openerTab = openerTabs[i];
            
if (openerTab.className && openerTab.className == "ms-crm-Tab")
            {
                
if(opener.document.getElementById(openerTab.id).style.display == "none")
                
var printTab = printTabs[openerTab.id.replace("tab","").replace("Tab","")];
                printTab.style.display 
= "none";
            }
        }

    }
}
</script>


下图可以看出来:我隐藏了LastName和2个Tab。

posted @ 2009-07-29 20:23 Jim Wang 阅读(201) | 评论 (6)编辑

2009年7月27日 #

     摘要: 这篇文章是Sonoma Partners和Microsoft 员工合作写的一篇文章,我做了摘要翻译,完整文章请见:Setting Up Your Development Environment摘要开发 Microsoft Dynamics CRM 同样使用被大多数微软开发人员所习惯使用的的架构和体系, 你应当在你的组织里搭建这样的开发环境。关于一些深层的东西,请阅读 《Programming Mi...  阅读全文
posted @ 2009-07-27 10:32 Jim Wang 阅读(342) | 评论 (0)编辑

2009年7月20日 #

     摘要: 在 IFrame里显示实体关联视图的技术经常被使用。比如你的自定义实体new_myentity和系统实体account建立了关联,无论是 1:N,还是N:N,下面的代码都可以显示出来关联视图。你唯一要做的是找到navId (可以用IE Developer Toolbar),然后替换下面的"nav_new_new_myentity_account"就可以了。[代码]  阅读全文
posted @ 2009-07-19 21:26 Jim Wang 阅读(253) | 评论 (10)编辑

2009年7月13日 #

     摘要: 微软终于发布了这款手机端的CRM应用程序,Mobile Express 的主要特点:支持On-Premise 和 IFD模式;没有离线数据库,只能在线浏览CRM内容;(非完整)支持任何HTML4.0兼容的手机浏览器,比如Windows Mobile, BlackBerry, Opera, Apple iPhone。很遗憾,目前还没有推出中文版本。安装方法:在CRM Server上运行安装程序,然后...  阅读全文
posted @ 2009-07-12 22:52 Jim Wang 阅读(338) | 评论 (0)编辑

2009年6月6日 #

     摘要: 2009年6月3日,微软和英特尔宣布 CRM 4.0 通过基准测试证明了微软CRM运行在Intel Xeon5500 的服务器上,同时存在5万用户,超过2.9百万日访问量,可以很好的运行。同时还比对了Oracle Siebel在2008年10月份的测试结果:Oracle Siebel 8.0: 14,000 用户/1.6 百万日访问,费用:US$150,000Microsoft Dynamics ...  阅读全文
posted @ 2009-06-06 07:34 Jim Wang 阅读(395) | 评论 (0)编辑

2009年6月2日 #

     摘要: 最近几年,随着对XMLHttpRequest 和DOM的跨浏览器支持成为标准,AJAX的应用越来越普遍。AJAX的全称是Asynchronous JavaScript and XML(异步JavaScript和XML),AJAX不是一项技术,而是一组技术(XMLHttpRequest, DOM, JavaScript)的配合应用,其主要作用是改善用户体验:比如连贯性,无闪烁更新,接口设备,混合应用...  阅读全文
posted @ 2009-06-01 23:16 Jim Wang 阅读(342) | 评论 (0)编辑

2009年5月18日 #

     摘要: 我之前写过一篇文章:怎样在快速查找里搜索Inactive的数据? 论坛里有网友问:怎样在Lookup里显示Inactive的数据?CRM MVP 刀客有一篇文章介绍过一个Plugin的解决方案,给我们开拓了思路。还有一种方法是在OnLoad()里动态的改变Lookupclass类:假设你想Lookup所有的Opportunity,包括活动的和关闭的,你可以用下面的语句来得到:crmForm.all...  阅读全文
posted @ 2009-05-18 15:16 Jim Wang 阅读(156) | 评论 (0)编辑

2009年5月11日 #

     摘要: 这是最后一个CRM Accelerator,目前这个加速器只支持On-Premise部署,它为CRM提供了如下增强的Workflow功能: 获取当前记录的GUID; 生成HTML链接,可链接到CRM数据; 生成HTML链接,可链接到非CRM的任何URL; 生成HTML链接,点击发送邮件; 合并两个字符串; 进行简单的数学运算(加、减、乘、除、取模) 这个加速器是典型的CRM Workflow As...  阅读全文
posted @ 2009-05-10 16:46 Jim Wang 阅读(165) | 评论 (0)编辑

2009年4月28日 #

posted @ 2009-04-28 12:17 Jim Wang 阅读(116) | 评论 (0)编辑

2009年4月22日 #

posted @ 2009-04-22 14:27 Jim Wang 阅读(144) | 评论 (0)编辑

2009年4月15日 #

posted @ 2009-04-14 18:35 Jim Wang 阅读(182) | 评论 (0)编辑

2009年4月6日 #

posted @ 2009-04-05 23:41 Jim Wang 阅读(160) | 评论 (0)编辑

2009年3月29日 #

     摘要: 微软CRM社区新创建了一个中文论坛,欢迎大家光临:中文界面:http://social.microsoft.com/Forums/zh-CN/crmchinese/threads/英文界面:http://social.microsoft.com/Forums/en-US/crmchinese/threads/欢迎您来到微软中文CRM论坛! 本论坛给您介绍微软商务解决方案之一:客户关系管理系统 (M...  阅读全文
posted @ 2009-03-29 12:13 Jim Wang 阅读(487) | 评论 (1)编辑

2009年3月22日 #

posted @ 2009-03-21 22:19 Jim Wang 阅读(171) | 评论 (0)编辑

2009年3月16日 #

posted @ 2009-03-15 21:56 Jim Wang 阅读(178) | 评论 (0)编辑

2009年2月25日 #

posted @ 2009-02-24 17:54 Jim Wang 阅读(252) | 评论 (2)编辑

2009年2月21日 #

posted @ 2009-02-21 15:36 Jim Wang 阅读(308) | 评论 (1)编辑

2009年1月24日 #

     摘要: 根据微软Microsoft CRM Sustained Engineering (SE) team的计划,每8周将推出一个Rollup升级程序,代替了大部分的补丁。CRM 4.0 Update Rollup 1 推出的时间是2008年11月底; CRM 4.0 Update Rollup 2 推出时间是2009年1月中旬;预计Rollup 3 的推出时间将是2009年3月中旬。这是一个很好的方法,...  阅读全文
posted @ 2009-01-24 11:35 Jim Wang 阅读(255) | 评论 (7)编辑

2009年1月17日 #

posted @ 2009-01-16 21:52 Jim Wang 阅读(664) | 评论 (11)编辑

2009年1月6日 #

posted @ 2009-01-06 14:18 Jim Wang| 编辑

2008年12月29日 #

     摘要: 《Programming Microsoft Dynamics CRM 4.0》(微软Dynamics CRM 4.0 编程) 由Sonoma Partners 公司组织编写,这个公司可以说是从事Microsoft CRM 最早期的合作伙伴(开始于CRM 1.0 pre-release beta),公司成立于2001年,坐落在美国芝加哥。在CRM3.0时期,Sonoma Partners(by M...  阅读全文
posted @ 2008-12-29 04:27 Jim Wang 阅读(1095) | 评论 (2)编辑

2008年12月13日 #

posted @ 2008-12-13 14:23 Jim Wang 阅读(232) | 评论 (3)编辑

2008年11月28日 #

posted @ 2008-11-28 12:20 Jim Wang 阅读(385) | 评论 (0)编辑

2008年10月29日 #

posted @ 2008-10-29 10:00 Jim Wang 阅读(323) | 评论 (7)编辑

2008年10月26日 #

posted @ 2008-10-25 17:47 Jim Wang 阅读(541) | 评论 (0)编辑

2008年10月12日 #

     摘要: 我很荣幸获得了Microsoft MVP (微软最有价值专家) 提名感谢微软和所有支持我的同行们,朋友们!  阅读全文
posted @ 2008-10-11 19:37 Jim Wang| 编辑