Friday 18 June 2010

My hero .. My Dad ...

To me, Dad is the hero for every child. Great poets and speakers talked great about motherhood but a father is great too... Mother teaches lessons and makes the child understand, she gets concerned if the kid doesn't get what she tries to teach, but a father doesn't even let the child know that he is teaching ! All that is taught by a father is by sharing experiences and examples.

If mom tells you "be careful", dad tells you "take a chance and yet, stand strong".

If mom prays God for your well being, dad comes to your rescue and shares your painful moments.

If mom feels that "everything should be good for you", dad teaches you how to handle those bad situations.

I got to know that many parts of the world celebrate "Father's day" on the third Sunday of June. I don't seem to be lucky to celebrate 'coz it's been almost a year since my dad passed away.

My brother and I call my dad - "Boss" - the one who decides what's good for us. We miss those moments when we discussed everything that happened during our day at work and my dad used to map those to his experiences and provided valuable inputs. He told us to love the work you do, in other words - get attached to the work and not with the people nor the workplace, which I think is quite a practical advice.

My dad suffered from Cancer but never did he express pain during the time he was undergoing the "killing" therapies - for the reason not to let us down or panic. He showed us how to handle pain and be optimistic.

The only gift that one can give to his/her dad is to keep him happy during his old age. Realize that your (older) dad is your first kid and I sometimes feel lucky to have taken care of him during the treatment, served food as if he was a kid.
I fed my dad the night before he passed away and I remember his last words - "Have some food, everything is going to be okay... I will take rest for now" and my dad's soul rests in peace.

Remembering my father in every moment of my life ...

    Following his principles in every walk of my life ....

Saturday 29 May 2010

Flexigrid.Net

Past weekend, I ran into an interesting implementation of a grid using JQuery called "Flexigrid". There used to be an elegant web page with little about the plugin and a couple of examples @ http://flexigrid.info/ but this link doesn't seem to work any more !
You could view the flexigrid in action @ http://flexigrid.eyeviewdesign.com/.
The plugin offers quite a lot of useful functionality. I found a couple of resources on the web that explains how to use this grid in an ASP.NET application, but I wanted to implement my version to fix/add the following functionality:
  1. An event when a row is selected
  2. Sort the data that is in the view (say you are viewing 10 records out of 100 and you just want the sort to happen on the 10 records on the page). Although you could use dynamic datasource and get the sort working, I wanted to have it on a table that is intialized on the page and without having to get data from a server component.
  3. A user control with flexigrid that lets you define the columns that you want to show, no matter how many ever columns are returned in the dataset

I tried my hand to build an ASP.NET website that uses Flexigrid (modified a bit to include the above two items, and I have used TinyTableSorter javascript to accomplish sorting) and the data for the grid comes dynamically from AdventureWorks database through an ASP.NET web form. The motto was to let a developer just place a user control and define the columns that should be visible without having to write flexgrid documented javascript on the page (except for sorting, as I am using another javascript to do this).

Currently the web form in the POC sends data as JSON string and I shall extend it to send XML too, sometime in future :) In the meantime, check out this code project article.

Here is the example for you to download :)

Thursday 15 April 2010

MS Dynamics CRM 4.0 Record Counter (revisited) ...

Few months ago, I talked about some of the work that we had done in adding some functionalities to CRM 4.0.In this post I would like to furnish some details about our way of implementing a "CRM Record Counter".

Google for "CRM Record Counter" and you will get "n" results and the common thing about the solutions offered in these sites is that they ask you to download/write and register a custom plugin. The way the plug-in works is that it gets invoked each time a search is fired (due to changing the view or by performing a quick search or an advanced find).The plugin has to be attached to the "Execute" message in CRM. After registering the plugin successfully, you would see that all the views that present a list of records will now start presenting the totals as a first row in the list ! Awesome, isn't it?

Technically, the plugin intercepts the search request and response and just adds the required detail to the response appropriately.

The best aspect of this solution is that it works for quick search, Advanced find, look ups as well as associated views too. No other solution can beat this, for sure. The downside of this solution is that it flushes this data in exports too ! I mean, if the user does an export to excel, the total row is also exported as the first row in excel. Well, this is not a major thing that prevents one from accepting this solution.Users can always delete the first row and deal with the rest of the data.

Having explained a bit of detail about a solution that is available on the web, I would like to share my knowledge on how we can do all this (with some limitations, though) without having to register a custom plugin, (yes, you heard it right ;-)).

Let me talk a bit about how the grid in CRM is rendered. Here is a screenshot of a view from CRM:








Now, if you want to manipulate the data that is in the table, you should be changing a file by name "grid.htc". This file is located @ \Microsoft Dynamics CRM\CRMWeb\_static\_grid on the server where CRM is installed.

Open this file and you will observe that it looks like a "hi-fi" javascript code.In this file, define a function as given below:


function PrintFetchXMLForActiveView() {


var fetchXML = '';

if (top.stage != null && top.stage.crmGrid != null) {

var sviewID = top.stage.crmGrid.GetParameter('viewid');


//alert(sviewID);


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

"<Retrieve xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>" +

"<entityName>savedquery</entityName>" +

"<id>" + sviewID + "</id>" +

"<columnSet xmlns:q1='http://schemas.microsoft.com/crm/2006/Query' xsi:type='q1:ColumnSet'>" +

"<q1:Attributes>" +

"<q1:Attribute>fetchxml</q1:Attribute>" +

"</q1:Attributes>" +

"</columnSet>" +

"</Retrieve>" +

"</soap:Body>" +

"</soap:Envelope>";

// Prepare the xmlHttpObject and send the request.

var xHReq = new ActiveXObject("Msxml2.XMLHTTP");

xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);

xHReq.setRequestHeader("SOAPAction", http://schemas.microsoft.com/crm/2007/WebServices/Retrieve);

xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");

xHReq.setRequestHeader("Content-Length", xml.length);

xHReq.send(xml);

// Capture the result.

var resultXml = xHReq.responseXML;

// // DEBUG: alert(resultXml.selectSingleNode("//q1:modifiedon").nodeTypedValue);

// // DEBUG: alert(crmForm.all.modifiedon.DataValue);


if (resultXml.selectSingleNode("//q1:fetchxml") != null) {

fetchXML = resultXml.selectSingleNode("//q1:fetchxml").nodeTypedValue;

}

}

else {

if (window.top != null && window.top.resultRender != null && window.top.resultRender.FetchXml != null) {

fetchXML = window.top.resultRender.FetchXml.value;

}

}

if (fetchXML.length > 0) {

fetchXML = fetchXML.replace(/\"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");

// Fire FetchXML and get the totals

var queryForTotals = "<?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>" +

"<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'><fetchXml>" +

fetchXML +

"</fetchXml></Fetch>" +

"</soap:Body>" +

"</soap:Envelope>";


xHReq = new ActiveXObject("Msxml2.XMLHTTP");

xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);

xHReq.setRequestHeader("SOAPAction", http://schemas.microsoft.com/crm/2007/WebServices/Fetch);

xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");

xHReq.setRequestHeader("Content-Length", queryForTotals.length);

xHReq.send(queryForTotals);


// Capture the result.

var resultXmlForFetch = xHReq.responseXML;

// Capture the result and UnEncode it.

var resultSet = new String();

resultSet = resultXmlForFetch.text;

resultSet.replace('&lt;', '<');

resultSet.replace('&gt;', '>');


// Create an XML document that you can parse.

var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM");

oXmlDoc.async = false;

// Load the XML document that has the UnEncoded results.

oXmlDoc.loadXML(resultSet);

// Display the results.

var results = oXmlDoc.getElementsByTagName('result');


TotalRecordsMessage = '--> Total Records = ' + results.length;

//alert(TotalRecordsMessage);


}

}


The purpose of the script is to track the id of the current view, fire an XMLHTTP request (call the CRM web service through javascript), parse the results and store the details in local variables. For example, if the current view is "Active Contacts", then the script determines the ID of the view, fires an XML HTTP request to query for the total number of records related to the current view. After we have the total number of records stored in a local variable, we then have to update the status bar of the table to present this detail.

Advantages :
The biggest advantage of this solution is that it doesn't require you to register a plug-in. All that one needs to know is to place the grid.htc file @ the appropriate location and ask the users of CRM to do a Ctrl+F5 (in order to download the latest scripts and CSS from the server). As you can see, the procedure to install this solution is its highlight.

Limitations:
  1. The solution doesn't work for quick find and look ups. It only works for saved views. Since the solution works in "Advanced Find" too, a workaround for the limitation is to use "Advanced Find" if you require totals.

  2. Since the solution is a change to a globally referenced script, all the org. units will get this detail (whether you like it or not :()

  3. Since we are modifying a script that comes along with CRM installation, I am not sure if it works seamlessly when you move to a higher version of CRM !
I have presented two possible solutions for "CRM Record counter" and I leave this to your discretion to pick a solution that better addresses your requirements :)

Download the script from here and please feel free to comment.

Saturday 10 April 2010

Hot Summer .. Cool moments ...

Last week, I took a three day long vacation and went to my home town (may be I should say "ex-home-town") on the occasion of one of my cousin's thread ceremony.
It feels good to go back in time and recollect those good old days, meet childhood friends. I got a chance to meet one of my teachers who taught us @ school and it was really a very good experience.

Here are a couple of photos that I captured during my visit to my home-town.

Tuesday 30 March 2010

Talent vs Attitude


The other day, my wife and I were having a stroll in a park near my home and were talking about "talent" and "attitude".

From my experience in IT, I feel that it is very important to have an attitude to solve a problem than talent.

I had worked with many developers - some of them were born talented but got relaxed over time and I find it very difficult to work with such people. On the other hand, there were developers that never bothered about the type of work but always have enjoyed the work. This set of people derive motivation from the work that they do and always try out newer approaches even while performing a repetitious task.

When I interview candidates for my projects, I always look for people that have an attitude to work over talent and this decision helped me survive with reasonable amount of success.





Needless to say - Talented people with an attitude to work is definitely a perfect choice at work :-)

Thursday 18 March 2010

Background process in ASP.NET ...

A few months ago, I came across an interesting technique in ASP.NET to simulate background processes @ http://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/. I have tried this approach in a couple of my projects and it works. I would like to extend the post at this link with my experiences after implementing this technique in a real-time project.

Firstly, thanks to whoever came up with this interesting idea. It works for sure but with limitations.
Let me just copy and paste the code snippet from the above url for reference purposes:

private static CacheItemRemovedCallback OnCacheRemove = null;

protected void Application_Start(object sender, EventArgs e)
{
AddTask("DoStuff", 60);
}

private void AddTask(string name, int seconds)
{
OnCacheRemove = new CacheItemRemovedCallback(CacheItemRemoved);
HttpRuntime.Cache.Insert(name, seconds, null,
DateTime.Now.AddSeconds(seconds), Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable, OnCacheRemove);
}

public void CacheItemRemoved(string k, object v, CacheItemRemovedReason r)
{
// do stuff here if it matches our taskname, like WebRequest
// re-add our task so it recurs
AddTask(k, Convert.ToInt32(v));
}
  1. We can have only one background process per web application (ref : CacheItemRemoved). If you have a requirement to perform two tasks at different intervals of time (say one at every 10 mins and the other at every one hour), then the approach that I propose is to have the background process wake up at regular intervals of time (say 10 sec) and track the timestamp when a particular process executed recently. This is more of an implementation level logic and I hope you got the idea :-)
  2. As a good coding practice, one would avoid inline business logic (in the CacheItemRemoved) but cache a business object and invoke a method on the business object at regular intervals (in the CacheItemRemoved method). Beware that you might run into a situation where your business object got garbage collected (eg: the process that runs every one hour in my example above) and your background process ends up doing nothing !! To solve this problem, I propose to create a blank file on the file system (preferably in the root directory of your web app) and the existance of this blank file indicates that the background process should be kept alive. So if you perform a null check on the cached business object and you find that it is "null", just instantiate and re-insert the new object in cache so that your background process works as expected :-)

The fundamental approach is awesome and I hope my experiences help developers solve a couple of practical problems with the approach :-)

Cheers.

Sunday 7 February 2010

Struts.NET = ASP.NET MVC ?



These days, I am reading a bit about ASP.NET MVC and it looks like Microsoft is trying to implement Java Struts !

Does it mean that it took so long for .NET guys to understand Struts and get ASP.NET support such a paradigm?

Let's not get into a Java vs .NET debate but see the similarities and differences between ASP.NET MVC and Java Struts:
  • To start with, an ASP.NET form expects/mandates only one form tag but with MVC now, you can have any number of form tags in an ASPx page. You could have any number of form tags in a JSP page.
  • The code-behind event handlers used to act as controllers in a standard ASP.NET web application. In Struts, an Action class acts as a controller which means that any submit action on a page is associated with an Action class. ASP.NET MVC is following the Struts Action controller approach now.
  • ASP.NET MVC takes very good advantage of LinQ-SQL. VS 2008 is made so powerful that it eases the life of a developer by generating lot of useful code. On the other hand, although there are OR class libraries like Hibernate, the IDEs (the ones that I used so far) generate very little code and thus make you write a lot of code !
  • ASP.NET MVC is convention based, meaning that it expects the controllers/view pages and the model to follow a consistent naming convention (although, this is not mandatory and not that restrictive too). In Struts, the key is Struts-config.xml that associates a view and a controller.

So far, these are some of my observations on Struts and ASP.NET MVC. Let me make my hands dirty with ASP.NET MVC and then talk more about it after I achieve a level of comfort :-)