When you code webparts, you really design it carefully and avoid use/expect/demand absolute url in webparts configuration settings, this will make moving from the site from environment to environment less painful and also make it work with AAM.
Here is one example:
A webpart that is used to output a few links stored in a list so one of the settings the webpart needs is the siteUrl, which is where the location of the list.
site = new SPSite(siteURL);
web = site.OpenWeb();
SPList spList = web.Lists(ListName);
foreach (SPListItem li in spList.Items)
{
//construct the link from one of the column with URL data type
}
If you have AAM on this site and you are using the absolute path for siteURL then every access URL will produce link with that hardcoded siteURL. The right way of doing this is to use relative path for siteUrl and you would get the base address from System.Web.HttpContext.Current and insert the base url in front of relative siteURL.
· You have basic knowledge of AD, MOSS and NLB
· Windows 2003 Domain name: contoso.com
· SQL cluster already installed
· 3 servers with Windows 2008 Server installed, Web1 and Web2 has 2 NICs installed and connected to different subnet
· You have a Domain Admin account
· 2 Web front servers (Web1 and Web2)
· 1 Index Server (Idx1)
· NLB SharePoint farm will be accessed as http://sharepoint
· Following domain accounts used for SharePoint have been created:
o Contoso\MOSSPortal
o Contoso\MOSSSearch
o Contoso\MOSSMysite
o Contoso\MOSSAdmin
o Contoso\MOSSSsp
Make sure all 3 Windows 2008 servers have the following roles added:
· Application Server Foundation
· Web Server (IIS) Support
· Windows Process Activation Service Support (optional – useful if this server will host WCF service)
· Distributed Transactions (optional for MOSS but install it in case some WebPart code need to use distributed transaction)

This is a very straight forward process and we need to make sure the NLB cluster is functioning before we install MOSS on it. Here is the step by step configuration process:
· Logon to Web1, make sure the both NIC configured correctly by pinging Web2 (this should resolve 172.168.8.57 in my case), also make sure you can ping Web2 using another IP (in my case it’s 192.168.10.2)
· Logon to Web2, make sure the both NIC configured correctly by pinging Web1 (this should resolve 172.168.8.56 in my case), also make sure you can ping Web1 using another IP (in my case it’s 192.168.10.1)
· Logon to Web1, from command window, type nlbmgr, right click the Network Load Balancing Clusters and select “New Cluster”, specify a host to connect to and select the interface with IP 172.16.8.56, select unicast because we have two NICs here (you need to use multicast if you have only one NIC), make sure to specify your cluster IP (in my case it’s 172.16.8.58) and full internet name as sharepoint.contoso.com





· Add Web2 into the cluster and you should see something similar to the following after NLB configured successfully
Before we install MOSS to Web1 and Web2, we need to make sure the NLB is functioning as expected and here are steps to do the test.
· Logon Web1
o Create a folder to host our test website C:\WebSites\SharePointPortalSite, create a test.htm file in this folder, enter <h1>Content on Web1</h> in test.htm
o Create a website using IIS manager point to the above created folder, make sure this website is bound to the cluster IP only and cluster name as host header (in my case it’s 172.16.8.58 and sharepoint)

o Select DefaultAppPool as application pool
· Logon Web2, repeat the above steps but edit the test.htm to change the content to “Content on Web2”
· Now logon to the index server, type http://sharepoint, you should see either “Content on Web1” or “Content on Web2”
· Keep testing by shutting down Web1 while Web2 is on
· Keep testing by shutting down Web2 while Web2 is on
We have successfully tested the NLB cluster. You might get authentication error if you access the http://sharepoint from either Web1 or Web2, if so, please following this link to see the workaround (Disableloopbackcheck): http://support.microsoft.com/default.aspx/kb/926642
· Install MOSS bits on all 3 servers in the following order:
o MOSS bits
o MOSS sp1 bits
o MOSS infrastructure update bits
· Logon Web1
o Run “SharePoint Products and Technologies Configuration Wizard” to create your farm
o Make sure to select “Central Admin” will run on this machine
· Logon Web2
o Run “SharePoint Products and Technologies Configuration Wizard” to join existing farm
o Make sure to select “Central Admin” will run on this machine
· Logon Idx1
o Run “SharePoint Products and Technologies Configuration Wizard” to join existing farm
o Make sure to select “Central Admin” will run on this machine
· Test on all 3 servers and make sure the Central Admin site comes up
· Logon to Index server
· Start Office SharePoint Server Search service
· Start Windows SharePoint Services Help Search services

· Create an application http://sharepoint:5100 to host SSP, app pool identity: Contoso\MOSSMysite
· Create an application http://sharepoint:5200 to host MySite, app pool identity: Contoso\MOSSSsp
· Make sure to the correct index server, in my case IDX1
· Plan the index capacity and make sure allocate enough space to host index
· Create new SSP
· Configure Search Settings
· Installed 64bit IFilter on Index Server (http://www.foxitsoftware.com/pdf/ifilter/ )
To make Kerberos authentication work with SharePoint portal http://sharepoint we need to make sure that both web servers are configured to be trusted for delegation and needed Service Principals are registered.
Launch Active Directory Users and Computers MMC, find the Web1 and Web2 server, and double click to go to the properties, at Delegation tab, select “Trust this computer for delegation to any service (Kerberos only)

Find the domain user account contoso\mossportal which will be used as identity of the application pool for our SharePoint portal site and double click to go to properties page, go to Delegation tab, select “Trust this user for delegation to any service (Kerberos only)

Here are commands we need to run on a machine that has SETSPN.exe (which is included in Windows 2003 Server media as Support.CAB):
Setspn –a http/sharepoint contoso\mossportal
Setspn –a http/sharepoint.contoso.com contoso\mossportal
· Logon to Web1, launch Central Admin site, create new application, make sure to check “using an existing IIS website” and select the website we tested the NLB in above steps, in my case, http://sharepoint
· Make sure to choose “Kerberos” as authentication provider and supply all other necessary information to create a web application
· Create the root site collection and select one site template meet your need
· Wait for few minutes to make sure the site provisioning job completed and changes will be made automatically on Web2 server
· Logon to a workstation, you should be able to access the SharePoint site by the NLB url http://sharepoint
Server virtualization technology has been adopted by more and more companies and some network engineers do not follow Microsoft guideline/best practices to create virtual machine hard disks by simply copy the virtual hard disks and then rename the machine name. This is not supported way of duplicating virtual machine.
I spent fair amount of hours to identify one of the MSDTC issue for my client on one of their virtual machines. I spot the issue until I used the DTCPing tool and the ping is successful but when you look at the log file, it contains some information like following:
WARNING:the CID values for both test machines are the same
while this problem won't stop DTCping test, MSDTC will fail for this
After I fixed the duplicate CID issue followed the instructions number 11 in http://support.microsoft.com/kb/306843 MSDTC worked correctly.
I was trying to validate an instance of X12 4010 867 file in VS.NET 2005 IDE and could not get it work correctly using the schema shipped with BizTalk Server 2006 R2. After several tries and struggles, Here is what I did to make it work:
1. You need to remove the ISA/GS ... GE/IEA from the instance document.
2. If the Segment teminator is ^, which is the default for Repetition Separator then you need to fake out an repetition separator which is not inside your edi file and then specify the ^ as your segment terminator.
If you ever need to send out a link of InfoPath form in email and want the link to open the form in browser instead of popup the xml in open/save dialog ... here is the trick:
send the link as following:
siteUrl/_layouts/FormServer.aspx?xmlLocation=InfoPathFormUrl?DefaultItemOpen=1
make sure the InfoPath form Url is UrlEncoded.
You need to set BDC permission at application level AND also entity level otherwise you would spend lots of time figuring out why you got error like 'You do not have permission to access ...' or 'Unable to connect to ... '
Recently I had to move all databases including the content database in a MOSS 2007 farm from one SQL server to another. I tried few methods and found the following steps are the easiest:
[Assumption: All databases in the farm are on one SQL server and the SQL server was not assigned to any other roles]
- Quiesce the farm ... make sure no one is accessing the farm
- List all databases in the farm (http://blogs.msdn.com/johnlee/archive/2008/03/09/list-all-databases-in-a-moss-2007-farm.aspx)
- Detach all databases from the above list
- Copy all databases to another SQL server and attach all of them
- Go to one of Web Front End (WFE) server
- Launch regedit and navigate to the following key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0\Secure\ConfigDB, double click the dsn and change the Data Source from the original SQL Server name to new SQL Server name
- Run stsadm.exe -o renameserver -oldservername <OldSQLServerName> -newservername <NewSQLServerName>
- Repeat step 5 on all WFE and other servers except the SQL server
- Reboot all servers except the SQL server
[Assumption: WSS/MOSS was installed on a single box - the new SQL server name will be "NewSQL"]
- Introduce the new SQL server ("NewSQL") into this farm by creating a web application pointed to the new SQL Server
- Quiesce the farm ... make sure no one is accessing the farm
- List all databases in the farm (http://blogs.msdn.com/johnlee/archive/2008/03/09/list-all-databases-in-a-moss-2007-farm.aspx)
- Detach all databases from the above list
- Copy all databases to a NewSQL server and attach all of them
- Open SSMS, pointed to configuration database, run the following script to get the Id for the "NewSQL" and record the ID, let's refer it as "InstanceID_GUID"
select ID from objects
where parentid = (
select Id from objects
where name = 'NewSQL')
- Since all roles are assigned to single server so I could not use stsadm.exe renameserver command to achieve the same goal. What I did is to run the following script to update the parentID of all databases to above instance Guid
UPDATE objects
SET parentid = 'InstanceID_GUID'
WHERE id IN
(
SELECT o.id
FROM objects o
INNER JOIN classes c on c.id = o.classid
WHERE c.Fullname LIKE '%Administration.SPConfigurationDatabase%'
OR c.Fullname LIKE '%Administration.SPContentDatabase%'
)
- Go to one of Web Front End (WFE) server
- Launch regedit and navigate to the following key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0\Secure\ConfigDB, double click the dsn and change the Data Source from the original SQL Server name to new SQL Server name
- Repeat step 9 on all WFE and other servers except the SQL server
- Reboot all servers except the SQL server
- Change the "Default database server" from Central Administration to "NewSQL"
[Disclaimer] The methods stated above are just how I did and provided as is for informational purpose only and I have not tested them in production environment and do not know if they are supported methods.
Run the following script in the content database:
SELECT o.[Name] AS 'DatabaseName',
Instance.[Name] AS 'DatabaseInstance',
[Server].[Name] AS 'DatabaseServer'
FROM Objects AS o
INNER JOIN classes c on c.id = o.classid
LEFT JOIN Objects AS Instance ON o.ParentId = Instance.Id
LEFT JOIN [Objects] AS [Server] ON Instance.ParentId = [Server].Id
WHERE c.Fullname LIKE '%Administration.SPConfigurationDatabase%'
OR c.Fullname LIKE '%Administration.SPContentDatabase%'
OR c.Fullname LIKE '%Administration.SharedDatabase%'
OR c.Fullname LIKE '%Administration.SearchSharedDatabase%'
This is something off topic ... some experience in Italy.
When you purchase certain amount of item in European countries you are entitled to get the 12% VAT tax refund when you leave the last European country ... sounds terrific idea, right?
In reality, it's very hard to get it because you can only do it at the airport where you are leaving Europe, you need to get the receipt stamped at the custom with your original item checked, you might need to find out where is the refund counter and most importantly, you might not have enough time between your international flights ...
My wife and I enjoyed our Christmas vacation in Venice and Rome and we did buy some items ... we could not get the tax refund a Rome airport and we can only get the receipt stamped in Frankfurt airport ... our flight delayed so we have barely 1.5 hours, when we got there we asked where the tax refund office was and we got different answers from different people so we just walked around with all of baggage ... from terminal A to terminal B and finally found the custom office for tax refund ... I saw one lady came out of the custom office in tears and I asked her what happened, she told me that it took her more than one hour to find this custom office and she almost gave up the tax refund!!!
We managed to get our refund and get on our airplane back SFO ... It's been a while I travel outside US and when I got home I felt I do not have any complaint anymore, home sweet home and US is the best country to live in.
Previously, server based BizTalk adapter was used to access MQ Series and that requires MQ series server on Windows as intermediate server between BizTalk Server and non-Windows Queue Managers. In some situation, when installing the MQAgent to Windows MQ Series box is not an option the client based BizTalk adapter (MQSC) is needed to achieve the same goal.
MQSC is not part of BizTalk bits but it can be found in Host Integration Server 2006.
While setting up MQSC adapter, here are some of the tips when authorization issues occurred (Thanks Tom Canter on this):
1. If MQSeries server box and BizTalk Server box are in the same domain
- Configure the MQSC receive/send adapter using a host instance with domain account as logon account
- Add this domain account to domain security group: "<YourDomainName>\Domain MQM"
2. If MQSeries server box and BizTalk Server box are NOT member server of any domain
- Create a username on both BizTalk Server box and MQ Series server box with identical username and password
- Add the above created account into local group "mqm" on BizTalk server and MQSeries server
- Configure the MQSC receive/send adapter using a host instance with the above created username as logon account
This is the first time I experience this type mandatory evacuation ... around 6AM this morning, reverse 911 call notified us to evacuate immediately.
Sitting in a corp. office at High Bluff Drive offered by my former boss, listening to the AM 600, surfing the net to check the fire conditions ... air condition is pretty bad and we can smell the smoke inside the office.
There is 50% chance that my home was burned but we are safe, my dog is with us and our two birds are with us too ... but I felt so sorry that I left my 4 fishes at home.
When I use the object model to add item to MOSS 2007 project task list, I have some code like following:
const string SitePath = "http://litwareserver:5000";
const string ListName = "ProjectTask";
// enter object model through site collection.
SPSite siteCollection = new SPSite(SitePath);
// obtain reference to top-level site.
SPWeb site = siteCollection.RootWeb;
site.AllowUnsafeUpdates = true; //needed since my code will be running as web service
SPList myList = site.Lists[ListName];
if (myList != null)
{
SPListItem item = myList.Items.Add();
item["Title"] = "test item";
item["PercentageComplete"] = 0.2; // 20%
item.Update();
}
// clean up by calling Dispose.
site.Dispose();
siteCollection.RootWeb.Dispose();
siteCollection.Dispose();
I got the "value does not fall within the expected range." error
By looking at this error message, my first reaction was to check the percentage complete column, it has min (0) and max (1) defined, 0.2 shall be within the range, anyway, I tried 20, 0, 1 and lots of other values, same error;
Then I wanted to reproduce it with a custom list so I created a new list with a field with min/max values defined, I could add items with values inside the range and also outside the range.
I could not reproduce it with custom list. What could be the issue? What's the difference between my custom list and the project task list? Is it possible the field (% complete) in project task list was defined in the content type?
Then I created a content type that has a field with min/max values defined and attached this content type to a new list, removed the original item content type from the list, tested it again, I could add item to the new list with values inside or outside the defined range. So it's not because it's defined in content type ... this made me wonder what could be the problem???
I decided to print out the internal name and other properties of each fields and found out the internal name of field (% complete) is "percentComplete" instead of "percentageComplete" - I fixed the problem right away.
What I learned from this debugging process?
1. Do not assume anything.
2. Do not be fooled by error message.
3. Product error message should contain meaningful and specific information to help identifying the problem.
The error message should be the classic "index is out of range" or even better "Cannot find the field name '{0}'"
While developing WinForm application, if you need to spawn multiple threads to work on different tasks and you need to wait all threads to complete ... then you might think to use the following approach:
>>> Create an array of AutoResetEvent; use ThreadPool.QueueUserWorkerItem to spawn each task in different thread; then call WaitHandle.WaitAll(AutoResetEvent[]) to wait all threads to complete the task
WinForm app is attributed as [STAThread] and you will get "WaitAll For Multiple Handles on a STA Thread Is Not Supported" error ... what's the solution? Here is what I used for this scenario:
>>> Create a ManualResetEvent; maintain a TaskCounter; use ThreadPool.QueueUserWorkerItem to spawn each task in different thread; Call ManualResetEvent.WaitOne(); Decrement the TaskCounter after each task is completed and Call ManualResetEvent.Set() when the TaskCounter == 0
Special notes: In your DoTask(object stateInfo), you need to handle exception properly and make sure the TaskCounter is subtracted should there is an exception
Symptom:
If you have a public facing site built on MOSS 2007 you normally would have two zones defined: default zone that is your local intranet and Internet zone that's your public facing zone. In Alternate Access Mapping, you would put something like http://servername in the default zone and put http://www.publicdomainname.com in your Internet zone; then you would create a content source to crawl your site as http://servername to if you want all documents stored in list to be crawled. This works very well but there is one area that the internal URL will be shown:
Enter something in the search box, click search, on the search result page, if you have RSS turned on, you would see an RSS link to allow you to subscribe the search result feed. Click on the RSS link icon, you would see all URLs are shown as internal http://servername
This is a bug - because the specific WebPart always get URL from default zone.
Workaround:
Go to Alternate Access Mapping, public the public facing URL into the default zone and put your local intranet URL into custom zone.
class Program
{
static void Main()
{
string sitePath = "http://litwareinc.com";
// enter object model through site collection.
SPSite siteCollection = new SPSite(sitePath);
// obtain reference to top-level site.
SPWeb site = siteCollection.RootWeb;
SPWeb site1 = site; //this actually create a copy of site, I will have followup blog on this after I find out how the assignment of an reference type got a copy instead of a pointer
site1.Dispose(); //this does not affect site
// enumerate through lists of site
foreach (SPList list in site.Lists)
{
Console.WriteLine(list.Title);
}
// clean up by calling Dispose.
site.Dispose();
siteCollection.RootWeb.Dispose();
siteCollection.Dispose();
}
}