Tuesday, June 24, 2008

MOSS 2007 Certification (70-542)


After my WSS Certificate yesterday, I passed the MOSS Developer exam (70-542) today with 969 points.
So now "I know everything" of InfoPath WebForms, the Business Data Catalog, Sharepoint Search, Audiences, Excel Web Access & Report Services, SharePoint Portals, Policies & Record Repositories, etc. etc... and have the MCTS: Microsoft Office SharePoint Server 2007 – Application Development certification.

Monday, June 23, 2008

WSS3.0 Certification (70-541)

WSS thumbnail I did my 70-541 certification exam today, and passed with a score of 972! Altough the exam was tough, I'm again in the books for the 70-542 (MOSS2007) exam, tomorrow mornig.

So what I've learned? Building webparts, features, site templates, workflows, custom fields, events, etc., etc....

What I've earned? Another MCTS on my CV ;)

Tuesday, June 17, 2008

Generics with Actions

An action is pointer to a function with no return value which you can use for working with generic lists. It looks like the Predicate functions, but the referenced method won't return a boolean value.

Still, the only parameter in the function is of the type of the generic. In this example, I extend the DateRange class with an Action.

An example of such an implementation:

/// <summary>
/// DateRange is a helper class to work with dates
/// </summary>
public class DateRange
{
private DateTime _startDate = DateTime.MinValue;
private DateTime _endDate = DateTime.MaxValue;

public DateTime StartDate
{
get { return _startDate; }
}
public DateTime EndDate
{
get { return _endDate; }
}

public DateRange(DateTime startDate, DateTime endDate)
{
_startDate = startDate;
_endDate = endDate;
}

/// <summary>
/// Gets the IsInPeriod method.
/// </summary>
/// <value>The in period.</value>
public Predicate<DateTime> InPeriod
{
get { return IsInPeriod; }
}

/// <summary>
/// Determines whether the specified date is in the period set by startdate and enddate.
/// </summary>
/// <param name="date">The date.</param>
/// <returns>
/// <c>true</c> if the specified date is in the period; otherwise, <c>false</c>.
/// </returns>
private bool IsInPeriod(DateTime date)
{
if ((date >= StartDate) && (date < EndDate))
{
return true;
}
else
{
return false;
}
}

/// <summary>
/// Gets the WriteNumberOfDaysFromStartDate method.
/// </summary>
/// <value>The write number of days till end.</value>
public Action<DateTime> WriteNumberOfDaysFromStart
{
get { return WriteNumberOfDaysFromStartDate; }
}
/// <summary>
/// Writes the number of days from the start date of the period.
/// </summary>
/// <param name="date">The date.</param>
private void WriteNumberOfDaysFromStartDate(DateTime date)
{
Console.WriteLine("{0} days to go", ((TimeSpan)(date - StartDate)).Days);
}
}


You can now use this conditional predicate with different Generic methods.

public class ActionExamples
{
public static void Main()
{
// fill an example list
List events = new List();
events.Add(DateTime.Now.AddDays(-1));
events.Add(DateTime.Now.AddDays(-2));
events.Add(DateTime.Now);
events.Add(DateTime.Now.AddDays(1));
events.Add(DateTime.Now.AddDays(2));
events.Add(DateTime.Now.AddDays(3));
events.Add(DateTime.Now.AddDays(4));

//create a DateRange object for tomorrow
DateRange nextMonth = new DateRange (new DateTime(2008,7,1), new DateTime(2008,7,31) )

//get events for next month
List nextMonthEvents = events.FindAll(nextMonth.InPeriod);

//write days from start of nextMonth to console
events.ForEach(nextMonth.WriteNumberOfDaysFromStart);
}
}

Monday, June 16, 2008

Generics with Predicates

A predicate is pointer to a boolean function which you can use for querying generic lists.
With these methods, coding against generic lists will be much easier. But one problem with these predicates is that the only parameter in the function is of the type of the generic. A solution for this is refactoring the predicate to a class, where you can provide these parameters to properties or the constructor.

An example of such an implementation:

/// <summary>
/// DateRange is a helper class to work with dates
/// </summary>
public class DateRange
{
private DateTime _startDate = DateTime.MinValue;
private DateTime _endDate = DateTime.MaxValue;

public DateTime StartDate
{
get { return _startDate; }
}
public DateTime EndDate
{
get { return _endDate; }
}

public DateRange(DateTime startDate, DateTime endDate)
{
_startDate = startDate;
_endDate = endDate;
}

/// <summary>
/// Gets the IsInPeriod method.
/// </summary>
/// <value>The in period.</value>
public Predicate<DateTime> InPeriod
{
get { return IsInPeriod; }
}

/// <summary>
/// Determines whether the specified date is in the period set by startdate and enddate.
/// </summary>
/// <param name="date">The date.</param>
/// <returns>
/// <c>true</c> if the specified date is in the period; otherwise, <c>false</c>.
/// </returns>
private bool IsInPeriod(DateTime date)
{
if ((date >= StartDate) && (date < EndDate))
{
return true;
}
else
{
return false;
}
}
}


You can now use this conditional predicate with different Generic methods.

public class PredicateExamples
{
public static void Main()
{
// fill an example list
List events = new List();
events.Add(DateTime.Now.AddDays(-1));
events.Add(DateTime.Now.AddDays(-2));
events.Add(DateTime.Now);
events.Add(DateTime.Now.AddDays(1));
events.Add(DateTime.Now.AddDays(2));
events.Add(DateTime.Now.AddDays(3));
events.Add(DateTime.Now.AddDays(4));

//create a DateRange object for tomorrow
DateRange tomorrow = new DateRange (DateTime.Today.AddDays(1), DateTime.Today.AddDays(2))

//get a boolean if there is an event tomorrow
bool tomorrowHasEvents = events.Exists(tomorrow.InPeriod);

//get events for tomorrow
List tomorrowEvents = events.FindAll(tomorrow.InPeriod);

//get first event for tomorrow (first item in list!)
DateTime tomorrowFirstEvent = events.Find(tomorrow.InPeriod);
//get last event for tomorrow
DateTime tomorrowLastEvent = events.FindLast(tomorrow.InPeriod);

//get the index of the first event for tomorrow
int tomorrowFirstEventIndex = events.FindIndex(tomorrow.InPeriod);
//get the index of the last event for tomorrow
int tomorrowLastEventIndex = events.FindLastIndex(tomorrow.InPeriod);

//remove all events for tomorrow
int removedItems = events.RemoveAll(tomorrow.InPeriod);

//are all events in the list tomorrow
bool allEventsTomorrow = events.TrueForAll(tomorrow.InPeriod);

}
}

Friday, June 13, 2008

.NET 2.0 = .NET 3.0 = .NET 3.5

Yesterday I had a discussion on an internal Microsoft Developers meeting about the status of .NET 3.5. We had presented that .NET 3.0 is an extension of .NET 2.0, and not a new version. My statement that the status of .NET 3.5 is the same, it is basically .NET 2.0 with extra features, was received sceptically.
Just to prove I'm right, here my observations.

Build a very simple .NET Console application, using two lines of code:

Console.WriteLine("Hello World");
Console.WriteLine("using version: {0}", System.Environment.Version.ToString());


With Visual Studio 2008, you can build this with all three version of the framework. And all three executables yields the same results: using version: 2.0.50727.1433.

ILDASM one of these assemblies gives the same assembly header:

// Metadata version: v2.0.50727
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 2:0:0:0
}


Proving all three versions are based on the .NET core version 2.0 (and this is the same version you should configure in ASP.NET. There is no v3 there).
Just to be sure if you want to upgrade to 3.5 at your customer to mention that version 3.5 also is basically .NET 2.0, with some extra features added. I won't say that the libraries of .NET 2.0 are the same with 3.5, but runtime versions are equal.

Rick Strahl blogged about this a while ago.

Friday, May 2, 2008

Unexpected SqlException...

On the production servers at the customer I'm currently working for, we had a strange error regarding a new feature in our reporting tools. A strange error, because during the development and test cycle of our release, we hadn't found any issues regarding SQL errors.

The error firing was 'The count aggregate operation cannot take a uniqueidentifier data type as an argument', during the execution of SELECT COUNT(Item.Id) AS ItemCount ... while the datatype of Item.Id was uniqueidentifier.

The only difference between our development, test and production servers is the version of MS SQL Server. While we migrated our dev and test servers to 2005, production was still behind on version 2000. And version 2000 can't run an aggregate operation on a uniqueidentifier, you need to run it with the asterisk instead (like COUNT(*) ).

Fortunately, migration to MS-SQL 2005 is already scheduled to happen soon :)

Thursday, April 3, 2008

Show your CC.NET dashboard in Sharepoint

I was wondering today if it's possible to get the webdashboard for CruiseControl.NET in Sharepoint. I didn't want to use an html scrapper, so I thought the XML webpart should be a good start. I needed the server status as XML, but found no reference in the helpfiles of CC.Net. On the other hand, Thoughtworks does have a page with information about a xml format for CI build status.
After inspecting the CCTray application and the logfiles on my server, I finally found the undocumented URL for this XML document with the status: http://buildserver.local/XmlServerReport.aspx.
After this quest for XML, I only needed an XSLT to get the results. So here it is:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" />

  <xsl:template match="/">

    <table border="0" cellpadding="0" cellspacing="0">

      <thead>

        <th style="text-align:left;">Project</th>

        <th>&#160;</th>

        <th style="text-align:left;">Status</th>

        <th>&#160;</th>

        <th style="text-align:left;">Last buildtime</th>

        <th>&#160;</th>

        <th style="text-align:left;">Buildlabel</th>

        <th>&#160;</th>

        <th style="text-align:left;">Activity</th>

      </thead>

      <tbody>

        <xsl:apply-templates select="/CruiseControl/Projects/Project"/>

      </tbody>

    </table>

  </xsl:template>

 

  <xsl:template match="Project">

    <tr>

      <td class="ms-vb" align="top" nowrap="nowrap">

        <xsl:element name="a">

          <xsl:attribute name="onfocus">OnLink(this)</xsl:attribute>

          <xsl:attribute name="href">

            <xsl:value-of select="@webUrl"/>

          </xsl:attribute>

          <xsl:attribute name="onclick">GoToLink(this);return false;</xsl:attribute>

          <xsl:attribute name="target">_self</xsl:attribute>

          <xsl:value-of select="@name"/>

        </xsl:element>

      </td>

      <td>&#160;</td>

      <xsl:element name="td">

        <xsl:attribute name="class">ms-vb</xsl:attribute>

        <xsl:attribute name="align">top</xsl:attribute>

        <xsl:attribute name="style">

          padding-bottom: 3px;

          <xsl:choose>

            <xsl:when test="@lastBuildStatus='Failed'">

              color:red;

            </xsl:when>

            <xsl:when test="@lastBuildStatus='Exception'">

              color:red;

            </xsl:when>

            <xsl:when test="@lastBuildStatus='Unknown'">

              color:yellow;

            </xsl:when>

            <xsl:when test="@lastBuildStatus='Failed'">

              color:red;

            </xsl:when>

            <xsl:otherwise>

              color:green;

            </xsl:otherwise>

          </xsl:choose>

        </xsl:attribute>

        <xsl:value-of select="@lastBuildStatus"/>

      </xsl:element>

      <td>&#160;</td>

      <td class="ms-vb" style="padding-bottom: 3px;" align="top">

        <xsl:value-of select="substring-before(@lastBuildTime,'T')"/>&#160;

        <xsl:value-of select="substring-before(substring-after(@lastBuildTime,'T'),'.')"/>

      </td>

      <td>&#160;</td>

      <td class="ms-vb" style="padding-bottom: 3px;text-align:right;" align="top">

        <xsl:value-of select="@lastBuildLabel"/>

      </td>

      <td>&#160;</td>

      <xsl:element name="td">

        <xsl:attribute name="class">ms-vb</xsl:attribute>

        <xsl:attribute name="align">top</xsl:attribute>

        <xsl:attribute name="style">

          padding-bottom: 3px;

          <xsl:choose>

            <xsl:when test="@activity='Building'">

              color:red;

            </xsl:when>

            <xsl:when test="@activity='CheckingModifications'">

              color:yellow;

            </xsl:when>

            <xsl:otherwise></xsl:otherwise>

          </xsl:choose>

        </xsl:attribute>

        <xsl:value-of select="@activity"/>

      </xsl:element>

    </tr>

  </xsl:template>

</xsl:stylesheet>

 



Now you need only to configure a custom XML Webpart to load the xml from your buildserver, set the XSLT and the title of the webpart.

Wednesday, March 26, 2008

C# test automation with Internet Explorer

As a follow up on my previous post, I had the idea you can do the same in managed code (in your test library, for instance). Although this won't exactly be a unit test (especialy with the example search form, it's more integration testing), it can be usefull in some scenarios.

You need a reference to two COM dll's,
MSHTML
Microsoft HTML Object Library
SHDocVw
Microsoft Internet Controls


After that, you can use the following example code inside your test (I present it here as one block. Some things can be put in test setup and teardown):

SHDocVw.InternetExplorer ie = new SHDocVw.InternetExplorerClass();
object missing = new object();
ie.Navigate("http://localhost/Default.aspx", ref missing, ref missing, ref missing, ref missing);
ie.Visible = true;
while (ie.Busy)
{
System.Threading.Thread.Sleep(500);
}
mshtml.HTMLDocumentClass doc = ie.Document as mshtml.HTMLDocumentClass;
doc.getElementById("searchbox").setAttribute("value", "test", 0);
doc.getElementById("searchsubmit").click();
while (ie.Busy)
{
System.Threading.Thread.Sleep(500);
}
string bodytext = doc.body.innerHTML;
Debug.Assert( bodytext.IndexOf("documents found")>0);

ie.Quit();

PowerShell Web UI test automation

Just read an article in MSDN magazine about test automation for Internet Explorer so you can test your web user interface. There are other tools for this (like Selenium), but those need quite a setup.

Execute a search-box test in PowerShell:

$ie = new-object -com "InternetExplorer.Application"
$ie.navigate("http://localhost/Default.aspx")
$ie.visible = $true
$doc = $ie.document
$doc.getElementByID("searchbox").value = "test"
$doc.getElementByID("searchsubmit").click()

Monday, March 17, 2008

Dutch 'Webrichtlijnen' and binary downloads

The Dutch Web Guidelines (a quality model for sustainable and accessible websites) describe what to do with downloads on your page. This isn't meant for HTML and images, but when you place Adobes Protable Document Format (pdf) or Rich Text Documents (rtf), those documents can be opened in your current window. It is possible to open those files in new windows (although the target="_blank" solution doesn't conform to XHTML validation, some solutions do exist , like rel="external" and JavaScript replacement). But this can be a browser window. Although you can configure your browser, the majority of users don't have the knowledge to tweak their browser to do what they want with these files.
The solution from the Webrichtlijnen:

Do not automatically open links to downloadable files in a new window.


Guideline R-pd.8.22


One way of getting this done is setting the HTTP Content-disposition header
By having the web server send an extra HTTP header for the downloadable file to the browser, the browser can decide whether to download this file untouched as an attachment to the visitor's hard disk.

The best way to do this is writing a custom HTTP Module which can check on served file extensions or MIME Types and add the content disposition header for configured downloads.
In this example implementation, I configure the file extensions in code, but you can always configure this in your config, database or any file.


public class ContentDispositionModule : IHttpModule
{
#region IHttpModule Members

///<summary>
///Initializes the module and prepares it to handle requests.
///</summary>
///<param name="context">An <see cref="T:System.Web.HttpApplication"></see>
///that provides access to the methods, properties, and events common
///to all application objects within an ASP.NET application </param>
public virtual void Init(HttpApplication context)
{
context.PreSendRequestHeaders += new EventHandler(SetContentDispositionHeader);
}

private static void SetContentDispositionHeader(object sender, EventArgs e)
{
string url = ((HttpApplication)sender).Context.Request.Path;
if (IsUrlPatternMatch(url))
{
((HttpApplication)sender).Context.Response.AppendHeader("Content-Disposition", "attachment");
}
}

///<summary>
///Disposes of the resources (other than memory) used by the module that implements
///<see cref="T:System.Web.IHttpModule"></see>.
///</summary>
public virtual void Dispose()
{
}

/// <summary>
/// Check the url against the binaryExtension patterns
/// </summary>
/// <param name="strUrl">url to check</param>
/// <returns>true if it matches the binaryExtension pattern</returns>
private static bool IsUrlPatternMatch(string strUrl)
{
// get this string from a more configurable place.
string binaryExtensions = "*.pdf,*.rtf";
if (!string.IsNullOrEmpty(binaryExtensions))
{
foreach (string extension in binaryExtensions.Split(','))
{
if (!string.IsNullOrEmpty(extension))
{
if (Regex.IsMatch(strUrl, extension.Trim()))
{
// the url path contains the specified regex string, so return true and break out of loop
return true;
}
}
}
}
return false;
}

#endregion
}


After coding this module, you'll need to change the .config so the module will be activated for usage. Add it to the httpModules of the system.web section. You'll also need to configure IIS that all needed extensions are routed through your ASP.NET ISAPI dll.

Friday, March 14, 2008

MCPD - EAD

Yesterday I had my 70-554 exam, and fortunately I passed with 880! It was a hard and difficult exam, so I was really glad to see this number on my screen.
I'm now certified for:
  • Microsoft Certified Technology Specialist: .NET Framework 2.0 Distributed Applications

  • Microsoft Certified Professional Developer: Enterprise Application Developer


Thursday, March 13, 2008

LINQ Design Guidelines

Mircea Trofin is writing on his blog some design guidelines for writing .NET LINQ code. It gives you a quick start to design qualitatively LINQ code.

Thursday, February 28, 2008

Sandcastle Themes

Sandcastle is documentation tool for building MSDN-like help documents. Although it's still in progress, it is already working great. Unfortunately, the documentation is very sparse.

One of the options you can give is the style of the documentation. But I found no description of the styles.
This are screenshots of the 3 different styles:

Hana



VS2005



Prototype


Tuesday, February 19, 2008

CC.Net project unexpected build failure

The last couple of weeks (after migrating to a new build server) I had strange behavior with two web projects building on CruiseControl.NET.
The build project is configured to build after a code check in on subversion. But after the check in, the build fails. A manual 'force build' successfully builds the project.
The build log only said there was an error durring the build, but without a clue what went wrong (and especially when you want to blame project members for checking in bad code ;).

Digging in the ccnet.log I finally found the the cause;

[project-x development:WARN] Process timed out:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe
Process id: 3380. This process will now be killed.


The question is, why can't there be a descriptive error in the build log of the project?
After increasing the seconds in the <tastk><MSBuild><Timeout> element, everything indeeds goes fine.

Wednesday, February 6, 2008

Friday, February 1, 2008

070-553 down, 070-554 to go

I just passed my 70-553 exam for the upgrade to MCPD:Enterprise Application Developer. So I will need an other exam, but following the 553 exam guide, I now earned two MCTS certification credits:

  • Microsoft Certified Technology Specialist: .NET Framework 2.0 Web Applications

  • Microsoft Certified Technology Specialist: .NET Framework 2.0 Windows Applications


Thursday, January 31, 2008

Lutz Roeder's Reflector

I just stumbled upon a nice Reflector tool for .NET after a tip of a colleague. It already helped with finding a bug in some Tridion .NET dll's...

Tuesday, December 11, 2007

OnInit not firing on controls with default property

Just build some nested controls to run on the web site, but there was some strange stuff going on.
The render method worked fine, but there weren't some values on those controls. The cause of this was that the OnInit, and the OnLoad event's weren't firing.
After some debugging and analyzing the control tree in /trace.axd, I saw there were no controls in the main control.
The reason? I'd put a [ParseChildren(ChildrenAsProperties = true,
DefaultProperty = "Groups")]
attibute on the main control, and all those controls were added to a List collection, but not to the Control collection. So ASP.net didn't took up those child controls for inclusion in the control tree.

A dirty line in the main control OnInit fixed this:
foreach (Group group in Groups) { Controls.Add(group); }.

Wednesday, November 28, 2007

.Net documentation missing

Just figured out that the HtmlWriter.AddAttribute() does an encoding based on the HtmlTextWriterAttribute enum you give as the first parameter. How? By inspecting the source code of Mono. With this knowledge I searched MSDN, but couldn't find a thing.

The bottomline: don't use a writer.AddAttribute(HtmlTextWriterAttribute.Href,
HttpUtility.HtmlAttributeEncode(url));
because then you're encoding twice.

The list of attributes which enode is:

HtmlTextWriterAttribute.Accesskey;
HtmlTextWriterAttribute.Alt;
HtmlTextWriterAttribute.Background;
HtmlTextWriterAttribute.Class;
HtmlTextWriterAttribute.Href;
HtmlTextWriterAttribute.Onchange;
HtmlTextWriterAttribute.Onclick;
HtmlTextWriterAttribute.Src;
HtmlTextWriterAttribute.Title;
HtmlTextWriterAttribute.Value;

Monday, November 12, 2007

PowerShell Shutdown COM+

To shutdown a COM+ object, so you can redeploy the binaries it uses, I had a VBS script to do it. I wondered how I could do it in PowerShell, but lacked time to do so. I just stumbled upon some code to do it, though.

The old script was:

Dim oCatalog ' As COMAdmin.COMAdminCatalog
Dim sName 'As String
Dim sMachine 'As String
Set oCatalog = CreateObject("COMAdmin.COMAdminCatalog")
sName = "Tridion Content Manager"
sMachine = "tridionserver"

oCatalog.Connect sMachine
oCatalog.ShutdownApplication sName



And this became:


$TridionAppName = "Tridion Content Manager"
$TridionMachine = "tridionserver"

$comAdmin = New-Object -comobject COMAdmin.COMAdminCatalog
$comAdmin.Connect($TridionMachine)
$comAdmin.ShutdownApplication($TridionAppName)


(Just leave the line with the call to connect out to run this on your local machine).