Teach kids to love, not to kill!

Below explains why Jimmy Kimmel has a big evil in his heart.
What a Journey
Chunyan Li, Ph.D.

Dear all,

How long does it take us to get here today? One hour? Two hours? For me, it is 20 years! 20 years ago I arrived at this land of the free, full of ideals, perhaps from listening too much to radio Voice of America.  20 years later my sons join me to protest against ABC's spreading of racial hatred and violence. What is wrong with this picture?

I ask my teenage son whether we should just treat "killing Chinese" as a joke. He said this demonstrates what the society and media had been feeding our young kids. My heart sank. Is America moving forward or backward?

Now I ask again: How long does it take us to get here? I don't know about you, but the last 20 days seem longer than my 20 years. Those of us who stand here today must have been reading, writing, arguing, and agonizing over confusing pieces of reports and commentaries. And we ask ourselves, are we being unreasonable? Is our English that bad that we don't know what an apology is? 

On 10/28, the night of our first protest, Mr. Kimmel said, "today is a weird day...I am sorry if I offended anyone." I ask ABC, who is the weird one? Who truly apologizes if he doesn't even say sorry for what? Sorry to whom? To the Chinese whose sense of tranquility is forever disturbed? To our kids whom we try so hard to shield from pain and harm?  To the schools that teach kids the spirit of Dr. Martin Luther King? ABC, do you call that an apology? 

In a letter to 80-20, ABC still claims it only entertains. Why is joking about killing Chinese so entertaining? Is it because we are always so nice? My friend's sweet 11-year old wants to write on his poster, "do not take advantage of us." I first didn't understand, then found out he meant "don't take advantage of us because we are nice." That broke my heart!!! ABC and Mr. Kimmel, we hold you accountable for taking away our children's precious sense of security. And we hold you accountable for sowing the seeds of hatred in other children's minds.

On 10/29, Mr. Kimmel even resorted to the ridiculous notion of cultural difference. I ask him, what cultural difference do you mean? A culture of hard work, respect, harmony versus a culture of violence and racial hatred? Then I admit there is an unbridgeable difference between Kimmel and us. We know what's right, what's wrong. And we know what humor is too! Yet we have no tolerance of your malicious joke, subsequent insult on air, then the absurd reference to culture. This only proves Mr. Kimmel you are unfit to be a public figure. 

ABC, stop blaming cultural differences for your own mistakes! Except for Native Americans, all are immigrants with unique traits. Yet we share the basic value of human dignity. Stop magnifying the differences in skin colors, or accents!

I ask a third time, how long does it take us to get here? Our friends started telling us there is another written apology somewhere. Who in real life is given so many third chances? Yet I ponder, are we asking too much? I discovered to my dismay that in that written letter, Mr. Kimmel is still hiding behind the 6-year old. Come on Mr. Kimmel, you think our anger is on a 6-year old? Our anger is on your malicious question "Should we allow the Chinese to live?" Our anger is on your subsequent arrogant ridicule. Our rage is on ABC for not letting the public know the truth of these so called apologies. 

America is the home of the brave. ABC, show our kids you have the courage of admitting wrong doing, show our kids you have the guts to take responsibility by dismissing Mr. Kimmel. Show us you have the resolve to institute procedures to prevent future incidences. ABC, do NOT hide behind a 6-year old!  Show us what a major network can do to right its wrong!

Twenty years, twenty days. Life, liberty and the pursuit of happiness. Who doesn't want to be happy? But without life, what is liberty? Without liberty, what is happiness? Let's join hands in our pursuit of human dignity. 

Teach kids to love, not to kill!

jQuery UI Dialog and ASP.NET PostBack

I am replacing MS ModalPopup with jQuery UI Dialog in my ASP.NET projects recently. It seemed easy in the beginning, but it turned out I had to search solutions on Internet.

Let me use a sample to explain what should be done to make jQuery Dialog work in ASP.NET page. The web page has input fields (Name and Address) and buttons (Save and Clone):

All the fields and buttons are inside an UpdatePanel. When Clone button is clicked, a Dialog will be displayed:

The Submit button is supposed to postback user's selection.

The JavaScript is like below:

        $(function () {

        function CreateCloneDialog() {
            // Clone dialog
              { autoOpen: false,
                modal: true,
                width: 'auto',
                height: 'auto',
                resizable: false

        function DisplayCloneDialog() {
            // Open the dialog

The pnlSectionsToClone is the content panel of the Dialog. When Clone button is clicked, function DisplayCloneDialog() will be called. Everything seems simple so far. But the page had these problems:

1. When the page is loaded and Clone button is clicked, the Dialog is displayed with no problem. But once the Save button is clicked, the Clone button will throw this exception (in Firefox) and no Dialog is displayed:

Error: cannot call methods on dialog prior to initialization; attempted to call method 'open'

The reason of the exception is: after Save button is clicked, the UpdatePanel receives HTTP response and updates DOM in browser. The Dialog object in CreateCloneDialog() is removed from DOM at this time. The following 'open' will fail.

The solution for this issue is to call CreateCloneDialog() after each postback:

        $(function () {

2. When the Dialog is displayed, the Submit button does NOT postback.

The reason for this issue is: the dialog object is added to the Document in DOM, not inside Form tag. You can see that clearly in Firebug:

To move the dialog object into Form tag for postback, you need add one extra line of code to function CreateCloneDialog():

        function CreateCloneDialog() {
            // Clone dialog
              { autoOpen: false,
                modal: true,
                width: 'auto',
                height: 'auto',
                resizable: false

            // Need add this line below, otherwise the buttons 
            // inside dialog will not postback

3. In Firefox or IE8+, the dialog layout looks OK; but in IE7 (yes, many people are still using IE7), the dialog title looks like this:

This is because IE7 does not understand CSS style width:'auto'. To fix this issue, we need to manually assign width for the title when the dialog is created:

        function CreateCloneDialog() {
            // Clone dialog
            $('#pnlSectionsToClone').dialog({ autoOpen: false,
                modal: true,
                width: 'auto',
                height: 'auto',
                resizable: false,
                open: function (event, ui) {
                    // fix for width:auto in IE7. If width is specified, 
                    // the logic below is not needed.
                    var contentWidth = $(this).width();
                    .each(function () {
                beforeClose: function (event, ui) {
                    //fix for width:auto in IE7
                    $(this).parent().css("width", "auto");

Now the dialog looks OK in IE7:

SQL Except statement with Order By

The Except statement is easy to understand. For example, the result of "result set A Except result set B" is like below in grey:
But thing will easily go wrong in SQL statement when you use Except and Order By.

Take AdventureWorks database as an example here:

1. Run the 1st statement and notice BusinessEntityID=66 is in the result:

2. Run the 2nd statement and notice BusinessEntityID=66 is also in the result:

3. How about combining those two SQL statements together with Except? From the concept, the record BusinessEntityID=66 should not be in the result, right? Let's take a look:

It is still there! Why?!

The reason is: Order By actually belongs to the first Select statement, not the 2nd.

This is the result set for the 2nd statement without Order By:

The record BusinessEntityID=66 is NOT in the result. So the result of Except statement is actually correct.

Migrating ASP.NET applications to another Windows Domain

You may have to migrate Intranet software applications to another Windows Domain (e.g. Due to company restructure). As Intranet applications normally uses Domain account to authorize user access, changing domain means you have to change user group settings, sometimes source code and sometimes even worse your third-party applications will not work properly -- e.g. old data uses old domain account so that user cannot change their data using new domain account.

If your applications were based on ASP.NET framework, then there is a simple solution to ease the pain at least temporarily: you keep all those ASP.NET applications in original domain and add a HttpModule (defined in web.config) to map new domain account to old domain account. When users in new domain access those applications in old domain, the ASP.NET application will only see the old domain account so the users still work in the exact same way as before.

The main code logic for domain mapping HttpModule is as below:

// Get user's domain account
string strDomainUserAccount = HttpContext.Current.User.Identity.Name.ToUpper();

// Remove domain name from the account
string strUserAccount = strDomainUserAccount;
if (strDomainUserAccount.StartsWith("NewDomain\\")) {
 strUserAccount = strUserAccount.Substring(10);

// Fetch cached user mappings
Dictionary userMap = (Dictionary)HttpRuntime.Cache(USER_CACHE_KEY);
if (userMap == null) {
 // If no cache is available, reload the cache from database 
 // and try to get the item again
 userMap = (Dictionary)HttpRuntime.Cache(USER_CACHE_KEY);
 if (userMap == null) {
  // Throw exception here!!

// Get user's NTUserName from the mapping in cache 
// and create a new user for Http context
if (userMap.ContainsKey(strUserAccount)) {
 string strNewAccount = userMap[strUserAccount];
 if (!string.IsNullOrEmpty(strNewAccount)) {
  // New Identity
  string strAuthenticateType = HttpContext.Current.User.Identity.AuthenticationType;
  System.Security.Principal.GenericIdentity newID = 
   new System.Security.Principal.GenericIdentity(strNewAccount, strAuthenticateType);
  // Get cached roles in mapped domain. 
  // If no cache is available, get from AD and put into cache
  string[] strRoles = this.GetDomainGroupsForUserFromCache[strNewAccount];
  System.Security.Principal.GenericPrincipal newP = 
   new System.Security.Principal.GenericPrincipal(newID, strRoles);
  HttpContext.Current.User = newP;

FlowLayoutPanel for .NET Compact Framework

It is quite complex to arrange Windows Form layout dynamically in .NET Compact Framework (CF) when you try to show/hide a control between other controls. Although you can use FlowLayoutPanel in desktop Windows Form application, there is no such kind of layout manager for CF.

I found an old article on CodeProject about FlowLayoutPanel, but that code cannot work on CF because CF Panel does not have OnLayout event. Also the code logic has bugs too.

After searching for a while, I decided to make a FlowLayoutPanel for CF by myself. It turns out the code is not hard to write:

Protected Overloads Overrides Sub OnPaint(ByVal pEvent As PaintEventArgs)
 Dim nextTop As Integer = 0, nextLeft As Integer = 0
 Dim maxHeight As Integer = 0
 Dim ParentWidth As Integer
 If Me.Parent IsNot Nothing Then
  ParentWidth = Me.Parent.Width
  ParentWidth = Me.Width
 End If

 ' Modify control location for the layout
 For Each myControl As Control In Me.Controls
  ' Ignore invisible controls
  If myControl.Visible = False Then
   Continue For
  End If

  If (nextLeft + myControl.Width) > ParentWidth Then
   nextTop += maxHeight
   nextLeft = 0
   ' Reset maxHeight
   maxHeight = 0
  End If

  myControl.Top = nextTop
  myControl.Left = nextLeft

  If myControl.Height > maxHeight Then
   maxHeight = myControl.Height
  End If

  nextLeft += myControl.Width
 Me.AutoScrollPosition = New System.Drawing.Point(0, 0)

End Sub

Below is a sample using the logic above:

To make layout easier, I put several panels inside the FlowLayoutPanel. You can see all the controls are arranged properly. When I click the "Hide Panel 3" button, the Form becomes this:

The controls below Panel 3 are moved up automatically. If I click "Show Panel 3" button, the Panel 3 will be displayed at original place and all the controls below it are moved down accordingly.

The code for the button is like below:

Private Sub btnHide_Click(ByVal sender As Object, ByVal e As EventArgs)
 If btnHide.Text = "Hide Panel 3" Then
  Panel3.Visible = False
  btnHide.Text = "Show Panel 3"
  Panel3.Visible = True
  btnHide.Text = "Hide Panel 3"
 End If

 ' Refresh the flow layout panel
End Sub

There is a small problem in Visual Studio though: when you drag-drop a control to the FlowLayoutPanel, Visual Studio always puts the control as the first one inside the FlowLayoutPanel. You need to modify the designer file to manually put those controls in order:


Set up Reporting Services in SharePoint Integration Mode

I followed How to: Configure SharePoint Integration on Multiple Servers step-by-step to configure SharePoint Integration on two servers. But on the step "Set server defaults" of "Configure the Report Server Integration Feature in SharePoint Central Administration", I only saw this error:

An unexpected error occurred while connecting to the report server. Verify that the report server is available and configured for SharePoint integrated mode.

I was sure I followed the steps carefully. Then I tried to look for error details in Web and Database log files, but there was no any error message. After many hours of investigation, I found somebody mentioned Kerberos/NTLM on MSDN Forum. As my environment did not allow me to use Kerberos, I tried to set up NTLM on both SharePoint server and Report server:

cscript adsutil.vbs set w3svc/NTAuthenticationProviders "NTLM"

Then I was so happy to see the error disappeared! :)

SSAS Permission for Least-Privilege User Account

When using LUA (Least-Privilege User Account) on development machine, I normally add myself as sysadmin for SQL server, so that I can create database freely.

As SSAS is part of SQL Server, I took it for granted that my account was also sysadmin in SSAS. But it turned out that SSAS has different permission settings from SQL Server.

When I tried to deploy an SSAS project, Visual Studio threw the error:

Error -1055391738 : Either the '[domain]\[account]' user does not have permission to create a new object in '[machine]', or the object does not exist.

So I went to SQL Server Management Studio and connected to SSAS, but I had no permission to create SSAS database manually:

Either the '[domain]\[account]' user does not have permission to create a new object in '[machine]', or the object does not exist. (Microsoft.AnalysisServices)

One way to solve the problem is to add my account to the SSAS' server role, which means to grant server-wide security privileges to my account in SSAS:
  1. Run SQL Server Management Studio as Administrator account
  2. Connect to the SSAS server
  3. Right-click the SSAS server name and select Properties from the popup menu
  4. Select Security to add account to server role

Now, I can deploy SSAS project in Visual Studio without problem.