Tuesday, January 19, 2010

Problems with AsyncFileUpload because of one Session per Control

I have had the following problem with AsyncFileUpload from Ajax Toolkit:

  • One aspx-Page with one asyncfileupload control
  • Same user could open the page severval times at the same time
  • Problem only one session value for all page instances!

I have found out that the client id was the key to the problem and have inherited from the standard class and have overridden the important properties:

   1: public class ExtendedAsyncFileUpload : AsyncFileUpload
   2:     {
   3:         private string newClientID = null;
   4:         private string newUniqueID = null;
   5:  
   6:  
   7:         /// <summary>
   8:         /// Unique ID for session of a user
   9:         /// </summary>
  10:         protected string SessionUniqueControlIdentifier
  11:         {
  12:             get
  13:             {
  14:                 if (string.IsNullOrEmpty(ViewState["SessionUniqueControlIdentifier"] as string))
  15:                     ViewState["SessionUniqueControlIdentifier"] = Guid.NewGuid().ToString().Replace("-", "");
  16:                 return ViewState["SessionUniqueControlIdentifier"] as string;
  17:             }
  18:         }
  19:  
  20:         protected override void OnPreRender(EventArgs e)
  21:         {
  22:             base.OnPreRender(e);
  23:         }
  24:  
  25:  
  26:         public override string ClientID
  27:         {
  28:             get
  29:             {
  30:  
  31:                 if (string.IsNullOrEmpty(newClientID))
  32:                     newClientID = UniqueID.Replace("$", "_");
  33:                 return newClientID;
  34:             }
  35:         }
  36:  
  37:         public override string UniqueID
  38:         {
  39:             get
  40:             {
  41:                 if (string.IsNullOrEmpty(newUniqueID) && !base.UniqueID.EndsWith(this.SessionUniqueControlIdentifier))
  42:                     newUniqueID = base.UniqueID + this.SessionUniqueControlIdentifier;
  43:                 return newUniqueID;
  44:             }
  45:         }
  46:  
  47:         /// <summary>
  48:         /// Overriden because, original method catches all exceptions, no matter what...
  49:         /// </summary>
  50:         public byte[] FileBytesNew
  51:         {
  52:             get
  53:             {
  54:                 HttpPostedFile file = base.PostedFile;
  55:                 if (file != null)
  56:                     return this.GetBytesFromStream(file.InputStream);
  57:                 else
  58:                     return null;
  59:             }
  60:         }
  61:  
  62:     }

Thursday, August 27, 2009

Prevent users from sending error messages to Microsoft

If error messages are sent or can be sent is a option within the user settings. There is no global switch for that, but at least you can use the following SQL script to set the value on all existing users and prevent them from sending error messages to Microsoft:

   1: update UserSettings set ReportScriptErrors = 3;

Thursday, November 20, 2008

CRM 4 - Prevent workflow execution

In order to prevent workflow execution, you can intercept the CRM logic by writing your own plug-in for  asyncoperation/create/pre. Within your plug-in you can apply your custom logic to decide whether the asyncoperation is a workflow and if this workflow should not be run and therefore not appear in the workflow instance list. Unfortunately you have to throw an exception to make this happen. Within you systemjobs you'll find a failed workflow expansion task.

In order to allow a plug-ing for create on asyncoperation you have to run the following SQL-statement:

update sdkmessagefilter
set IsCustomProcessingStepAllowed = 1
where sdkmessageid in (
select sdkmessageid
from sdkmessage
where name = 'Create')
and primaryobjecttypecode = 4700

Monday, September 22, 2008

Showing child grids in an IFRAME without custom grid

Here is some code where you can just insert some fetch xml in JavaScript and display the data in the standard grid. just replace the IFRAME_<name> and adjust the CHANGE HERE section with some custom fetch xml and the entity type name.

//Load view data
function GetAdvFindView(entityname)
{
var xml = "" + 
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" + 
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" + 
GenerateAuthenticationHeader() + 
"  <soap:Body>" + 
"    <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" + 
"      <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" + 
"        <q1:EntityName>savedquery</q1:EntityName>" + 
"        <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" + 
"          <q1:Attributes>" + 
"            <q1:Attribute>savedqueryid</q1:Attribute>" + 
"            <q1:Attribute>layoutxml</q1:Attribute>" + 
"          </q1:Attributes>" + 
"        </q1:ColumnSet>" + 
"        <q1:Distinct>false</q1:Distinct>" + 
"        <q1:Criteria>" + 
"          <q1:FilterOperator>And</q1:FilterOperator>" + 
"          <q1:Conditions>" + 
"            <q1:Condition>" + 
"              <q1:AttributeName>querytype</q1:AttributeName>" + 
"              <q1:Operator>Equal</q1:Operator>" + 
"              <q1:Values>" + 
"                <q1:Value xsi:type=\"xsd:int\">1</q1:Value>" + 
"              </q1:Values>" + 
"            </q1:Condition>" + 
"            <q1:Condition>" + 
"              <q1:AttributeName>fetchxml</q1:AttributeName>" + 
"              <q1:Operator>Like</q1:Operator>" + 
"              <q1:Values>" + 
"                <q1:Value xsi:type=\"xsd:string\">%&lt;entity name=\""+entityname+"\"&gt;%</q1:Value>" + 
"              </q1:Values>" + 
"            </q1:Condition>" + 
"          </q1:Conditions>" + 
"        </q1:Criteria>" + 
"      </query>" + 
"    </RetrieveMultiple>" + 
"  </soap:Body>" + 
"</soap:Envelope>" + 
"";
 
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
 
xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);
 
var resultXml = xmlHttpRequest.responseXML;
 
if(resultXml.getElementsByTagName('BusinessEntity').length>0)
{
  return resultXml.getElementsByTagName('BusinessEntity')[0];
}
else
{ return null; }
 
 
}
 
//Loads page with child grid
function LoadChildGrid()
{
crmForm.all.IFRAME_LOCS.onreadystatechange= null;
 
//Create a new HTTP request.
    var request = new ActiveXObject("Msxml2.XMLHTTP");    
    request.onreadystatechange = function() 
    { 
      if (request.readyState == 4) 
      {
         if (request.status == 200) 
        {
           var newDoc=crmForm.all.IFRAME_LOCS.contentWindow.document.open();
           newDoc.write(request.responseText);
           newDoc.close();
         } 
        else 
        {
            alert('There was a problem with the request:'+request.status);
        }   
      }
    }
 
request.Open("POST", "/AdvancedFind/fetchData.aspx",true);
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
 
//CHANGE HERE------------------
var entityname='account';
var fetchxml = "<fetch mapping='logical'><entity name='account'><all-attributes/></entity></fetch>";
//CHANGE HERE-----------------
 
var view = GetAdvFindView(entityname);
if (view)
{
  var layoutxml = view.getElementsByTagName('q1:layoutxml')[0].firstChild.data;
  var defaultAdvFindViewId =view.getElementsByTagName('q1:savedqueryid')[0].firstChild.data;
 
  request.send("FetchXml="+escape(fetchxml)+"&DefaultAdvFindViewId="+defaultAdvFindViewId  +"&LayoutXml="+escape(layoutxml)+"&ViewType=1039");
}
 
}
crmForm.all.IFRAME_LOCS.onreadystatechange= LoadChildGrid;

Friday, August 29, 2008

Export to Excel - More than 10000 records

For Dynamics CRM 4.0 run this SQL-Statement in your Organization Database:

update organizationbase 
set MaxRecordsForExportToExcel = 65535

Thursday, August 7, 2008

Duplicate detection - non-empty fields

If run a duplicate detection job with a rule that has more than one column, CRM would treat records as duplicates where at least conditions is met and the other fields are empty. In my case this lead to a huge problem, e.g.:

A rule defines that records are duplicates if full name and email address are equal. Unfortunately these two records would also be duplicates:

Full name email address Matchcode
Max   max-
Max   max-

But I would only like to be the next two records identified as duplicates

Full name email address Matchcode
Max max@xyz.com max-max@xyz.com
Max max@xyz.com max-max@xyz.com

There is an unsupported possibility to solve this issue. The problem is that the generate match code is compared and they are equal. So a solution would be to generate random values for empty fields when generating the match codes.

Before the match codes are generated, the message "getunprocessedrecords" is used to retrieve the records (DynamicEntities). The idea now is to intercept the read records and random values for empty fields.

public void Execute(IPluginExecutionContext context)
       {
           //-----------------
           //Check context
           //-----------------
           this.CheckContext(context, "getunprocessedrecords", "none");                      
 
           //Iterate over all records and add StringProperties with random content for empty fields
           BusinessEntityCollection eColl = (BusinessEntityCollection)context.OutputParameters["UnprocessedRecords"];
           foreach (DynamicEntity d in eColl.BusinessEntities)
           {
               foreach (string col in (string[])context.InputParameters["columns"])
               {
                   if (!d.Properties.Contains(col))
                       d.Properties.Add(new StringProperty(col,Guid.NewGuid().ToString().Substring(0,10)));
               }    
           }
                     
       }

The plug-in must be registered as "sync" and "post" options and entity as "none". In order to allow registering a plug-in we must execute the following SQL statement:


update dbo.SdkMessageFilter set iscustomprocessingstepallowed=1 where Sdkmessageid='E3D6A4D6-5C39-477A-B616-C29232313779'