Monday, December 14, 2009

Modal Window redraws itself on postback

This is a known issue with IE 7/8, that a window opened using window.showModalDialog redraws itself upon postback.

The solution to this problem is that the following statement be added to the <head> tag of the page:

<base target = "_self" />

This should do the trick.

Error message when you try to log on to the Microsoft Dynamics CRM Web site from the Microsoft Dynamics CRM 4.0 server: "Request IP Address has differ

Recently on a IFD deployment, i faced this issue "Request IP Address has different address family from network address". I got to know from (here) that this is a knows issue.

When you try to log on to the Microsoft Dynamics CRM Web site from the Microsoft Dynamics CRM 4.0 server, you receive the following error message:
Request IP Address has different address family from network address.
This problem occurs if the following conditions are true:
1. You run Microsoft Dynamics CRM 4.0 on a Windows Server 2008-based computer.
2. You use Internet-Facing Deployment (IFD) for Microsoft Dynamics CRM 4.0.

The fix for this is simple as posted here. Open your hosts files (:\windows\system32\drivers\etc\hosts) and add a line for your server name there with an IPv4 address. Reset IIS and this is it.

This solves the problem. :)
I hope this helps!!

Unauthorized 401 status error after deploying custom web application

Very recently while working on custom solution deployment on ms crm 4.0, i encountered a 401 Unauthorized error. The web site was deployed to work On-Premise and was deployed as a new virtual directory within the ISV folder, authentication was set to integrated windows authentication. I recieved the following error though the login credentials used were correct.

Server Error in '/ISV/CustomApplication' Application.
The request failed with HTTP status 401: Unauthorized. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.Net.WebException: The request failed with HTTP status 401: Unauthorized.Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. Stack Trace:
[WebException: The request failed with HTTP status 401: Unauthorized.]
System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall) +551137
System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters) +204
Microsoft.Crm.Metadata.MetadataWebService.GetDataSet() +31
Microsoft.Crm.Metadata.DynamicMetadataCacheLoader.LoadDataSetFromWebService(Guid orgId) +301
Microsoft.Crm.Metadata.DynamicMetadataCacheLoader.LoadCacheFromWebService(LoadMasks masks, Guid organizationId) +40
Microsoft.Crm.Metadata.DynamicMetadataCacheFactory.LoadMetadataCache(LoadMethod method, CacheType type, IOrganizationContext context) +418
Microsoft.Crm.Metadata.MetadataCache.LoadCache(IOrganizationContext context) +324
Microsoft.Crm.Metadata.MetadataCache.GetInstance(IOrganizationContext context) +386
Microsoft.Crm.BusinessEntities.BusinessEntityMoniker..ctor(Guid id, String entityName, Guid organizationId) +115
Microsoft.Crm.Caching.UserDataCacheLoader.LoadCacheData(Guid key, ExecutionContext context) +323
Microsoft.Crm.Caching.ObjectModelCacheLoader`2.LoadCacheData(TKey key, IOrganizationContext context) +389
Microsoft.Crm.Caching.BasicCrmCache`2.CreateEntry(TKey key, IOrganizationContext context) +82
Microsoft.Crm.Caching.BasicCrmCache`2.LookupEntry(TKey key, IOrganizationContext context) +108
Microsoft.Crm.BusinessEntities.SecurityLibrary.GetUserInfoInternal(WindowsIdentity identity, IOrganizationContext context, UserAuth& userInfo) +344
Microsoft.Crm.BusinessEntities.SecurityLibrary.GetCallerAndBusinessGuidsFromThread(WindowsIdentity identity, Guid organizationId) +194
Microsoft.Crm.Authentication.CrmWindowsIdentity..ctor(WindowsIdentity innerIdentity, Boolean publishCrmUser, Guid organizationId) +279
Microsoft.Crm.Authentication.WindowsAuthenticationProvider.Authenticate(HttpApplication application) +605
Microsoft.Crm.Authentication.AuthenticationStep.Authenticate(HttpApplication application) +125
Microsoft.Crm.Authentication.AuthenticationPipeline.Authenticate(HttpApplication application) +66
Microsoft.Crm.Authentication.AuthenticationEngine.Execute(Object sender, EventArgs e) +513
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +92
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +64
Version Information: Microsoft .NET Framework Version:2.0.50727.1433; ASP.NET Version:2.0.50727.1433


The ms crm stack trace showed: LoadDataSetFromWebService() got exception: The request failed with HTTP status 401: Unauthorized.

On searching the web i found out here, that CRM is using an HttpModule for multitenancy. The http module will be called with an anonymous user account and will fail when it's requesting the metadata webservice. So, to make the custom application working, i will need to remove the HttpModule from being loaded. This could be done by making modification the config file of the custom application. Add the remove tags as mentioned below and this would do the trick.

<httpModules>
<remove name ="CrmAuthentication"/>
<remove name ="MapOrg"/>
</httpModules>

This would remove only the specific modules. To remove all the modules you can use a generic clear tag.

<httpModules>
<clear/>
</httpModules>

I hope this helps!!

Custom message on the web page during server side processing

Recently, while working on MS CRM based solution, I encountered a typical aspx page loading issue. I had added a button in ISV config of my custom entity, which would load an aspx page. There was a code block on the page load which would fetch records from the server and display them on the page. The processing time increased enormously since there were a lot many records to retrieve and hence, the page loaded real slow :(. And while the user kept waiting for the page to show up, there was really no intimation to the user that the page processing is going on and the page would finally appear :)

I found a very simple solution to this problem through Meta tag usage on the page. Below is a quick sample showing a custom message on the web page while some processing happens at the server side. Though there were other approaches also for this problem, I found this the simplest and easiest to implement.

Follow the below mentioned steps to use the Meta refresh tag.

1. Add the Meta refresh Tag in the HTML code for the web page in the header tag of the page.

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<Title>Copying Event</Title>
<! ----- dynamically filled META REFRESH element ----->
<Meta id="mtaRefresh" runat="server" />
</head>
<Body>
<form id="frmMain" runat="server">
<! ----- "please wait" display ----->
<Center>
<div id="divWait" Visible="false" runat="server"><b>Processing, please wait...</b></div>
</Center>
</form>

</Body>
</html>

Also Notice that a div tag is added in the body section of the page that would display the required “Processing, please wait” message to the user.

2. On the server side code of the page, in Page_load event, add the following code to refresh the URL.

protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["doAction"] == null)
{
string sRefreshURL = Request.Url.ToString();
// use META REFRESH to start loading next page
mtaRefresh.Attributes.Add("http-equiv", "refresh");
mtaRefresh.Attributes.Add("content", "0;url=" + sRefreshURL + "&doAction=1");

divWait.Visible = true;
}
else
{

divWait.Visible = true;
divWait.InnerText = "Done";
}
}


That’s it!! This code block would ensure that the page displays a wait message till the page load event is being processed. Happy Coding!!

Sunday, December 13, 2009

Customizing Workplace

We might be faced with a customer requirement, where it is required that we customize workplace to show say, Type as "Phone Call" instead of "All" and Due as "Today" instead of "All". This customization is possible, although it is unsupported by Microsoft.

The activities page displayed as default view in Workplace can be found at: <install dir>\CRMWeb\Workplace\

Modifying the code as follows does the trick:
function window.onload()
{
// ---------- Add the following code to the OOTB method ----------
// Eg. Set Due = Today as default
crmDateSelector.selectedIndex = 1;

// Eg. Type = Phone Call as default
crmTypeSelector.selectedIndex = 3;
// ---------------------------------------------------------------
}

Please note that above code is unsupported.

Monday, December 7, 2009

Tool to enhance Developer Productivity - XRMLinq

It is designed to increase Developer productivity by reducing the amount of time and effort needed to effectively use Dynamics CRM. Instead of learning the ins and outs of the complex Software Development Kit (SDK) and the complexities of CRM you can use this library, putting CRM into a context that you are already familiar with.

Example:
CRM SDK
QueryExpression q = new QueryExpression();
q.EntityName = "account";
q.ColumnSet = new AllColumns();

q.Criteria = new FilterExpression();
q.Criteria.FilterOperator = LogicalOperator.And;
q.Criteria.AddCondition("name", ConditionOperator.Like, "A%");

q.AddOrder("name", OrderType.Ascending);

BusinessEntityCollection r = sdk.RetrieveMultiple(q);
foreach (account account in r.BusinessEntities)
{
Console.WriteLine(account.name);
}

XrmLinq
CrmDataContext context = new CrmDataContext(sdk);

var accounts = (from a in context.Accounts
where a.Name.StartsWith("A")
orderby a.Name
select a).ToList();

foreach (var account in accounts)
{
Console.WriteLine(account.Name);
}

The tool requires developer license to work. Read about it at:

http://www.xrmlinq.com/

Customization Best Practices

Just came across this list of custmization best practices to follow in the CRM SDK 4.0.10.
Using Custom Entities and Attributes
ISV solutions can save data by using:
• Custom attributes on existing entities
• Custom entities
• A separate database
• Use entity rename to make system entities more meaningful
You should use a unique prefix for custom entities and attributes. For example, use an abbreviation of your company name to avoid conflicts with other ISV add-ins.
Using Isv.Config.xml
Microsoft Dynamics CRM ships with a sample configuration file in the wwwroot_Resources folder. To prepare for installation of ISV add-ins you should rename this file and then create your own configuration file. By doing this you will not confuse the sample UI changes with the UI changes you need for your customization.
ISVs should merge changes into the existing ISV.Config file and not overlay the existing configuration file.
When Do I Add vs. When Do I Customize an Entity?
You should customize a system entity, such as the account entity, rather than replace it because there are many built-in features that you would have to recreate. For example, the account entity has lookup fields to associated customers. You can use the entity rename feature to make the system entity more meaningful to your business.
When Do I Use a Workflow .NET Assembly?
Workflow rules cannot create an instance of an entity. You can use a .NET assembly to do this.
When do I use plug-ins vs. workflow?
As a developer who is interested in extending or customizing Microsoft Dynamics CRM, you have several methods of performing your tasks. In addition to adding client-side JavaScript code to a form, or adding custom ASP.NET page, you can choose to write a plug-in or create a custom workflow by using the Web interface that calls a custom workflow activity. But how do you decide when you should use a plug-in or when you should use a workflow? The technology that you choose depends on the task that you have to perform.
For example, you must use a synchronous plug-in if you need to execute custom code immediately before or after the core platform operation executes and before the result of the operation is returned from the platform. You cannot use a workflow or asynchronous plug-in in this situation because they are queued to execute some time after the core operation executes to completion, and so you are not guaranteed when they will run. On the other hand, if you want to add custom functionality to Microsoft Dynamics CRM Online, only workflows are supported because you cannot add your DLL to the server.
You can read the entire article at:
http://msdn.microsoft.com/en-us/library/dd548494.aspx

The key specified to compute a hash value is expired, only active keys are valid.

Ever accessed Microsoft Dynamics CRM and got the error “The key specified to compute a hash value is expired, only active keys are valid.” The error logged in the trace is as follows:

MSCRM Error Report:
--------------------------------------------------------------------------------------------------------
Error: Exception of type 'System.Web.HttpUnhandledException' was thrown.

Error Number: 0x8004A106

Error Message: The key specified to compute a hash value is expired, only active keys are valid. Expired Key : CrmKey(Id:d0879dd3-7f9d-de11-a5c4-0003ff392bd3, ScaleGroupId:00000000-0000-0000-0000-000000000000, KeyType:CrmWRPCTokenKey, Expired:True, ValidOn:09/09/2009 20:32:01, ExpiresOn:10/12/2009 20:31:55, CreatedOn:09/09/2009 20:32:01, CreatedBy:NT AUTHORITY\NETWORK SERVICE.

Error Details: The key specified to compute a hash value is expired, only active keys are valid. Expired Key : CrmKey(Id:d0879dd3-7f9d-de11-a5c4-0003ff392bd3, ScaleGroupId:00000000-0000-0000-0000-000000000000, KeyType:CrmWRPCTokenKey, Expired:True, ValidOn:09/09/2009 20:32:01, ExpiresOn:10/12/2009 20:31:55, CreatedOn:09/09/2009 20:32:01, CreatedBy:NT AUTHORITY\NETWORK SERVICE.

Source File: Not available

Line Number: Not available

Request URL: http://localhost/Contoso/loader.aspx

Stack Trace Info: [CrmException: The key specified to compute a hash value is expired, only active keys are valid. Expired Key : CrmKey(Id:d0879dd3-7f9d-de11-a5c4-0003ff392bd3, ScaleGroupId:00000000-0000-0000-0000-000000000000, KeyType:CrmWRPCTokenKey, Expired:True, ValidOn:09/09/2009 20:32:01, ExpiresOn:10/12/2009 20:31:55, CreatedOn:09/09/2009 20:32:01, CreatedBy:NT AUTHORITY\NETWORK SERVICE.]
at Microsoft.Crm.CrmKeyService.ComputeHash(CrmKey key, Guid scaleGroupId, HashParameterBase[] parameters)
at Microsoft.Crm.CrmKeyService.ComputeHash(CrmKey key, HashParameterBase[] parameters)
at Microsoft.Crm.Application.Security.WRPCContext..ctor()
at Microsoft.Crm.Application.Controls.AppPage.ValidateWrpcContext()
at Microsoft.Crm.Application.Controls.AppPage.OnInit(EventArgs e)
at System.Web.UI.Control.InitRecursive(Control namingContainer)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

[HttpUnhandledException: Exception of type 'System.Web.HttpUnhandledException' was thrown.]
at System.Web.UI.Page.HandleError(Exception e)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest()
at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
at System.Web.UI.Page.ProcessRequest(HttpContext context)
at ASP.contoso_loader_aspx.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)


Simply restart the asynchronous service and reset IIS. This should make it work!!!

Sunday, December 6, 2009

Unable to publish workflows

After installing Rollup 7 on my MS CRM server, I could not publish any of the workflows. The error in the trace files read “Type System.Globalization.CultureInfo is not marked as authorized in the application configuration file.”
Add the following entries to Microsoft Dynamics CRM web config did the trick to make it work&#61514;

<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"/>

Here’s the link where I got this from:
http://social.microsoft.com/Forums/en/crmdevelopment/thread/e0a8b1f3-5bbf-4040-ab5c-2a67df362b5b

Field-Level Security in Microsoft Dynamics CRM

Recently I came across the blog article Field-Level Security in Microsoft Dynamics CRM : Options and Constraints (at http://blogs.msdn.com/crm/archive/2009/11/16/field-level-security-in-microsoft-dynamics-crm-options-and-constraints.aspx) which talks about restricting attribute level access to certain users based on the different access channels for users to Microsoft Dynamics CRM.

On the client side, one of these ways can be used –
a. Passing an indicator to the field to display to the user that they user cannot view the attribute e.g. ‘******’, ‘Restricted’, ‘Not Authorized’.
b. Making fields read only or hidden if a user is not authorized to view them.

Server Side Restriction –Two strategies are basically proposed:

1. Role based filtering - If the user does not have a role that is entitled to access the attribute then it will be restricted.
An advantage of role based filtering (and a reason for its simplicity) is that the access to an entity’s attributes can be defined without needing to access the data in the entity’s records, and that the definition will apply uniformly across the instances of an entity for a particular user.


2. Rule based Filtering- Building extra rules into the query that is used to request information from the CRM platform. Alternatively, it may be necessary to filter after retrieval, though this results in the additional performance overhead associated with having retrieved data that will be filtered out before information is displayed to the user.


Consider a scenario in which business rules require that all records with a customer turnover:
• Greater than or equal to £1M have the profit field restricted
• Less than £1M have the profit field displayed

Because the platform cannot return the profit for one subset of the customers but hide it for the others, it is necessary to query the platform to return the profit for all customer records and then to filter out the values of the returned records set afterwards before returning it for display at the client.

Following is a brief explanation of the recommended mechanisms for restricting attribute level access in case of server-side access to entities:

1. Plug-ins and CRM SDK web service: Scenarios that require restricted access to data must account for the different ways that forms are used (Create, Retrieval, and Update).

During the creation of a record, for example, it is important to prevent accidental or malicious entry of unauthorized data. This can be accomplished by implementing the appropriate logic within a Pre-Create Plug-In. To prevent a particular user from setting specific attributes, remove those attributes from the InputParameters collection.


DynamicEntity updateValues = (DynamicEntity)context.InputParameters.Properties["Target"];
If( ([USER NOT AUTHORISED] ) && updateValues.Properties.Contains[“ATTRIBUTE_NAME”])
{
updateValues.Properties.Remove[“ATTRIBUTE_NAME”]);
}

During Retrieval, it is important to hide data from unauthorized users. This can be accomplished by using either a Pre-Retrieve or Post-Retrieve Plug-In. It is also necessary to provide a Pre-Update Plug-In to prevent unauthorized users from updating record fields that those users are not authorized to view or to update

2. Access via Views/Advanced Find - The underlying queries that these user interface elements use to retrieve data are sent to the platform via one of two types of messages, either by FetchXML requests sent through the Execute message or by RetrieveMultiple messages. Both types of messages must be filtered to ensure data security.

3. CRM Metadata web service – If the metadata service is used, it is not possible to hide the fact that information of a certain type is captured for a particular entity, and therefore other mechanisms should be used so as to hide the actual data captured against that attribute.

4. Export-to-Excel feature ensures that users can only export the data to which they have access. After enabling field-level security by using the plug-in approach, it is important to consider the Export-to-Excel capability to prevent data restricted through the UI from being accessed in this way.
Applying field-level security is more problematic with dynamic worksheets however, because the data is retrieved directly by the Excel worksheet at the client from the database by using the filtered views that the CRM product creates and exposes. There is no supported way to intercept or alter the data returned by the CRM provided Filtered Views. As a result, the best option available is to prevent direct access to the database Filtered Views altogether by the user from the client

5. To secure data that can be accessed by using reports, available options include:
• Disabling Reports access for roles that require field-level security.
• Disabling report creation for roles that require field-level security. Provide pre-defined custom reports for those users.

6. To secure direct access to Database Filtered Views, available options include:
• Denying user membership to the CRM Reporting Group.

7. Access via Outlook Synchronization and the Offline Client - If any of the data shared with Outlook (i.e. contacts, appointments, tasks) or the offline client needs to be limited at a field level for particular users, then those users must not be able to use the CRM Outlook add-in.

Wednesday, November 4, 2009

JavaScript code to parse FetchXMLResult

Recently I came across a scenario, where I was required to query based on an intersect table for which Fetch method can only be used, which required parsing Fetch XML Result string was at the client side.

Fetch method is the query language used in Microsoft Dynamics CRM. It is based on a schema that describes the capabilities of the language. The FetchXML language supports similar query capabilities as query expression.

Following is the code that can be used to achieve the same:
var resultXml = xmlHttpRequest.responseXML;
var objNodeList = resultXml.documentElement.selectNodes("//FetchXmlResult");
var totalNodesCount = objNodeList.length;
if (totalNodesCount > 0)

{
var iter = 0;
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.loadXML(objNodeList[0].childNodes[0].nodeValue);


var objResultList = xmlDoc.documentElement.selectNodes("//result");
for(iter = 0; iter <= totalNodesCount; iter++)

{
// Columns Retrieved can be accessed using child nodes
var strEntityId = objResultList[iter].childNodes[0].text;
var strEntityType= objResultList[iter].childNodes[1].text;
}
}

Tuesday, October 13, 2009

msftesql and cisvc services error in MS CRM 4.0

While installing MSCRM 4.0 on Windows Server 2008, having SQL Server 2008 preinstalled, the installation verification fails and displays missing components msftesql and cisvc services.

1) msftesql (the sql server full text indexing service) has a different name against sql server 2008.

Just rename the registry key HKLM\System\CurrentControlSet\Services\MSSQLFDLauncher to msftesql to give an illusion to the MSCRM verification phase that services is present in the system so that it does not fail while installation.
Now restart the system and run the installation of MSCRM again.
After installation rename the HKLM\System\CurrentControlSet\Services\msftesql back to MSSQLFDLauncher.

2) To solve the cisvc services error, right click the MyComputer->Manage, now right click the Roles ->Add Roles and now install the file services role.

Thursday, October 1, 2009

Using Disposable Windows SharePoint Services Objects

During my last project on MS CRM 4.0, I had to integrate SharePoint and MSCRM. This majorly involved creation of a web part for data analysis. During the development, I encountered scenarios facing long term retention of share point objects in memory.

Memory leak is one of the major problems that can arise due to bad coding practices. Disposing the used SharePoint Objects is one of the best practices while coding in SharePoint. Here, I have explained why, how and when to dispose off the SP Objects in code.


The objects in the Windows SharePoint Services 3.0 object model serve as an interface for working with Windows SharePoint Services data. The developers frequently call into the object model to read data from or write new data to the Windows SharePoint Services store.
The Windows SharePoint Services object model contains objects that implement the IDisposable interface. One must take precautions when using these objects to avoid their long-term retention in memory in the Microsoft .NET Framework.
Specifically, one should explicitly dispose of those SharePoint objects that implement IDisposable after using them.

Why Dispose is required?
Several of the Windows SharePoint Services objects, primarily the SPSite class and SPWeb class objects, are created as managed objects. However, these objects use unmanaged code and memory to perform the majority of their work. The managed part of the object is much smaller than the unmanaged part. Because the smaller managed part does not put memory pressure on the garbage collector, the garbage collector does not release the object from memory in a timely manner. The object's use of a large amount of unmanaged memory can cause some of the unusual behaviors described earlier. Calling applications that work with IDisposable objects in Windows SharePoint Services must dispose of the objects when the applications finish using them. You should not rely on the garbage collector to release them from memory automatically.

Coding Techniques to Ensure Object Disposal

One can employ certain coding techniques to ensure object disposal. These techniques include using the following in your code:
• Dispose method
• using clause
• try, catch, and finally blocks

The using Clause:
Automatically dispose of SharePoint objects that implement the IDisposable interface by using the Microsoft Visual C# using statement.

using(SPSite oSPsite = new SPSite("http://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
String str = oSPWeb.Title;
str = oSPWeb.Url;
} //SPWeb object is disposed automatically
} // SPSite object is disposed automatically


The try, catch, and finally Blocks

Using try, catch, and finally blocks obviously makes sense whenever you need to handle exceptions. Any code within a try/catch block should have a governing finally clause to ensure that the objects that implement IDisposable are disposed. Never leave a catch block empty. Also note the best practice of testing for null before disposing.

String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
try
{
oSPSite = new SPSite("http://server");
oSPWeb = oSPSite.OpenWeb(..);

str = oSPWeb.Title;
}
catch(Exception e)
{ //Handle exception, log exception, etc.
}
finally
{
if (oSPWeb != null)
oSPWeb.Dispose();

if (oSPSite != null)
oSPSite.Dispose();
}


Note: SPContext objects are managed by the SharePoint framework and should not be explicitly disposed in code. This is true also for the SPSite and SPWeb objects returned by SPContext.Site, SPContext.Current.Site, SPContext.Web, and SPContext.Current.Web.

Recommendations to Reduce Long-Term Object Retention

You can reduce long-term retention of SharePoint objects by following these general recommendations:-

• While creating an object with a new operator, ensure that the creating application disposes it off.

Explicitly Disposing
void CreatingSPSiteExplicitDisposeNoLeak()
{
SPSite siteCollection = null;
try
{
siteCollection = new SPSite("http://moss");
}
finally
{
if (siteCollection != null)
siteCollection.Dispose();
}
}


Automatically Disposing
void CreatingSPSiteWithAutomaticDisposeNoLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
} // SPSite object siteCollection.Dispose() is called automatically.
}

• Dispose of items created by SharePoint methods that return other SPWeb objects (such as OpenWeb).

• Do not share any SPRequest object (and by extension any object that contains a reference to an SPRequest object) across threads. Any coding technique that shares a SPRequest object between two or more threads, or creates a SPRequest object on one thread and disposes it on another, is not supported. This means that any object that holds a reference to a SPRequest object in a static variable cannot be stored. Thus, Do not store SharePoint objects that implement IDisposable (such as SPWeb orSPSite) in static variables.

For Reference, SharePoint Dispose Checker Tool, is a free tool available as a download that inspects assemblies for coding practices that cause memory leaks because of improper handling and disposal of SharePoint objects.

Soon I will post another article on How To Find Incorrectly Disposed Objects.

Wednesday, September 30, 2009

Issues after Re-Naming MS CRM Servers computer/machine name

Currently I faced a scenario where the machine on which MS CRM 4.0 was installed was required to be renamed on LAN. My deployment had the SQL Server installed on another machine on LAN.

After changing the MSCRM Server machine name, MS CRM browser stopped working and started showing error “SqlException: Login failed for user [Domain Name]\[New Machine Name].” This error comes when the MS CRM server related entries in registry and database (that refer to the server name) have not yet been changed to the new server name

This problem could be solved by 'Repairing’ the MS CRM 4.0 installation on the machine after renaming it.
Go to the Control Panel -> Add or Remove programs-> Microsoft Dynamics CRM Server. Repair the installation.

Repair installation internally performs the following actions:
a. In the registry, ServerURL key value at HKEY_LOCAL_MACHINE/SOFTWARE/MICROSOFT/MSCRM is updated with the new machine name (example: the value would be like “http://:5555/MSCRMServices”).
b. Adds new server name entry to dbo.server table in the MSCRM_CONFIG database.
c. Adds the new machine name (computer name) to the PrivUserGroup and SQLAccessGroup in the active directory groups.

These steps could also be performed manually (I actually performed steps a) and c) manually before ‘Repairing’  ).

These steps should ensure correct working of MS CRM in case of renaming of server machine after installing MS CRM 4.0.

Tuesday, September 22, 2009

Working with MSCRM 4.0 Reports in IFD deployment

Recently for a project requirement I had to deploy MSCRM 4.0 on an IFD server. Everything else seems to work properly except the Reports. The reports were running good on on-premise configurations for the same organization. This issue was same for Custom reports as well as the out-of-box reports by MS CRM.

Its then I read that this is a common issue with IFD deployment. To resolve this, the deployment must be running the Microsoft Dynamics CRM Connector for Microsoft SQL Server Reporting Services.

The SRSDataConnector for MS CRM 4.0 comes with the installation files for MS CRM 4.0 and is located on the installation CD: “drive:\Server\i386 \srsDataConnector” folder. Run the “setupsrsdataconnector.exe” executable in the mentioned folder and you are done with it!

How does it work? - When a Microsoft Dynamics CRM report is run, Microsoft SQL Server Reporting Services Viewer requests the report and data from the remote Microsoft SQL Server Reporting Services computer. To access the report, the Microsoft Dynamics CRM server URL is used. Microsoft Dynamics CRM Connector for Microsoft SQL Server Reporting Services runs as a Microsoft SQL Server Reporting Services data processing extension and handles the authentication in the delegated mode used for reports.

However, there is a limitation with the connector too. The MS CRM connector doesn't work with the Microsoft SQL Server 2005 Workgroup Edition. This is because the Workgroup edition does not support custom data extensions used in the Microsoft Dynamics CRM Connector for Microsoft SQL Server Reporting Services. So, to further resolve this, one should use Standard Edition OR Enterprise Edition of SQL Server. This is applicable for both SQL Server 2005 OR SQL Server 2008 Installations.

Tuesday, September 15, 2009

Initializing distinct XAMLs included in a single XAP

So I had a situation where I had to load multiple charts on my custom page default.aspx within Microsoft Dynamics CRM. These charts rendered different reporting data after fetching it using the MS CRM webservices.
I used silverlight visifire charts for each of my reports. Instead of creating a different silverlight application for each of my reports, I instead created different XAMLs for each of them and compiled a single XAP. Well, this is a well tried and tested approach used in any silverlight application or any aspx application using Silverlight controls which involves multiple XAMLs compiled in a single XAP. These are the steps I used:

1. Created a Silverlight application that contained multiple XAMLs each one including a different report (i.e. a different visifire chart) and compiled it as one "SLDashboardPOC.xap".

2. For each of the reports to be rendered, I embedded the compiled Silverlight XAP control multiple times in my default.aspx as follows:

<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<div>
<asp:Silverlight ID="Xaml1" runat="server" Source="~/SL/SLDashboardPOC.xap"
MinimumVersion="2.0.31005.0" Width="66%" Height="50%" />



<asp:Silverlight ID="Xaml2" runat="server" Source="~/SL/SLDashboardPOC.xap"
MinimumVersion="2.0.31005.0" Width="66%" Height="80%" />
.
.
.
and so on..

Now, at the time of rendering the reports I had to specify to my embedded XAP(s) which XAML page to initialize and render. I fetched datasets for different reports using the MS CRM web services from my business tier and converted them into XMLs using dsReportData.GetXml().ToString(). Then I passed these distinct dataset XMLs as parameters to the different visifire charts included in my XAP. I did this on my Page_Load by sending the datasets to my silverlight app as follows:

String strData1 = GetDataForReport1(); //This will return the dataset XML from MS CRM
initParams = "ReportParam1=" + strData1; //Create "Key=Value" Initialization parameters
Xaml1.InitParameters = initParams; //Add these to the collection of initparameters for the specific report

String strData2 = GetDataForReport2();
Xaml2.InitParameters = "ReportParam2=" + strData2;

4. Then I used these parameters in my Silverlight application's Application_Startup event to add them to the application's resources as follows:

if (e.InitParams.Count > 0)
{
foreach(var item in e.InitParams)
{
this.Resources.Add(item.Key , item.Value ); //Add each of the "Key=Value" pairs paased from the hosting page
}
}

if (e.InitParams.ContainsKey("ReportParam1"))
{
this.RootVisual = new Report1(); //This will define which XAML will be the default for the XAP. I specify a different RootVisual property for rendering each of the charts
}
if (e.InitParams.ContainsKey("ReportParam2"))
{
this.RootVisual = new Report2();
}

5. And finally while initializing my chart and rendering data, I used the applications' resources to fetch the report parameter that contained the dataset XML my chart required:
if (App.Current.Resources.Contains("ReportParam1"))
{
//Add data points to the chart and render it
{
}

Wednesday, September 9, 2009

Windows Prompt for an IFD Deployment

This blog will explain why Microsoft Dynamics CRM prompts for Windows Credentials when you log on to your development environment (configured for IFD ) using the server's IP address or the Host file configured URL (such as http://contoso.crm.grapecity.org).
This is basically determined by MS CRM by reading the registry value of the key [HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSCRM]- IfdInternalNetworkAddress.

In reality, Microsoft Dynamics CRM will look at the IP address of the incoming request and match it against the internal range of the registry key specified. If the request is in the internal range of the registry key, it will give you a "Windows experience." If the IP address is external, it gives an "IFD experience."

This registry can be modified to switch between the windows and the IFD experiences (and this is supported :)). You will need to set the value for the "IfdInternalNetworkAddress" registry key in order to change the internal subnet of the IFD setting.

Philip Richrdson explains this well(Refer to : http://blog.philiprichardson.org/2007/12/20/switching-between-ifd-and-windows/)
For example, if the client is configured with an IP address of 192.168.1.2 (Subnet: 255.255.255.0):
When the internal range is 192.168.1.1-255.255.255.0 you will get a Windows experience. (assuming that this is the value which you provided in the config.xml file when Microsoft Dynamics CRM was installed.)
When the internal range is 192.168.1.1-255.255.255.255 you will get an IFD experience. This is because the client on the host operating system has an IP of 192.168.1.2 (Subnet: 255.255.255.0), which is outside the internal range.

Here are the two registry key modifications:
IFD Experience:
[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSCRM] "IfdInternalNetworkAddress"="192.168.1.1-255.255.255.255"

Windows Experience:
[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSCRM] "IfdInternalNetworkAddress"="192.168.1.1-255.255.255.0"

Tuesday, September 8, 2009

Using CRM Services to create or update records in large numbers

Recently, I implemented a plug in code which invoked auto creation of records from a contacts list (rather marketing lists based on MS CRM contacts) for my custom entity. The code worked well but it hanged whenever I considered a marketing list of more than 3000 contacts. The error logged read: Only one usage of each socket address (protocol/network address/port) is normally permitted.


Basically, the error was thrown when the creation process exhausted the limit of socket connections allowed by the IIS and the SDK still polled for another connection and consequentially timed out after a certain number of records were created. A quick workaround for this is to change the following entries in the registry:

HKLM\System\CurrentControlSet\Services\Tcpip\Parameters\MaxUserPort to 65534AndHKLM\System\CurrentControlSet\Services\Tcpip\Parameters\TCPTimedWaitDelay to be 30




The link : http://dmcrm.blogspot.com/2008/07/updating-records-in-mass-via-sdk.html also mentions a resolution of this by using the following code snippet:



int NUM_RETRY_ITERATIONS = 50;
int p = 0;
do
{
try
{
crmService.Create(customentity);
p = NUM_RETRY_ITERATIONS;
}
catch (Exception e)
{
p++;
if (p >= NUM_RETRY_ITERATIONS)
{
throw (e);
//WriteToFile("updateContact error: " + e.Message + " " + e.InnerException);
}
System.Threading.Thread.Sleep(500);
}
} while (p < NUM_RETRY_ITERATIONS);

IFD Implementation for custom web pages and Anonymous Access

When deploying my custom web pages for an IFD deployment, I got a FileLoadException (Exception from HRESULT: 0x80131401) on accessing the page in the browser.


Basically, this issue arose since anonymous access was somehow disabled for the “ISV/CustomPages” application in IIS while by default it is enabled for the Microsoft Dynamics CRM website.

This implied that my custom web site was using Windows NT Authentication, while the Microsoft Dynamics CRM Website was enabled for anonymous access. Hence, I got “Loading this assembly would produce a different grant set from other instances” on accessing any of my web pages from within MS CRM.

Simply turning on anonymous access for my custom website did the trick and rendered all my custom web pages correctly :)

Saturday, August 29, 2009

Support for onPremise version over internet

Recently we faced issue when we tried to access MSCRM in MS Outlook. We were trying to access onPremise version over Internet and was getting Authentication failed error while configuring using Configuration Wizard. After lot of research to resolve authentication problem, we did found out that only IFD version can be accessed in MS Outlook. OnPremise version cannot be accessed in Outlook over internet. However, onPremise version can be accessed in Internet explorer, but it might not be supported. It is recommended to use IFD version (Service Provider edition), if you have to use MSCRM over internet. OnPremise edition should be used inside corporate network only.

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;
}
}
}




Tuesday, May 26, 2009

Bulk Edit Issue on Custom Entities

A typical problem while performing a Bulk edit on any custom entity is that all the hidden fields on the form become visible. Since MS CRM does not provide a way out to let the fields remain hidden on bulk edit form, you might think of restricting users from performing bulk edits for custom entities.
Here is the simple way to proceed on this:

1. On the custom entity Form, add a hidden Iframe.
2. Create a simple .htm file and create a new virtual directory in IIS under the ISV folder where you place this htm page.
3. Specify this relative path as the URL of the iframe.

Example if your custom application is hosted at \ISV\MyCustomWebPages folder and the .htm file is MyPage.htm, the Iframe URL would be \ISV\MyCustomWebPages\MyPage.htm.

In the .htm file, check the form type for Bulk Edit. MSCRM by default provides form type an enum for form types. (See SDK:
http://technet.microsoft.com/en-us/library/cc150873.aspx ) Bulk edit type form value is 6.

if(parent.document.forms[0].FormType == 6)
// parent.document.forms[0] is the reference for the custom entity form
{
// Do required
alert("Bulk Edit is restricted for this entity");
window.close();
}

This code would check if the user wants to perform a Bulk edit for the custom entities. If so, it alerts the user and closes the form.

Note: This behavior is not reproducible for out of the box entities.

Wednesday, May 20, 2009

How to develop yahoo widgets for accessing MSCRM 4.0 data


In this blog, I’ll take you through the development of yahoo widget for accessing MSCRM 4.0 data for the on premise deployment of MSCRM. In my future blogs you may find the implementation for the hosted & Crm Live models as well.

The purpose of this article is to quickly access the MSCRM data & provide a quick navigation link to directly jump to the record in MSCRM using browser.So, what kind of data would be more relevant to a user in MSCRM? Well answer to this question depends on the role of the user but, MSCRM Activities are something which is relevant to all types of users. So, in this walkthrough we’ll retrieve the activity records of MSCRM 4.0 to display i.e fetching all the activities for the currently logged in user from the MSCRM server.

Audience Profile: The target audience should already be familiar with Yahoo widget’s development and should have little understanding of XML, JavaScript and webservice calls as well.

To start with prepare your layout for the widget by specifying some XML tags in the .kon file which is the main starting point for the widgets.
Below is an example for the kon file.

<?xml version="1.0" encoding="UTF-8"?>
<widget minimumVersion="4.0">
<settings>
<setting name="allowCustomObjectAttributes" value="true"/>
<setting name="debug" value="on"/>
</settings>
<window title="Sample Widget!">
<name>mainWindow</name>
<width>380</width>
<height>136</height>
<alignment>left</alignment>
<opacity>255</opacity>
<visible>1</visible>
<shadow>1</shadow>
<onFirstDisplay>
mainWindow.hOffset = (screen.width/2) - 114;
mainWindow.vOffset = screen.availHeight + 195;
</onFirstDisplay>
<!-- Specify images as part of layout you want to display-->
<image src="Topbar.png">
<name>top</name>
<hOffset>7</hOffset>
<vOffset>1</vOffset>
<onMouseUp>
openURL("http://www.Grapecity.com");
</onMouseUp>
</image>

<image src ="pagingFooter.png">
<name>pagingFooterImage</name>
<hOffset>7</hOffset>
<vOffset>45</vOffset>
<zindex>1</zindex>
<visible>false</visible>
</image>
</window>

<action trigger="onLoad">
<!--include the javascript file here containing code for web service calls-->
include("MSCRM_Activities.js");
</action>

<action trigger="onUnload" >
<!--Specify any action when disconnecting-->
play('Widget_Shut_disconnect.wav');
</action>

<!-- Specify the preference groups, displayed as tabs in the preferences window -->
<prefGroup>
<name>msCRM</name>
<title>MSCRM Settings</title>
<icon>Resources/CRM_Logo.gif</icon>
<order>1</order>
</prefGroup>

<!-- Specify the user configurable preferences -->
<preference name="OrganizationName">
<title>Organization Name</title>
<group>msCRM</group>
<type>Text</type>
<defaultValue>MicrosoftCRM</defaultValue>
<description>Organization's Name (Case Sensitive)
</description>
</preference>
<preference name="ServerName">
<title>Server</title>
<group>msCRM </group>
<type>Text</type>
<defaultValue>localhost:5555</defaultValue>
<description>With port number (if any). Example: ServerName:Port</description>
</preference>
</widget>


Note: The above declarations are just for example, you’ll need to design the layout for the widget.

We will be using web service calls to retrieve the xml data and that we can parse later on and display.

Below is the code to be included in the MSCRM_Activities.js file to call the MSCRM 4.0 webservices with active directory authentication.

//Poll for the target organization using discovery service and get the Url for the CrmService
function RetrieveOrganizationsRequest()
{
// 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="RetrieveOrganizationsRequest" />" +
" </Execute>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";

xmlHttpRequest = new XMLHttpRequest();

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

//Fire the request asynchronously
xmlHttpRequest.open("POST", "http://"+ "localhost:5555"+"/MSCRMServices/2007/AD/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);
}


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

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/OrganizationDetails/OrganizationDetail");

for( i=0 ; i < orgDetailNodes.length ;i++)
{
//Extract the single Business entity node item from the nodelist
var singleOrgDetailNode = orgDetailNodes.item(i);
var org = singleOrgDetailNode.evaluate("string(OrganizationName)");
if ( org == strOrganizationName )
{
crmServiceUrl = singleOrgDetailNode.evaluate("string(CrmServiceUrl)");
break;
}
}

if(crmServiceUrl=="")
{
alert("Organisation not found");
}
else
{
WhoAmIRequest();
}
}
}
}


Note: Some supported methods listed in konfabulator reference (SDK for yahoo widgets) have been used for xml document parsing. Please refer to SDK for more information.


// Retreive the MSCRM userid for the currently logged in user
function WhoAmIRequest()
{
//Prepare the xml soap request to be fired
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">" +

// This is the soap header containing your authentication information uses windows authentication in this case.
" <soap:Header>" +
" <CrmAuthenticationToken xmlns="http://schemas.microsoft.com/crm/2007/WebServices">" +
" <AuthenticationType xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">0</AuthenticationType>" +
" <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 starts here
" <soap:Body>" +
" <Execute xmlns="http://schemas.microsoft.com/crm/2007/WebServices">" +
" <Request xsi:type="WhoAmIRequest" />" +
" </Execute>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";

xmlHttpRequest = new XMLHttpRequest();

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

//Fire the request asynchronously
xmlHttpRequest.open("POST", "http://" + "localhost:5555" + crmServiceUrl.substring(7).substring(crmServiceUrl.substring(7).search("/")), 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";
}
// Reteive the activities for the UserId Passed
GetActivitiesRequest(UserId);

}

}

}


// Now retreive all activities for the user
function GetActivitiesRequest(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">0</AuthenticationType>" +
" <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 = ActivitiesRequestStatusProc;
//Fire the request asynchronously
xmlHttpRequest.open("POST","http://"+"localhost:5555"+crmServiceUrl.substring(7).substring(crmServiceUrl.substring(7).search("/")), 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 ActivitiesRequestStatusProc()
{

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

}

}
}

}


You can download the GrapeCity widget’s accessing the MSCRM 4.0 data from here
http://www.grapecity.com/india/widgets

Creating your custom solution for MS CRM 4.0 - Programmatically fetch Isv Config and Sitemap for an organization

The CRM SDK provides ExportXmlRequest in order to export any customizations programatically. For example, the isv config for an organization can be exported as :
 CrmServiceRef.ExportXmlRequest exportRequest = new CrmServiceRef.ExportXmlRequest();

// Define the nodes to retrieve, the site map and the isv configuration.

exportRequest.ParameterXml = @"<importexportxml>
<entities></entities>
<nodes>
<node>isvconfig</node>
</nodes>
<securityroles></securityroles>
<settings></settings>
<workflows></workflows>
</importexportxml>";

// Execute the request.
CrmServiceRef.ExportXmlResponse isvconfigXml = (CrmServiceRef.ExportXmlResponse)crmService.Execute(exportRequest);
Alternatively, in order to read the contents of the ISV Config or Sitemap for any organization using code, the "organizationid" attribute for the organization class can be used as well. This is explained as follows:

In order to read the isv.config content :
1. Use the Discovery service to get the organization id for the organization.
2. Query the "isvconfig" entity (use QueryByAtrribute based on the attribute "organizationid") and fetch the "configxml" attribute. This returns the xml content for the isv.config file of a particular organization.

To access the sitemap xml content:
1. Use the Discovery service to get the organization id for the organization.
2. Fetch the organization instance using CrmService.Retrieve based on organization id.
3. Read the "sitemapxml" attribute of the organization retrieved.
The code sample here illustrates this:
CrmServiceRef.ColumnSet cols = new CrmServiceRef.ColumnSet();
cols.Attributes = new string[] { strpSITEMAPXML };

CrmServiceRef.organization org = (CrmServiceRef.organization)crmService.Retrieve(CrmServiceRef.EntityName.organization.ToString(),organizationid, cols);
CrmServiceRef.ColumnSet cols = new CrmServiceRef.ColumnSet();
if (org != null)
{
String strSitemapBackupPath = @"C:\Sitemap Backup\orgSitemap.xml";
// Create an instance of StreamWriter to write text to a file.
// The using statement also closes the StreamWriter.
using (StreamWriter sw = new StreamWriter(strSitemapBackupPath))
{
// Write the customization XML to the file
sw.Write(org.sitemapxml);
}
}

Monday, May 18, 2009

Troubleshooting the Reports Issues in MS CRM 4.0

This article helps you troubleshoot couple of Reporting Services issues that you may encounter while running reports in MS CRM 4.0.The problems may occur even when the MS CRM 4.0 installation was done successfully and no error message was shown for reporting services.

The most common error message that is generally displayed while running the report in MS CRM 4.0 is “The report cannot be displayed”.
This message is a very general one and does not point to any specific problem. There may be various causes for it and one of those is that SQL Server Reporting Services is not running on the server where SQL is installed.

To solve this problem, locate the service in Service Manager and check its status and startup type in the Properties.
If the status is ‘Stopped’, first make the startup type of the service as ‘Automatic’; this will ensure auto-start of the service whenever required, and then start the service.

If you are not able to start the SQL Server Reporting Services and you get Error 1503: The service did not respond to the start or control request in a timely fashion, verify that multiple application pools with similar names do not exist for Reports to run.

To do away with this problem, perform the following steps:
Make sure that you are logged in to the system with administrative rights.

1. Go to IIS Manager.( Open the Run window and type ‘inetmgr’)

2. Navigate to Websites->Default Web Sites and then locate the ‘Reports’ and ‘Report Server’ virtual directory.

3. Check the Properties of each virtual directory and make note of the name of the Application Pool specified.

4. Now, navigate to ‘Application Pools’ and check if there is any other application pool with similar name apart from the one identified in Step 4.If such application pool exists, delete it.

For example, if the Application Pool name specified in the properties of ‘Reports’ and ‘Reports Server’ Virtual directory is ‘ReportServer’ and there exists application pools ‘ReportsServer’ and ‘ReportServer$SQL’ under Application Pool, then you need to delete ‘ReportServer$SQLExpress’ application pool which is additional and not used by Reports to run.

Apart from the problem specified above many a times we face the problems in deploying the reports to MS CRM 4.0 after developing in Visual Studio.

Even when the report is working fine in Visual Studio (SQL server Business Intelligence Development Studio) 2005, it shows either of the following problems:
  1. Prompts for the SQL server username and password every time you wish to deploy it and does not accept any credentials.
  2. Shows an error message -Logon failed. Logon failure: unknown user name or bad password. (Exception from HRESULT: 0x8007052E) is shown in Report Manager while running the report.

To overcome this problem, you need to ensure that the same user credentials are used by the report server in execution account. Perform the following steps to fix the issue:

1. Open the Reporting Services Configuration Manger. You can navigate to Programs->SQL Server 2005->Configuration Tools-> Reporting Services Configuration.

2. Check the ‘Execution Account’ tab in Reporting Services Configuration Manager.

3. If the execution account is set, check the account name and password entered there. It should be same as the user credentials used for logging into the system. Enter the password if it is left blank or reset it, otherwise.