Sunday, June 21, 2009

Resolving “Cannot Create File Mapping” on custom webpage

When I deployed my custom webpage on Windows Server 2008 (64 bit), and accessed it I got an InvalidOperationException with the description “Cannot create file mapping error “. The Microsoft CRM Trace read as follows:

Exception information:
Exception type: TypeInitializationException
Exception message: The type initializer for 'Microsoft.Crm.Authentication.BaseAuthenticationProvider' threw an exception.


[2009-05-11 22:42:33.5] Process: w3wp Organization:00000000-0000-0000-0000-000000000000 Thread: 3 Category: Exception User: 00000000-0000-0000-0000-000000000000 Level: Error CrmPerformanceCounterFactory.LoadCounters
at CrmPerformanceCounterFactory.LoadCounters(PerformanceCounterLoadSetting settings, String component)
at BaseAuthenticationProvider..ctor()
at MultipleOrganizationSoapHeaderAuthenticationProvider..ctor()
at RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandle& ctor, Boolean& bNeedSecurityCheck)
at RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean fillCache)
at RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)
at Activator.CreateInstance(Type type, Boolean nonPublic)
at BaseAuthenticationSettings.CreateType(String typeName, Type requiredBaseType)
at BaseAuthenticationSettings.CreateProvider(String typeName, IDictionary`2 configuration)
at AuthenticationPipelineSettings.LoadPipeline()
at AuthenticationPipelineSettings.get_AuthenticationProvider()
at AuthenticationEngine.get__provider()
at AuthenticationEngine.Execute(Object sender, EventArgs e)
at SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
at ApplicationStepManager.ResumeSteps(Exception error)
at HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
at HttpRuntime.ProcessRequestNoDemand(HttpWorkerRequest wr)
at ISAPIRuntime.ProcessRequest(IntPtr ecb, Int32 iWRType)


After much R&D, I found two links that helped in fixing the issue:
1. http://support.microsoft.com/kb/958377

2. https://community.dynamics.com/forums/p/11935/16202.aspx


However, even after I repaired my installation of MS CRM ( as advocated in 1 above), I still had to add the following entries in the authorizedTypes section in the web.config for Microsoft CRM (as advocated in the 2nd link provided above) which finally resolved this issue:

<authorizedType Assembly="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Namespace="System" TypeName="Void" Authorized="True"/>
<authorizedType Assembly="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Namespace="System.Reflection" TypeName="AssemblyFileVersionAttribute" Authorized="True"/>
<authorizedType Assembly="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Namespace="System.Reflection" TypeName="AssemblyVersionAttribute" Authorized="True"/>
<!--
* CRM SE 5934 V4: Workflow Designer and Runtime Do Not Handle Decimal Numbers Correctly
-->
<authorizedType Assembly="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Namespace="System.Globalization" TypeName="CultureInfo" Authorized="True"/>


Refreshed my browser window and the custom page rendered just fine!!

Tuesday, June 16, 2009

Combining OR and AND Conditions in Query Filters

Consider a scenario where you have to run a query to retrieve records from MS CRM with the following conditions:
Select Attribute1, Attribute2, Attribute3 from Entity_A where
(Attribute1=value1 OR Attribute1=value2)
AND
Attribute 2=value3
AND
Attribute3=value4

Since, FilterOperators permit only AND or OR Conditionoperators, you can use the FilterExpression.filters property to use a nested subquery for the OR clause.
The code for this will be as follows:

try
{
ColumnSet cols = new ColumnSet();
cols.Attributes = new string[] { Attribute1, Attribute2, Attribute3 };

ConditionExpression attr1Val1= new ConditionExpression();
attr1Val1.AttributeName =Attribute1;
attr1Val1.Operator = ConditionOperator.Equal;
attr1Val1.Values = new object[] { value1};

ConditionExpression attr1Val2= new ConditionExpression();
attr1Val2.AttributeName = Attribute1;
attr1Val2.Operator = ConditionOperator.Equal;
attr1Val1.Values = new object[] { value2};

FilterExpression attr1Conditions= new FilterExpression();
attr1Conditions.Conditions = new ConditionExpression[] { attr1Val1, attr1Val2};
attr1Conditions.FilterOperator = LogicalOperator.Or;


ConditionExpression attr2= new ConditionExpression();
attr2.AttributeName = Attribute2;
attr2.Operator = ConditionOperator.Equal;
attr2.Values = new object[] { value3};

ConditionExpression attr3= new ConditionExpression();
attr3.AttributeName =Attribute3;
attr3.Operator = ConditionOperator.Equal;
attr3.Values = new object[] { value4};


FilterExpression filter = new FilterExpression();
filter.Conditions = new ConditionExpression[] { attr2, attr3 };
filter.Filters = new FilterExpression[] { attr1Conditions };
filter.FilterOperator = LogicalOperator.And;


QueryExpression query = new QueryExpression();
query.ColumnSet = cols;
query.EntityName = Entity_A;
query.Criteria = filter;

BusinessEntityCollection objBusinessEntities = service.RetrieveMultiple(query);

return objBusinessEntities;

}
catch (Exception ex)
{
throw new Exception(ex.Message.ToString());
}

Updating Retrieved Custom Entity Records

The update procedure for Dynamic Entities works well for updating custom entities. However, consider a scenario where you retrieve typecasted records of a custom entity meeting a certain criteria. If you wish to update certain information in these records, using DynamicEntity will throw a type cast error (as the service.retrieve function will return you not a DynamicEntity type but a custom entity type, since the entity for the query is specified by you). The resolution for this is to use the TargetUpdateCustomEntity class for the update.

For example, if you wish to retrieve “prospects” in the city “Redmond” and associate them with a certain “project”, you can use “TargetUpdateProspectsRequest” for the operation.


ColumnSet cols = new ColumnSet();
// Sets the ColumnSet's Properties

cols.Attributes = new string[] { "projectid" };
// Create the ConditionExpression

ConditionExpression condition = new ConditionExpression();


// Set the Condition for the Retrieval to get the Prospects in Redmond City condition.AttributeName = "prospectCity ";

condition.Operator = ConditionOperator.Equal;

condition.Values = new object[] { “Redmond”};


// Create the FilterExpression

FilterExpression filter = new FilterExpression();


// Set the Filter's Properties

filter.FilterOperator = LogicalOperator.And;

filter.Conditions = new ConditionExpression[] { condition };

// Create the QueryExpression Object

QueryExpression query = new QueryExpression();


// Set the QueryExpression Object's Properties

query.EntityName = EntityName.Prospect.ToString();

query.ColumnSet = cols;

query.Criteria = filter;

// Retrieve the records

BusinessEntityCollection prospects = service.RetrieveMultiple(query);
for (int i = 0; i < prospects.BusinessEntities.Length; i = i + 1)

{

//Prospect being the custom entity

Prospect objProspect = (Prospect) prospects.BusinessEntities[i];
Lookup objProjectID = new Lookup();

objProjectID.Value = projectIDValue ;


//set the property value to be updated

objProspect. projectid = objProjectID;


// Create the update target.

TargetUpdateProspect updateProspect = new TargetUpdateProspect();


// Set the properties of the target.

updateProspect.Prospect = objProspect;


// Create the update request object.

UpdateRequest update = new UpdateRequest();


// Set request properties.

update.Target = updateProspect;


// Execute the request.

UpdateResponse updated = (UpdateResponse)service.Execute(update);
}

Thursday, June 4, 2009

Passing Parameters to a URL Using SiteMap for a Multilingual solution

In order to host a web page in the MS CRM sitemap, where the virtual directory resides on the IIS (or specifically in the MS CRM Directory, as is the case with an ISV solution), you are required to export the sitemap, add a SubArea element to the sitemap xml, and then re-import the edited file.

In case it’s a single language solution, you can specify the URL as
http://myserver/mypage.aspx?orgname=AdventureWorksCycle&userlcid=1033&orglcid=1033

However, if your solution supports more than one languages for the user, you will need to get the userlcid parameter on the page in order to render it based on the user’s currently set language (you cannot simply set it to “userlcid=1033”).
Setting the PassParams parameter to 1 in the SubArea tag does this:

<SubArea Id="MyCustomArea" Icon="/_imgs/Icon.gif" PassParams="1" Url="/../ISV/MyCustomSite /HomePage.aspx" Client="Web">

You can then use the page’s querystring in order to read the userlcid parameter:
currentLanguageCode = Request.QueryString["userlcid"].ToString();

Tuesday, June 2, 2009

How to develop yahoo widgets for accessing MSCRM 4.0 data: Part 2




This blog is in continuation to my last blog How to develop yahoo widgets for accessing MSCRM 4.0 data in which I had taken you through the development of yahoo widgets for accessing MSCRM 4.0 data for the on-premise deployment of MSCRM 4.0.
Now, I’ll explain how you can access MSCRM 4.0 data in the internet facing deployment (IFD) model of MSCRM 4.0.
Well, the first and foremost difference between the On-premise & IFD models is the strategy used for authentication, while fetching data from CRM using the web services .
In order to authenticate the web service calls in the hosted (IFD) model we need to specify the valid active directory user credentials to request for the issue of Crm Ticket. This Crm ticket will inherently be used in the crm authentication token provided to the soap request for authenticating the web service calls to fetch data from MSCRM.


See the JavaScript below to retrieve the crm ticket. Fire a soap request including valid user credentials and on authentication, MSCRM will in turn issue a crm ticket, which must be used further in web service calls to fetch the data.

function RetrieveCrmIFDTicket()
{



// Prepare the soap request
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">" +
" <soap:Body>" +
" <Execute xmlns="http://schemas.microsoft.com/crm/2007/CrmDiscoveryService">" +
" <Request xsi:type="RetrieveCrmTicketRequest">" +
" <OrganizationName>"+"MicrosoftCRM"+"</OrganizationName>" +
" <UserId><![CDATA["+strUserName+"]]></UserId>" +
" <Password><![CDATA["+strPassword+"]]></Password>" +
" </Request>" +
" </Execute>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";
//Note: strUserName & strPassword are the preference variables containing user credentials.


xmlHttpRequest = new XMLHttpRequest();

//Event to monitor the status of the request, asynchronously
xmlHttpRequest.onreadystatechange = CrmIFDTicketStatusProc;

xmlHttpRequest.open("POST", "localhost:5555 "+"/MSCRMServices/2007/SPLA/CrmDiscoveryService.asmx", true);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/CrmDiscoveryService/Execute");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);

}

//Note: Replace the “MicrosoftCRM” with your organization name.



// Monitor the status of the request
function CrmIFDTicketStatusProc()
{

if ( this.readyState == 4 ) // request complete
{
if ( this.status == 200 ) // Success
{
var resultXml = this.responseXML;
var orgDetailNodes = resultXml.evaluate("soap:Envelope/soap:Body/ExecuteResponse/Response/OrganizationDetail");

//Extract the single Business entity node
var singleOrgDetailNode = orgDetailNodes.item(0);
var org = singleOrgDetailNode.evaluate("string(OrganizationName)");

//Replace the “MicrosoftCRM” with your organization name
if ( org == "MicrosoftCRM" )
{
crmServiceUrl = singleOrgDetailNode.evaluate("string(CrmServiceUrl)");
}
//Get the crm ticket
crmTicket = resultXml.evaluate("string(soap:Envelope/soap:Body/ExecuteResponse/Response/CrmTicket)");
}
}

}



//Retrieve the MSCRM userid for the currently logged in user
function WhoAmIRequest()
{

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">" +
" <soap:Header>" +
" <CrmAuthenticationToken xmlns="http://schemas.microsoft.com/crm/2007/WebServices">" +
" <AuthenticationType xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">2</AuthenticationType>" +
" <CrmTicket xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">"+crmTicket+"</CrmTicket>" +
" <OrganizationName xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">"+"MicrosoftCRM"+"</OrganizationName>" +
" <CallerId xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">00000000-0000-0000-0000-000000000000</CallerId>" +
" </CrmAuthenticationToken>" +
" </soap:Header>" +
" <soap:Body>" +
" <Execute xmlns="http://schemas.microsoft.com/crm/2007/WebServices">" +
" <Request xsi:type="WhoAmIRequest" />" +
" </Execute>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";


xmlHttpRequest = new XMLHttpRequest();

// Event to monitor the status of the request, asynchronously
xmlHttpRequest.onreadystatechange = WhoAmIStatusProc;
xmlHttpRequest.open("POST", crmServiceUrl, true);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Execute");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);

// Send the request
xmlHttpRequest.send(xml);

}


// Monitor the status of the request
function WhoAmIStatusProc()
{
if ( this.readyState == 4 ) // request complete
{
if ( this.status == 200 ) // Success
{
var resultXml = this.responseXML;

// retrieve userId
UserId = resultXml.evaluate("string(soap:Envelope/soap:Body/ExecuteResponse/Response[@xsi:type='WhoAmIResponse']/UserId)");

if(UserId==null || UserId=="" )
{
throw "UserId is Null";
}
// Retreive the activities for the UserId Passed
IFDActivitiesRequest(UserId);
}

}


}



// Now retreive all activities for the user
function IFDActivitiesRequest(UserId)
{

// Prepare the soap request
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">" +
" <soap:Header>" +
" <CrmAuthenticationToken xmlns="http://schemas.microsoft.com/crm/2007/WebServices">" +
" <AuthenticationType xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">2</AuthenticationType>" +
" <CrmTicket xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">"+crmTicket+"</CrmTicket>" +
" <OrganizationName xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">"+"MicrosoftCRM"+"</OrganizationName>" +
" <CallerId xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">00000000-0000-0000-0000-000000000000</CallerId>" +
" </CrmAuthenticationToken>" +
" </soap:Header>" +
" <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>activitypointer</q1:EntityName>" +
" <q1:ColumnSet xsi:type="q1:ColumnSet">" +
" <q1:Attributes>" +
" <q1:Attribute>activityid</q1:Attribute>" +
" <q1:Attribute>activitytypecode</q1:Attribute>" +
" <q1:Attribute>prioritycode</q1:Attribute>" +
" <q1:Attribute>scheduledend</q1:Attribute>" +
" <q1:Attribute>subject</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:PageInfo>" +
" <q1:PageNumber>" + "1"+ "</q1:PageNumber>" +
" <q1:Count>"+"10"+"</q1:Count>" +
" </q1:PageInfo>" +
" <q1:Criteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>ownerid</q1:AttributeName>" +
" <q1:Operator>Equal</q1:Operator>" +
" <q1:Values>" +
" <q1:Value xsi:type="xsd:string">"+ UserId + "</q1:Value>" +
" </q1:Values>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:Criteria>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";


xmlHttpRequest = new XMLHttpRequest();

// Event to monitor the status of the request, asynchronously
xmlHttpRequest.onreadystatechange = IFDActivitiesRequestStatusProc;
xmlHttpRequest.open("POST", crmServiceUrl, true);
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);

// Send the soap request
xmlHttpRequest.send(xml);

}



// Monitor the status of the request
function IFDActivitiesRequestStatusProc()
{
if ( this.readyState == 4 ) // completed
{
if ( this.status == 200 ) // success
{
//This is the final XML data retreived as a result of the webservice call
var resultXml = this.responseXML;
}
}
}