Monday, December 22, 2008
Analyzing Risk
Tuesday, December 16, 2008
Creating a configuration section
A better way to provide your application with configuration values, is the use of a configuration section. Instead of using the appSettings node, you can specify your own.
The key to your own configuration section is the System.Configuration namespace.
You write your own class, but should inherit ConfigurationSection.
Now you can spice up your properties in this class with the ConfigurationProperty attribute. Validation can be programmed with the attributes
- IntegerValidatorAttribute
- LongValidatorAttribute
- RegexStringValidatorAttribute
- StringValidatorAttribute
- TimeSpanValidatorAttribute
For example:
public class TestConfigurationSection : ConfigurationSection
{
// Empty Construct
public TestConfigurationSection() { }
// default string property
[ConfigurationProperty("deliveryStore", IsRequired = true)]
public string DeliveryStore
{
get
{
return (string)this["deliveryStore"];
}
set
{
this["deliveryStore"] = value;
}
}
//uri types will work too
[ConfigurationProperty("serviceUrl", DefaultValue = "http://192.168.1.10/MessageService", IsRequired = false)]
public Uri ServiceUrl
{
get
{
return (Uri)this["serviceUrl"];
}
set
{
this["serviceUrl"] = value;
}
}
[ConfigurationProperty("timeOut", DefaultValue = "0:00:20")]
[TimeSpanValidator(MinValueString = "0:00:05", MaxValueString = "0:05:00", ExcludeRange = false)]
public TimeSpan TimeOut
{
get
{
return (TimeSpan)this["timeOut"];
}
set
{
this["timeOut"] = value;
}
}
}
To use this configuration section in your config, you need to add it to the configsections node, for instance:
<configSections>
<section name="testConfig" type="NJV.Utils.TestConfigurationSection, NJV.Utils"/>
</configSections>
<testConfig
deliveryStore="D:\Test"
serviceUrl="http://10.0.0.9/MessageService"
timeOut="0:00:20"
/>
Now you've the settings defined and validation setup. To read these values, you can use the following code:
TestConfigSection config = (TestConfigSection)System.Configuration.ConfigurationManager.GetSection("testConfig");
TimeSpan timeout = config.TimeOut;
string deliveryLocation = config.DeliveryStore;
Uri serviceLocation = config.ServiceUrl;
The only drawback here is, you should be sure to have a testConfig section defined in your config, and it should be of the correct type. So maybe there could be some type and null reference checking on line 1:
TestConfigSection config = System.Configuration.ConfigurationManager.GetSection("testConfig") as TestConfigSection;
if (config==null)
{
throw new ApplicationException("No configuration available");
}
Sunday, December 14, 2008
Certified SQL Server Developer
MCTS: SQL Server 2008, Database Development, MCTS: SQL Server 2008, Business Intelligence Development and Maintenance and MCITP: Database Developer 2008.
Quite funny the SQL Developer is under the MCITP certification brand, instead of the MCPD.
Thursday, October 16, 2008
Presentation on .NET 3.5
I've made use of the demo pptPlex functionality from Office Labs. The version posted here, doesn't have these nice little extra's, but see the pptPlex website for more info about this tool. I didn't used pptPlex during the presentation the whole time... during the presentation I noticed I was presenting the wrong sheets, with old content on it. I changed back to normal presentation mode. Afterwards, I found the 'clear cache' under 'Learn more' to tidy up the cache and got the latest version of the sheets (but such a strange place for such an option).
Downloads
Thursday, October 9, 2008
PandP Cheat Sheet
Wednesday, October 8, 2008
Mount VHD in host OS
- Download the Virtual Server 2005 R2 SP1
- Install, uncheck all boxes except VHD Mount
- Edit registry
- Right click VHD
- ???
- Profit ;)
The registry should be edited with:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Virtual.Machine.HD]
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Virtual.Machine.HD\shell]
@="Mount"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Virtual.Machine.HD\shell\Dismount]
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Virtual.Machine.HD\shell\Dismount\command]
@="\"C:\\Program Files\\Microsoft Virtual Server\\Vhdmount\\vhdmount.exe\" /u \"%1\""
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Virtual.Machine.HD\shell\Mount]
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Virtual.Machine.HD\shell\Mount\command]
@="\"C:\\Program Files\\Microsoft Virtual Server\\Vhdmount\\vhdmount.exe\" /p \"%1\""
[HKEY_CLASSES_ROOT\.vhd]
@="Virtual.Machine.HD"
Monday, October 6, 2008
Code Review Checklist for ASP.NET
The goal of the review should be
- The code satisfies the requirements
- The code is robust (ie stable and should be descriptive in case of error)
- The code handles wrong inputs (SQL/XSS Injection!)
- The code is scalable
- The code is extensible and maintainable
Microsoft has a couple of checklists about these topics (unfortunately for .NET 1.1). Useful are: Securing ASP.NET, Security Review for Managed Code and Code Review for .NET Application Performance. And just found the guide for the .NET 2.0 version: How To: Perform a Security Code Review.
Monday, August 18, 2008
Filter tripple Gmail Starred items from Outlook 2007 To-Do List
Hey, I know I've got a lot to do, but this view isn't workable.
The solution is to right click the list, and choose 'Customize Current View...', there you can change the filter for the view, and in the Advanced tab you say, don't show items where the In Folder contains Starred or All Items.
The stupidity of the standard filter is that the two statements two aren't combined with the OR specifier... hey, that's first grade logica. So switch to SQL tab, ignore the message about not being able to use the other three tabs anymore, and change the OR in an AND...
Problem solved.
Saturday, July 19, 2008
First 3.5 Certifications
Last week I had finaly the results of my beta exam 70-562 for the MCTS ASP.NET 3.5 certificiation. Microsoft let me know that I have passed the exam! So it is my first VS2008 certificate.
Update:I also had a mail two days later I succeeded in the beta ADO.NET 3.5 exam. That's two down, still four to go ;)
Friday, July 11, 2008
Backup your files with WinRar
A while ago (actualy, three and a half years), I've made a backupscript with winrar. With these four files and your windows task scheduler, you've a full featured backup system, producing rar-files per run. The incremental version makes use of the archive flag of the filesystem. The full backup justs proces all the files. Backuplist.txt contains a list of all the folders to include. Exludefile.txt contains all the folders inside the included folders to exclude.
Incremental.cmd
"c:\program files\winrar\winrar.exe" a -ac -ao -ep2 -os -ow -IBCK -inul -r -agYY-MM-DD -x@B:\backup\excludefile.txt B:\backup\incremental- @B:\backup\backuplist.txt
REM -ac clear archive flag on files
REM -ao for archive flag only files
REM -ag[format] to include date in filename
REM -ep2 Store full file paths
REM -OS - save NTFS streams
REM -OW - process file security information
Full.cmd
"c:\program files\winrar\winrar.exe" a -ac -ep2 -os -ow -r -IBCK -inul -agYY-MM-DD -x@B:\backup\excludefile.txt B:\backup\full- @B:\backup\backuplist.txt
REM -ac clear archive flag on files
REM -ag[format] to include date in filename
REM -ep2 Store full file paths
REM -OS - save NTFS streams
REM -OW - process file security information
backuplist.txt
D:\Data
D:\My Documents
C:\Documents and Settings\User\StartMenu
C:\Documents and Settings\User\Desktop
C:\Documents and Settings\User\Application Data\Mozilla
excludefile.txt
C:\Documents and Settings\User\Application Data\Mozilla\Firefox\Profiles\default.xyz\Cache
Wednesday, July 2, 2008
Compiling documents the OpenXML way
Besides the standard list of words, the document should contain page-numbers. In the past, this was done by writing out html, saving the document with the .doc extension and pushing the file with the right MIME headers. But with this approach, insertion of page numbers on each page is quite a burden (if possible at all)... so enter OpenXML.
The other scenario, using mail-merge functionality, isn't used. I want the merge takes place on the server, not on the client.
The code here is just programming with XML and the System.IO.Packaging namespace, introduced in .NET 3.0. I still haven't tried the OpenXML SDK, so you should know what you're doing with adding XML fragments and their relations into the package.
The solution is quite straightforward, and it won't be difficult to expand this to your own needs.
We start by referencing the System.IO.Packaging namespace. It is delivered in the WindowsBase GAC dll. Add a reference to the WindowsBase and you can build your own Package from scratch:
private void Load(string documentPath)
{
Package pkgOutputDoc = null;
pkgOutputDoc = Package.Open(@"c:\work\test.docx", FileMode.Create, FileAccess.ReadWrite);
Uri uri = new Uri("/word/document.xml", UriKind.Relative);
PackagePart partDocumentXML = pkgOutputDoc.CreatePart(uri,
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml");
StreamWriter streamStartPart = new StreamWriter(partDocumentXML.GetStream(FileMode.Create, FileAccess.Write));
XmlDocument xdoc = new XmlDocument();
xdoc.Load(@"C:\work\document.xml");
FillDocument(xdoc);
xdoc.Save(streamStartPart);
streamStartPart.Close();
pkgOutputDoc.Flush();
pkgOutputDoc.CreateRelationship(uri, TargetMode.Internal,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
"rId1");
pkgOutputDoc.Flush();
pkgOutputDoc.Close();
}
I've added all the xml fragments I've used as embedded resources to my dll, so it is quite easy for me to version and deploy this solution. The added document.xml, the main content of my docx file, is expanded by generating custom xml, based on the database content.
I've read my database content into a dictionary, and I'm generating the xml based on each keyword in my database.
<w:p>
<w:r>
<w:t>Term</w:t>
</w:r>
<w:r>
<w:br />
</w:r>
<w:r>
<w:tab />
<w:t>SN</w:t>
</w:r>
<w:r>
<w:tab />
<w:t>scope note for Term</w:t>
</w:r>
<w:r>
<w:br />
</w:r>
<w:r>
<w:tab />
<w:t>UF</w:t>
</w:r>
<w:r>
<w:tab />
<w:t>Term B</w:t>
</w:r>
<w:r>
<w:br />
</w:r>
<w:r>
<w:tab />
<w:t>RT</w:t>
</w:r>
<w:r>
<w:tab />
<w:t>Term C</w:t>
</w:r>
<w:r>
<w:br />
</w:r>
</w:p>
This xml fragment is built by the following code
private void FillDocument(XmlDocument xdoc)
{
XmlNamespaceManager nsMgr = new XmlNamespaceManager(new NameTable());
string wNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
nsMgr.AddNamespace("w", wNamespace);
XmlNode wBody = xdoc.SelectSingleNode("/w:document/w:body", nsMgr);
//begin inserting at the last defined paragraph
XmlNode lastParagraph = xdoc.SelectSingleNode("/w:document/w:body/w:p[last()]", nsMgr);
Dictionary<string, List<RelatedKeyword>> thesaurus = ThesaurusList.GetThesaurus();
foreach (string keyword in thesaurus.Keys)
{
XmlElement thesaurusTerm = xdoc.CreateElement("w", "p", wNamespace);
wBody.InsertAfter(thesaurusTerm, lastParagraph);
lastParagraph = thesaurusTerm;
XmlElement thesaurusRterm = xdoc.CreateElement("w", "r", wNamespace);
XmlElement thesaurusText = xdoc.CreateElement("w", "t", wNamespace);
XmlElement thesaurusRbreak = xdoc.CreateElement("w", "r", wNamespace);
XmlElement thesaurusBreak = xdoc.CreateElement("w", "br", wNamespace);
thesaurusText.InnerText = keyword;
thesaurusTerm.AppendChild(thesaurusRterm);
thesaurusRterm.AppendChild(thesaurusText);
thesaurusTerm.AppendChild(thesaurusRbreak);
thesaurusRbreak.AppendChild(thesaurusBreak);
foreach (RelatedKeyword relatedKeyword in thesaurus[keyword])
{
XmlElement termTypeR = xdoc.CreateElement("w", "r", wNamespace);
XmlElement termDescriptionR = xdoc.CreateElement("w", "r", wNamespace);
XmlElement termBreakR = xdoc.CreateElement("w", "r", wNamespace);
XmlElement termTypeT = xdoc.CreateElement("w", "t", wNamespace);
XmlElement termTypeTab = xdoc.CreateElement("w", "tab", wNamespace);
XmlElement termDescriptionT = xdoc.CreateElement("w", "t", wNamespace);
XmlElement termDescriptionTab = xdoc.CreateElement("w", "tab", wNamespace);
XmlElement termBreak = xdoc.CreateElement("w", "br", wNamespace);
termTypeT.InnerText = relatedKeyword.Relation;
termDescriptionT.InnerText = relatedKeyword.Keyword;
thesaurusTerm.AppendChild(termTypeR);
termTypeR.AppendChild(termTypeTab);
termTypeR.AppendChild(termTypeT);
thesaurusTerm.AppendChild(termDescriptionR);
termDescriptionR.AppendChild(termDescriptionTab);
termDescriptionR.AppendChild(termDescriptionT);
thesaurusTerm.AppendChild(termBreakR);
termBreakR.AppendChild(termBreak);
}
}
}
Now I need to add different xml fragments to the package. The XML fragments for settings, footer and styles are added with this generic function.
AddPart(pkgOutputDoc, uri, partDocumentXML,
"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings",
"rId2",
"/word/settings.xml",
"settings.xml");
/// <summary>
/// Adds the part from an embedded XML to the Package.
/// </summary>
/// <param name="package">The package.</param>
/// <param name="documentUri">The document URI.</param>
/// <param name="partDocumentXML">The part document XML.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="relationshipType">Type of the relationship.</param>
/// <param name="relationId">The relation id.</param>
/// <param name="partPath">The part path.</param>
/// <param name="embeddedFile">The embedded file.</param>
private void AddPart(Package package, Uri documentUri, PackagePart partDocumentXML,
string contentType, string relationshipType, string relationId, string partPath,
string embeddedFile)
{
XmlDocument xdoc = new XmlDocument();
Uri uriPart = new Uri(partPath, UriKind.Relative);
PackagePart part = package.CreatePart(uriPart, contentType);
Uri relativePartUri =
PackUriHelper.GetRelativeUri(documentUri, uriPart);
Stream contentStream = GetEmbeddedXml(embeddedFile);
xdoc.Load(contentStream);
contentStream.Close();
xdoc.Save(part.GetStream());
partDocumentXML.CreateRelationship(relativePartUri, TargetMode.Internal, relationshipType, relationId);
}
The footer can contain the page number by using this xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:ftr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:p>
<w:pPr>
<w:jc w:val="right"/>
</w:pPr>
<w:fldSimple w:instr=" PAGE \* MERGEFORMAT ">
<w:r>
<w:t>1</w:t>
</w:r>
</w:fldSimple>
</w:p>
</w:ftr>
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)
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
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
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
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...
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
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> </th>
<th style="text-align:left;">Status</th>
<th> </th>
<th style="text-align:left;">Last buildtime</th>
<th> </th>
<th style="text-align:left;">Buildlabel</th>
<th> </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> </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> </td>
<td class="ms-vb" style="padding-bottom: 3px;" align="top">
<xsl:value-of select="substring-before(@lastBuildTime,'T')"/> 
<xsl:value-of select="substring-before(substring-after(@lastBuildTime,'T'),'.')"/>
</td>
<td> </td>
<td class="ms-vb" style="padding-bottom: 3px;text-align:right;" align="top">
<xsl:value-of select="@lastBuildLabel"/>
</td>
<td> </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
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
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 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
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
Thursday, February 28, 2008
Sandcastle Themes
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 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
- Microsoft Certified Technology Specialist: .NET Framework 2.0 Web Applications
- Microsoft Certified Technology Specialist: .NET Framework 2.0 Windows Applications