Showing posts with label CRM. Show all posts
Showing posts with label CRM. Show all posts

Friday, May 25, 2012

Timeout expired on Dynamics CRM 4.0

Today I got a timeout expired error when calling a query to dynamic crm (from web service). I saw the query from trace and I when I executed it from sql server it elapsed 31 seconds. The problem was that the default timeout of sql queries in Microsoft Dynamics CRM 4.0 is 30 seconds.
This timeout can be overriden from registry by adding a DWORD value with the timeout time in seconds to the following registry key:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM\OLEDBTimeout]

Good reference of timeout specifications can be found here

Tuesday, December 28, 2010

Custom Isv.Config behavior based on User Security Roles

In Microsoft Dynamics CRM we can embed our custom solution throw sitemap, isv.config and iframe. If we want to have a custom behavior in isv.config based on User Security Roles we can inject javascript to do that.

Here is a custom button on toolbar that has custom javascript inside isv.config:

 
<ToolBar ValidForCreate="0" ValidForUpdate="0">
  <Button Icon="/_imgs/ico_16_1013.gif" ValidForCreate="0" 
ValidForUpdate="0" PassParams="1" WinMode="0" JavaScript="…………">
    <Titles>
      <Title LCID="1033" Text="Export To CSV" />
    </Titles>
      <ToolTips>
      <ToolTip LCID="1033" Text="Export To CSV" />
    </ToolTips>
  </Button>
</ToolBar>


 
Inside javascript we want a function to return all roles of a specific user by creating s SOAP message request:

//*********************
function GetCurrentUserRoles()
{
 var xml = "" +
 "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
 "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
 GenerateAuthenticationHeader() +
 " <soap:Body>" +
 " <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
 " <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" +
 " <q1:EntityName>role</q1:EntityName>" +
 " <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
 " <q1:Attributes>" +
 " <q1:Attribute>name</q1:Attribute>" +
 " </q1:Attributes>" +
 " </q1:ColumnSet>" +
 " <q1:Distinct>false</q1:Distinct>" +
 " <q1:LinkEntities>" +
 " <q1:LinkEntity>" +
 " <q1:LinkFromAttributeName>roleid</q1:LinkFromAttributeName>" +
 " <q1:LinkFromEntityName>role</q1:LinkFromEntityName>" +
 " <q1:LinkToEntityName>systemuserroles</q1:LinkToEntityName>" +
 " <q1:LinkToAttributeName>roleid</q1:LinkToAttributeName>" +
 " <q1:JoinOperator>Inner</q1:JoinOperator>" +
 " <q1:LinkEntities>" +
 " <q1:LinkEntity>" +
 " <q1:LinkFromAttributeName>systemuserid</q1:LinkFromAttributeName>" +
 " <q1:LinkFromEntityName>systemuserroles</q1:LinkFromEntityName>" +
 " <q1:LinkToEntityName>systemuser</q1:LinkToEntityName>" +
 " <q1:LinkToAttributeName>systemuserid</q1:LinkToAttributeName>" +
 " <q1:JoinOperator>Inner</q1:JoinOperator>" +
 " <q1:LinkCriteria>" +
 " <q1:FilterOperator>And</q1:FilterOperator>" +
 " <q1:Conditions>" +
 " <q1:Condition>" +
 " <q1:AttributeName>systemuserid</q1:AttributeName>" +
 " <q1:Operator>EqualUserId</q1:Operator>" +
 " </q1:Condition>" +
 " </q1:Conditions>" +
 " </q1:LinkCriteria>" +
 " </q1:LinkEntity>" +
 " </q1:LinkEntities>" +
 " </q1:LinkEntity>" +
 " </q1:LinkEntities>" +
 " </query>" +
 " </RetrieveMultiple>" +
 " </soap:Body>" +
 "</soap:Envelope>" +
 "";
 
 var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
 xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
 xmlHttpRequest.setRequestHeader("SOAPAction"," http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
 xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
 xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
 xmlHttpRequest.send(xml);
 
 var resultXml = xmlHttpRequest.responseXML;
 return(resultXml);
}
//*********************

Then we need a function to check if specified user has specified role:

//*********************
function UserHasRole(roleName) {
    //get Current User Roles, oXml is an object
    var oXml = GetCurrentUserRoles();
    if (oXml != null) {
        //select the node text
        var roles = oXml.selectNodes("//BusinessEntity/q1:name");
        if (roles != null) {
            for (i = 0; i < roles.length; i++) {
                if (roles[i].text == roleName) {
                    //return true if user has this role
                    return true;
                }
            }
        }
    }
    //otherwise return false
    return false;
}
//*********************

Finally we have the actual code for button. Our intention is to check if a user has a specific security role (by name) and if he has it, to restrict him from making executing the action of button.
 
if(!UserHasRole("No CSV Export"))
{
  // OK pass and go to custom solution for making the export
  window.open('/ISV/CRMISVCustoms/ExportToCSV.aspx?orgname=' + ORG_UNIQUE_NAME,'ExportCSV','width=500,height=200,resizable=yes');
}
else
{
   // Not authorized
   alert('You are not authorized to Export in csv');
}


Monday, December 13, 2010

Microsoft Dynamics CRM limits export to 10000 rows to Excel

Our customer is making lists of contacts to send then in third parties for process. The number of contacts in those list can be larger than 50000. When we made an export to excel we saw that only 10000 contacts where exported. It turned out that there was a limitation in the organization database for exporting only 10000 rows from excel. The solution was easy. We changed the limit value from sql server (table OrganizationBase field MaxRecordsForExportToExcel). The query was:

update OrganizationBase set
      MaxRecordsForExportToExcel = 65500

After the query we made a restart of iis and everything worked.

Thursday, December 2, 2010

The E-mail Router service could not run the service main background thread

Well I found in a Microsoft Dynamics CRM 4.0 rollup 10 with email router (on-premise) deployment that the email router service was stopped. I tryed to start the service and I couldn' t. I looked at event viewer and I found this log:

#16192 - The E-mail Router service could not run the service main background thread. The E-mail Router service cannot continue and will now shut down. System.Configuration.ConfigurationErrorsException: The E-mail router service cannot access system state file Microsoft.Crm.Tools.EmailAgent.SystemState.xml. The file may be missing or may not be accessible. The E-mail Router service cannot continue and will now shut down. ---> System.Xml.XmlException: Root element is missing.
   at System.Xml.XmlTextReaderImpl.Throw(Exception e)
   at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
   at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)
   at System.Xml.XmlDocument.Load(XmlReader reader)
   at System.Xml.XmlDocument.Load(String filename)
   at Microsoft.Crm.Tools.Email.Providers.ConfigFileReader..ctor(String filePath, ServiceLogger serviceLogger)
   at Microsoft.Crm.Tools.Email.Providers.SystemState.Initialize(ServiceLogger serviceLogger)
   at Microsoft.Crm.Tools.Email.Agent.ServiceCore.InitializeSystemConfiguration()
   --- End of inner exception stack trace ---
   at Microsoft.Crm.Tools.Email.Agent.ServiceCore.InitializeSystemConfiguration()
   at Microsoft.Crm.Tools.Email.Agent.ServiceCore.ExecuteService()

After some search I found here that file Microsoft.Crm.Tools.EmailAgent.SystemState.xml was corrupted. I deleted the file and started the email router service.

Tuesday, November 30, 2010

Change max publish duplicate detection rules

We have an on premise CRM 4.0 rollup 10 deployment, and our client have requested a number of duplicate detection rules upon leads. We started the development of the rules but when we tried to publish then the system informed us that only 5 rules where allowed per entity.

After some googling we found a solution to our problem. In MSCRM_CONFIG database table DeploymentProperties witch holds the constraint of max 5 rules per entity. With a simple query and a full restart of IIS – CRM Asynchronous Service we were able to publish more than 5 rules.

The query for max 7 duplicate detection rules was:

update DeploymentProperties set
      IntColumn = 7
where ColumnName = 'DupMaxPublishedRules'

The above solution is not supported by Microsoft and can cause serious performance issues, but in our case (the number of leads is relatively small) we didn’t have any problem.

Monday, October 25, 2010

User ID assosiated with the current record is not valid

We had a CRM 4 rollup 10 installation in a on premise deployment and we tried to make an image of the deployment and change the name of the computer. At the end of the procedure we couldn’t login to the original server and the server returned the following message User ID associated with the current record is not valid.
Well the problem that caused this message was that the machine principal in active directory had become invalid. We fixed the problem be making the following steps:
1. We detached the server machine from the domain and we made sure that the principal of the server was deleted from AD.

2. We attached again the machine to AD.

3. We added the newly created machine principal to the following crm security groups: PrivReportingGroup, SQLAccessGroup and PrivUserGroup (CRM security groups of deployment)

After that we were able to login again:-)

Tuesday, April 6, 2010

Problem after deleting Active Directory User Account

I found my self in a strange situation after disabling a user from Microsoft Dynamics CRM 4.0 and the deleting the user from Active Directory. I couldn’t enable again the user because there was no Active Directory User associated with it. I tried many solutions to bypass this problem but they did not worked for me. The only solution that worked was to remove the company from the deployment (I made a backup first, just in case) and then I import the same company. When importing a company Microsoft Dynamics CRM 4.0 make new associations of crm users with active directory users, so I matched the disabled user with a new account of active directory.

Friday, March 26, 2010

Error Reinstalling CRM 4.0

The following error occurred when trying to reinstall MS Dynamics CRM 4.0. The problem is that the uninstall process of CRM does not make a complete removal of all the security principals from the system.



In order to make a proper uninstall of Microsoft Dynamics CRM 4.0 product, I followed the steps below:


1. I made the uninstall of all crm products from add/remove programs


2. I deleted all contents of crm directory. Mine was ‘C:\Program Files\Microsoft Dynamics CRM’


3. From Active Directory I removed all the crm principals




Principals in you deployment may be placed on other folder in Active Directory depending of the installation you have made


4. From Sql Server I made a backup and then a delete of all crm databases (*_MSCRM, *_CONFIG)
5. From Sql Server I removed all the crm principals





6. I deleted CRM registry settings HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM (I had a trace flag on, and a language pack)

After all this I made a brand new installation without any problem.


This post help me to come with this solution

Friday, February 12, 2010

How to send sms from Microsoft Dynamics CRM

In order to be able to send sms from Microsoft Dynamics CRM we must have an account with a sms provider. For my example I used an account with http api. This means that I need to send http request to the provider in order to send sms. For my solution I will create a workflow that handles the sms sending. The workflow will be triggered by campaign activities, and the sms will be send to each account in Marketing List (of accounts) of campaign activity. The mobile phone is placed on a custom field in account, named new_mobile.
First of all I we create a new project in Visual Studio 2008 of type CrmWorkflowActivity. Rename the workflow file to SendSMSWorkflow.cs.

using System;
using System.IO;
using System.Net;
using System.Web;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using System.Security.Principal;
// Microsoft Dynamics CRM namespaces
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Workflow;
using Microsoft.Crm.Workflow.Activities;
namespace CustomWorkflowActivity
{
[CrmWorkflowActivity("Send SMS", "My Company")]
public class SendSMSWorkflow : SequenceActivity
{
private int smsToSend;
private int smsSent;
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
try
{
// Get access to the Microsoft Dynamics CRM Web service proxy.
IContextService contextService = (IContextService)executionContext.GetService(typeof(IContextService));
IWorkflowContext context = contextService.Context;
ICrmService crmService = context.CreateCrmService();
...
}
catch (System.Web.Services.Protocols.SoapException ex)
{
throw new InvalidPluginExecutionException(
String.Format("An error occurred in the {0} plug-in.",
this.GetType().ToString()),
ex);
}
return ActivityExecutionStatus.Closed;
}
}
}
We will declare some input variables witch are needed for the specific sms provider
public static DependencyProperty UserProperty =
DependencyProperty.Register("User", typeof(System.String), typeof(SendSMSWorkflow));
///
/// This is the username of the sms provider account
///
[CrmInput("User")]
public String User
{
get
{
return (String)base.GetValue(UserProperty);
}
set
{
base.SetValue(UserProperty, value);
}
}
public static DependencyProperty PasswordProperty =
DependencyProperty.Register("Password", typeof(System.String), typeof(SendSMSWorkflow));
///
/// This is the password of the sms provider account
///
[CrmInput("Password")]
public String Password
{
get
{
return (String)base.GetValue(PasswordProperty);
}
set
{
base.SetValue(PasswordProperty, value);
}
}
public static DependencyProperty SenderProperty =
DependencyProperty.Register("Sender", typeof(System.String), typeof(SendSMSWorkflow));
///
/// This is the sender that will be displayed in sms
///
[CrmInput("Sender")]
public String Sender
{
get
{
return (String)base.GetValue(SenderProperty);
}
set
{
base.SetValue(SenderProperty, value);
}
}
public static DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(System.String), typeof(SendSMSWorkflow));
///
/// This is the text of the sms
///
[CrmInput("Text")]
public String Text
{
get
{
return (String)base.GetValue(TextProperty);
}
set
{
base.SetValue(TextProperty, value);
}
}
public static DependencyProperty ProxyProperty =
DependencyProperty.Register("Proxy", typeof(System.String), typeof(SendSMSWorkflow));
///
/// Proxy to be used (It's necessary where proxy is used to access internet)
///
[CrmInput("Proxy")]
public String Proxy
{
get
{
return (String)base.GetValue(ProxyProperty);
}
set
{
base.SetValue(ProxyProperty, value);
}
}
public static DependencyProperty DemoProperty =
DependencyProperty.Register("Demo", typeof(CrmBoolean), typeof(SendSMSWorkflow));
///
/// I use this so I can set the workflow to demo state, and not send
/// actual sms.
///
[CrmInput("Demo")]
[CrmDefault("false")]
public CrmBoolean Demo
{
get
{
return (CrmBoolean)base.GetValue(DemoProperty);
}
set
{
base.SetValue(DemoProperty, value);
}
}

Next I will declare some output parameters that I will update them with status data.

public static DependencyProperty SmsResultProperty =
DependencyProperty.Register("SmsResult", typeof(System.String), typeof(SendSMSWorkflow));
///
/// Result of sms send workflow. It returns "OK" if everything is ok.
///
[CrmOutput("SmsResult")]
public String SmsResult
{
get
{
return (String)base.GetValue(SmsResultProperty);
}
set
{
base.SetValue(SmsResultProperty, value);
}
}
public static DependencyProperty SmsSentProperty =
DependencyProperty.Register("SmsSent", typeof(CrmNumber), typeof(SendSMSWorkflow));
///
/// Sms that actually sent
///
[CrmOutput("SmsSent")]
public CrmNumber SmsSent
{
get
{
return (CrmNumber)base.GetValue(SmsSentProperty);
}
set
{
base.SetValue(SmsSentProperty, value);
}
}
public static DependencyProperty SmsToSendProperty =
DependencyProperty.Register("SmsToSend", typeof(CrmNumber), typeof(SendSMSWorkflow));
///
/// Sms supposed to be sent
///
[CrmOutput("SmsToSend")]
public CrmNumber SmsToSend
{
get
{
return (CrmNumber)base.GetValue(SmsToSendProperty);
}
set
{
base.SetValue(SmsToSendProperty, value);
}
}

In Workflow execute function first I will retrieve the marketing lists of campaign activity. And for each List I’ll send sms to all members

try
{
// Get access to the Microsoft Dynamics CRM Web service proxy.
IContextService contextService = (IContextService)executionContext.GetService(typeof(IContextService));
IWorkflowContext context = contextService.Context;
ICrmService crmService = context.CreateCrmService();
// We run only for Campaign Activity
if (context.PrimaryEntityName == EntityName.campaignactivity.ToString())
{
smsSent = 0;
smsToSend = 0;
TargetRetrieveDynamic targetRetrieve = new TargetRetrieveDynamic();
RetrieveRequest retrieverequest = new RetrieveRequest();
retrieverequest.ColumnSet = new AllColumns();
retrieverequest.ReturnDynamicEntities = true;
retrieverequest.Target = targetRetrieve;
targetRetrieve.EntityName = EntityName.campaignactivity.ToString();
targetRetrieve.EntityId = context.PrimaryEntityId;
RetrieveResponse retrieved = (RetrieveResponse)crmService.Execute(retrieverequest);
DynamicEntity entity = (DynamicEntity)retrieved.BusinessEntity;
if (entity != null)
{
// Find all activity lists
LinkEntity le = new LinkEntity();
le.LinkFromEntityName = EntityName.list.ToString();
le.LinkFromAttributeName = "listid";
le.LinkToEntityName = EntityName.campaignactivityitem.ToString();
le.LinkToAttributeName = "itemid";
LinkEntity le2 = new LinkEntity();
le2.LinkFromAttributeName = "campaignactivityid";
le2.LinkFromEntityName = EntityName.campaignactivityitem.ToString();
le2.LinkToAttributeName = "activityid";
le2.LinkToEntityName = EntityName.campaignactivity.ToString();
// Create the condition to test the user ID.
ConditionExpression ce = new ConditionExpression();
ce.AttributeName = "activityid";
ce.Operator = ConditionOperator.Equal;
ce.Values = new object[] { context.PrimaryEntityId };
// Add the condition to the link entity.
le2.LinkCriteria = new FilterExpression();
le2.LinkCriteria.Conditions.Add(ce);
le.LinkEntities.Add(le2);
// Put everything together in an expression.
QueryExpression qryExpression = new QueryExpression();
qryExpression.ColumnSet = new AllColumns();
qryExpression.EntityName = EntityName.list.ToString();
qryExpression.LinkEntities.Add(le);
// Return all records.
qryExpression.Distinct = true;
// Execute the query.
BusinessEntityCollection entityResultSet = crmService.RetrieveMultiple(qryExpression);
foreach (list crmList in entityResultSet.BusinessEntities)
{
SendToMembers(crmService, crmList);
}
}
}
SmsResult = "OK";
SmsSent = new CrmNumber(smsSent);
SmsToSend = new CrmNumber(smsToSend);
}
catch (System.Web.Services.Protocols.SoapException ex)
{
throw new InvalidPluginExecutionException(
String.Format("An error occurred in the {0} plug-in.",
this.GetType().ToString()),
ex);
}
return ActivityExecutionStatus.Closed;

Function SendToMembers will send sms to each member

private void SendToMembers(ICrmService crmService, list crmList)
{
if (crmList.membertype == null)
return;
LinkEntity le = new LinkEntity();
QueryExpression qryExpression = new QueryExpression();
// Create the retrieve target.
TargetRetrieveDynamic targetRetrieve = new TargetRetrieveDynamic();
RetrieveRequest retrieverequest = new RetrieveRequest();
retrieverequest.ColumnSet = new AllColumns();
retrieverequest.ReturnDynamicEntities = true;
retrieverequest.Target = targetRetrieve;
switch (crmList.membertype.Value)
{
case 1:
le.LinkFromEntityName = EntityName.account.ToString();
le.LinkFromAttributeName = "accountid";
qryExpression.EntityName = EntityName.account.ToString();
targetRetrieve.EntityName = EntityName.account.ToString();
break;
default:
return;
}
le.LinkToEntityName = EntityName.listmember.ToString();
le.LinkToAttributeName = "entityid";
// Create the condition to test the user ID.
ConditionExpression ce = new ConditionExpression();
ce.AttributeName = "listid";
ce.Operator = ConditionOperator.Equal;
ce.Values = new object[] { crmList.listid.Value };
// Add the condition to the link entity.
le.LinkCriteria = new FilterExpression();
le.LinkCriteria.Conditions.Add(ce);
// Put everything together in an expression.
qryExpression.ColumnSet = new AllColumns();
qryExpression.LinkEntities.Add(le);
// Return all records.
qryExpression.Distinct = true;
BusinessEntityCollection profilesResultSet = crmService.RetrieveMultiple(qryExpression);
DynamicEntity entity;
string mobile;
foreach (BusinessEntity crmEntity in profilesResultSet.BusinessEntities)
{
smsToSend++;
mobile = null;
switch (crmList.membertype.Value)
{
case 1:
account crmAccount = crmEntity as account;
targetRetrieve.EntityId = crmAccount.accountid.Value;
RetrieveResponse retrieved = (RetrieveResponse)crmService.Execute(retrieverequest);
entity = (DynamicEntity)retrieved.BusinessEntity;
// This is custom field
if (entity.Properties.Contains("new_mobile"))
{
mobile = (string)entity.Properties["new_mobile"];
}
break;
default:
return;
}
string strResponse = SendSms(mobile);
if (!string.IsNullOrEmpty(strResponse) && strResponse.ToLower().StartsWith("sent"))
smsSent++;
}
}

Function SendSms makes the actual request to the provider.

private string SendSms(string receiver)
{
if (Demo != null && Demo.Value == true)
return "sent";
string result = null;
// Make some modifications to prepare the phone number for the provider
receiver = PrepareReceiver(receiver);
if (receiver != null)
{
string url = "not set yet";
try
{
url = string.Format(@"http://services.yuboto.com/sms/api/smsc.asp?User={0}&Pass={1}&Action=send&From={2}&To={3}&Text={4}",
HttpUtility.UrlEncode(User),
HttpUtility.UrlEncode(Password),
HttpUtility.UrlEncode(Sender),
HttpUtility.UrlEncode(receiver),
HttpUtility.UrlEncode(Text));
WebRequest request = HttpWebRequest.Create(url);
if (!string.IsNullOrEmpty(Proxy))
{
request.Proxy = new WebProxy(Proxy, true);
}
WebResponse response = request.GetResponse();
StreamReader sresponse = new StreamReader(response.GetResponseStream());
result = sresponse.ReadToEnd();
}
catch (Exception ex)
{
throw new Exception(string.Format("Url '{0}' failed", url), ex);
}
}
return result;
}

Function PrepareReceiver make some modifications to the mobile phone to meet the criteria of the provider

private string PrepareReceiver(string receiver)
{
if (string.IsNullOrEmpty(receiver))
return null;
receiver = receiver.Replace(" ", "");
receiver = receiver.Replace("-", "");
return receiver;
}

This workflow is only for demonstration and its purpose is only to show you the capabilities and the flexibility of Microsoft Dynamics CRM Workflow.