One of my favorite features of CRM 4.0 is the ability to extend the application using .NET, specifically interacting with other websites through the use of buttons, menu options, and other ISV configuration points. This has allowed me to develop “Wizards” and other pieces of functionality inside of CRM to help users accomplish certain tasks and enter in more accurate information in CRM. There are a lot of posts on the Internet about how to clone records in CRM.
I’ve found a pretty simple way of cloning records as well and it really just requires a few lines of .NET code and an .aspx webpage. Essentially, if you want to clone an exact copy of a record all you need to do is retrieve the Entity from CRM, loop through the properties and remove all references to KeyProperties. Once you remove all the Keys from the Entity you can call the Create method of the web service. It will generate a new key and you have a cloned record in CRM.
In the rest of the post, I will walk through setting up the .aspx web page, the required .NET code and how to tie it into CRM with the ISV.Config file. For another little twist to the project, I used AJAX controls to give a visual indication to the end user that the cloning process is executing.
Step 1: Setup your web project in Visual Studio 2005 or 2008 (this code was written in VS 2005)
In Visual Studio create a new Web Site project. One of the options you will have is to setup an AJAX Enabled Web Site. This ensures that all of the necessary web.config entries are included in your Website in order for the AJAX functionality to work properly.

*If you don’t see this option, then you need to install the AJAX Toolkit for Visual Studio 2005. The following URL provides information on where to download it and how to install it.
http://naveedmazhar.wordpress.com/2008/01/10/install-ajax-on-machines-running-visual-studio-2005/
Step 2: Setup your .aspx web page to use the AJAX Controls
1. Add a new .aspx page to your website solution, call it: Clone.aspx
2. Add references to the following .dll files included in the SDK
a. Microsoft.Crm.Sdk
b. Microsoft.Crm.SdkTypeProxy
3. At the top of the code behind page add:
a. Using Microsoft.Crm.Sdk;
b. Using Microsoft.Crm.Sdk.Query;
c. Using Microsoft.Crm.SdkTypeProxy;
4. From the Design Editor of the page, add the following AJAX controls to the page (in this order)
a. ScriptManager
b. UpdatePanel
c. UpdateProgress
5. Drag the following .NET Controls inside of the UpdatePanel object
a. .NET Button
i. Enabled = true
ii. Visible = false
iii. Name = Button1
b. .NET Hyperlink
i. Enabled = true
ii. Visible = false
iii. Name = Hyperlink1
6. Ascentium created a handy animated .gif for CRM 4.0, it looks great and works very well
a. http://www.ascentium.com/blog/crm/Post189.aspx
7. Drag the animated_2.gif file into the UpdateProgress section of the web page
8. Underneath the animated .gif, type Please Wait…Cloning
9. Your page should now look like the following:

Step 3: Add the necessary code behind for the Clone button functionality
1. Add the following code to the Button1_OnClick()
protected void Button1_Click(object sender, EventArgs e)
{
//Parse Query String Variables
if(Request.QueryString.Count > 0)
{
string _org = Request["orgname"].ToString();
Guid _entityid = new Guid(Request["id"].ToString());
string _entitytypename = Request["typename"].ToString();
//Setup Target Retrieve Request
TargetRetrieveDynamic _entitytoclone = new TargetRetrieveDynamic();
_entitytoclone.EntityName = _entitytypename;
_entitytoclone.EntityId = _entityid;
//Execute Retrieve and Clone Entity
try
{
DynamicEntity _entity;
RetrieveResponse _response;
RetrieveRequest _request = new RetrieveRequest();
//We want all columns that contain data, so use AllColumns()
_request.ColumnSet = new AllColumns();
//We want to use the Dynamic Entity object
_request.ReturnDynamicEntities = true;
_request.Target = _entitytoclone;
//Retrieve our Entity To Clone
CrmAuthenticationToken _token = new CrmAuthenticationToken();
_token.OrganizationName = _org;
_token.AuthenticationType = 0;
CrmService _service = new CrmService();
_service.PreAuthenticate = false;
_service.CrmAuthenticationTokenValue = _token;
_service.Credentials = new System.Net.NetworkCredential("administrator", "pass@word1");
_service.Url = "http://MOSS:5555/mscrmservices/2007/crmservice.asmx";
_response = (RetrieveResponse)_service.Execute(_request);
_entity = (DynamicEntity)_response.BusinessEntity;
//Remove The Key Properties (CustomerAddressID etc)
//Otherwise, it will throw an error about a PK Violation in SQL
foreach(Property p in _entity.Properties)
{
if(p is KeyProperty)
{
_entity.Properties.Remove(p.Name);
}
}
//Create Our Cloned Entity
Guid _newguid = (Guid)_service.Create(_entity);
string _url = "";
HyperLink1.Text = "Record Cloned Successfully! Click Here To Open";
_url = SetLinkButtonProperties(_entityid.ToString(),_entitytypename,_org);
HyperLink1.Attributes.Add("onclick","window.open('" + _url + "');window.close();");
HyperLink1.Visible = true;
_service.Dispose();
}
catch(Exception x)
{
//Handle the Exception
}
}
}
Step 4: Add the following function to the code behind as well, this will get the URL of our newly created record.
private string SetLinkButtonProperties(string id,string name,string org)
{
string path = "../../../";
switch(name)
{
case "account":
path += org + "/sfa/accts/edit.aspx?id=" + id;
break;
case "contact":
path += "/sfa/conts/edit.aspx?id=" + id;
break;
case "opportunity":
path += "/sfa/conts/edit.aspx?id=" +id;
break;
case "lead":
path += "/sfa/lead/edit.aspx?id=" + id;
break;
case "incident":
path += "/cs/cases/edit.aspx?id=" + id;
break;
default:
path += "/userdefined/edit.aspx?id=" + id + "&etn=" + name;
break;
}
return path;
}
Step 5: Add the following Script block to the .aspx page. This will cause our Clone button to automatically trigger a postback when the page is loaded.
<script language='javascript' type="text/javascript">
var _isInitialLoad = true;
function pageLoad(sender, args)
{
if(_isInitialLoad)
{
_isInitialLoad = false;
// simulate a button click by forcing the postback
// causing the updatepanel to update
__doPostBack('<%= this.Button1.ClientID %>','');
}
}
</script>
Step 6: Deploy your custom web project to the ISV Folder or to its own individual website.
In this example, I’ve deployed it to the ISV folder underneath the CRM Website. I added a new folder underneath the ISV folder called: CRMExtensions and copied my code files to that location. I then converted the CRMExtensions folder to a Virtual Directory.
Step 7: Update the ISV.Config File
Deploy the following XML to every entity where the cloning button should be enabled in the ISV.Config file; in this sample it’s deployed to the Account Entity.
<Entities>
<Entity name="account">
<!-- The Account Tool Bar -->
<ToolBar ValidForCreate="0" ValidForUpdate="1">
<Button Icon="/_imgs/ico_18_debug.gif" Url="/isv/crmextensions/clone.aspx" PassParams="1" WinParams="dialogHeight:100px;dialogWidth:300px;" WinMode="1">
<Titles>
<Title LCID="1033" Text="Clone Account" />
</Titles>
<ToolTips>
<ToolTip LCID="1033" Text="Clone Account" />
</ToolTips>
</Button>
</ToolBar>
</Entity>
</Entities>
Step 8: Testing
Now we’re ready to test. Browse to an Account in CRM and you should see the Clone Account button on the Account screen.


When you click on the Button, it should open up our Clone Window, complete with the Progress information.

Once the cloning process has completed, our Hyperlink now appears and the user can open the cloned Account record.

Also, if we look in our list of Accounts, we will see our Account Listed twice now as well.

While this method only does a basic clone, you can extend this functionality further to allow for users to enter in a new Name or other details as well. This cloning method also works well for cloning related entities along with the main entity. That may come in a later post however as it’s a little more involved. You can also download the solution files and the ISV.Config sample.
Jeremy
** This posting is provided "AS IS" with no warranties, and confers no rights.