Sean Carpenter
Thoughts on Development

Custom Config Sections and CallbackValidatorAttribute

I was recently working on creating a custom configuration section (deriving from ConfigurationSection) to provide some configuration metadata to an app I’m working on.  In creating a ConfigurationElement, I wanted to validate that one of the attributes of the element was a valid value for the application.  While looking at the example here I noticed the use of various validation attributes applied to validate the format of configuration values.

The CallbackValidatorAttribute and CallbackValidator seemed like they would fit the bill for doing custom validation.  Unfortunately, the documentation on CallbackValidatorAttribute is severely lacking.  Here’s what I found out using Reflector:

  • Both the CallbackMethodName and Type properties of the CallbackValidatorAttribute must be specified.  The Type property is the type of object that contains the CallbackMethodName method.
  • The callback method must be public, static and be a ValidatorCallback delegate.
  • To fail validation, throw an ArgumentException during execution of the callback.  Reflector isn’t actually any help here for the CallbackValidator, but this is what the other validator types do.

I’ve created a small sample demonstrating the use of the CallbackValidatorAttribute.  You can get it here.  The sample validator itself isn’t very useful, but hopefully it illustrates the concept well enough.

NCover on Vista x64

I've been trying to get NCover to run successfully on Vista Ultimate x64 on and off for the last month. We're still using one of the free versions of NCover available from here, so there is no built-in x64 support. I came across this post today that mentioned using corflags to set the 32BIT flag in both NCover and your testing application (the post is describes using NUnit, but this worked for me with MBUnit as well).

Those changes got me past the

Profiled process terminated. Profiler connection not established.

error, but I was then confronted with a new wrinkle:

Index was outside the bounds of the array.

This error would appear at the end of the profiled MBUnit run. I poked around some and found some old posts on the NCover forums talking about this error and how it didn't occur in NCover 1.5.5. So I downloaded 1.5.5 (I had been using 1.5.8) and now all is good - coverage runs successfully!

We're not doing that much with NCover at this point, so I don't think we're giving up anything going with 1.5.5. Maybe someday we'll be able to purchase a current license that supports x64 out of the box.

Passing Data to Master Pages with ASP.NET MVC

There is a discussion occurring on the ASP.NET forums about passing data to Master Pages using ASP.NET MVC.  I couldn't figure out how to post code in the forums, so this post contains an example of the solution I am currently using.

I defined a "container" class that contains the data for the specific view and the data needed by the master page.  It looks like this:

public class ViewDataContainer<T>
{
public ITopMenuData TopMenu { get; set; }
public ILeftMenuData LeftMenu { get; set; }
public T Data { get; set; }
}

As you can see, this class is generic on the type of view data that it contains.

Next, I created a custom Controller-derived class that looks like this:

public class ControllerBase<T> : Controller
{
protected override void RenderView(string viewName, string masterName, object viewData)
{
ViewDataContainer<T> container = new ViewDataContainer<T>
{
TopMenu =
new TopMenuData(),
LeftMenu =
new LeftMenuData(),
Data = (T)viewData
};
base.RenderView(viewName, masterName, container);
}
}

This class takes the view data that's passed in by the controller and wraps it up in an instance of ViewDataContainer<T> that is then passed along.  As to where to get the TopMenuData and LeftMenuData, I'm still working on that :).

In this incarnation, views need to derive from ViewPage<ViewDataContainer<T>> (specifying T) and then access their view data via the ViewData.Data property.  Today I was playing around with a ViewPage<T> derived class that would expose the typed view data directly.  Maybe I'll post that soon as well.

Changing the Protocol Version for a Web Service Call

I've been playing with the National Digitial Forecast Database (NDFD) provided by the National Weather Service.  You can retrieve a forecast via a SOAP web service by providing your latitiude and longitude and a few other details.  The web service worked fine when I called it from Python, but I was getting an error when calling it from .NET (using the generated proxy classes in VS 2003 and VS 2005).  It turns out that the service needs HTTP version 1.0, not 1.1 (the default in .NET).  Unfortunately, the generated proxy class doesn't expose the ProtocolVersion property of the underlying HttpWebRequest object.  So, I added support for this property.

It's a little easier with VS 2005 because of the support for partial classes:

public partial class ndfd {
    private Version _protocolVersion = System.Net.HttpVersion.Version11;
    public Version ProtocolVersion {
        get { return _protocolVersion; }
        set { _protocolVersion = value; }
    }

    protected override System.Net.WebRequest GetWebRequest(Uri uri) {
        System.Net.WebRequest req = base.GetWebRequest(uri);
        ((System.Net.HttpWebRequest)req).ProtocolVersion = _protocolVersion;
        return req;
    }
}

I just added a new file to my solution and added a partial class definition (ndfd is the proxy class generated for the web service) that adds a ProtocolVersion property. Then in the GetWebRequest method, I set the protocol version of the WebRequest.

For VS 2003, I just derived a new class from the generated proxy class and added my property and override there:

public class ndfd2 : ndfd {
    private Version _protocolVersion = System.Net.HttpVersion.Version11;
    public Version ProtocolVersion {
        get { return _protocolVersion; }
        set { _protocolVersion = value; }
    }

    protected override System.Net.WebRequest GetWebRequest(Uri uri) {
        System.Net.WebRequest req = base.GetWebRequest(uri);
        ((System.Net.HttpWebRequest)req).ProtocolVersion = _protocolVersion;
        return req;
    }
}

Then I can just use the new class in my code. Both of these approaches avoid doing anything to the actual generated class, so the Web Reference can be updated without losing any of the customizations.

SQL Server 2005 SP1 Released

Well, after a relatively short CTP, SQL Server 2005 SP1 has been released.  This should be pretty cool, since the CTP solved the problems I was having with Import/Export.

[Found via David Hayden]

Changing the Receive Handler for a BizTalk 2004 Receive Location

This is actually pretty basic, but I had a hard time with it this morning, so I thought I'd share how to do this.

I created a host and host instance on our BizTalk server specifically to host the receive locations we are running.  Creating the host and instance was no problem, but I had a hard time changing the receive locations to use the new host.  In the BizTalk Administration Console as well as in BizTalk Explorer, there is a place to choose the Receive Handler - the problem was that this dropdown contained only the original host, not the new one.

It turns out that the host is associated with the receive adapter, not each instance.  So the solution is to go to the Adapters node in the BizTalk Administration Console, choose the Adapter, choose Receive Locations, and then specify the host.  This changes it for all receive locations using that host.  It's actually documented here, so I'm not sure why I had so much trouble finding the answer.

SQL Server 2005 SP1 CTP

The SQL Server 2005 SP1 CTP is available for download.  I'm going to be trying it out immediately since one of the included fixes is for the problem I was having with the Import/Export wizard.

SQL Server Backup to UNC Path Failing

We back up our SQL Server databases to a central storage machine using UNC paths in the BACKUP statement (BACKUP DATABASE db1 TO DISK='\\storage\Backups\db1\db1.bak...). For a while, backups were failing with the message:
BackupDiskFile::RequestDurableMedia:  failure on backup device '\\storage\Backups\db1\db1.bak'. Operating system error 64(The specified network name is no longer available.).

I struggled for a while to find a solution and finally found this thread on www.msce.ms.  Unfortunately that seems to have disappeared, so this is a summary of what that thread recommended (and what worked for me):

  • Open the registry of the SQL Server machine and navigate to HKLM\System\CurrentControlSet\Services\lanmanworkstation\parameters
  • Create a new DWORD value named SessTimeout (if it's already there I guess you can change the value but it wasn't there on any server I was having the problem with).
  • Set the value to decimal 600 (hex 0x258) - I believe this is seconds.

This corrected the problem for me (on two machines I had to bump the value to 900) and seems to have no ill effects on the rest of the server.

Preventing Automatic Generation of Web Service WSDL File

We have a web service that is exposed on the internet but is only used by our client application.  The client uses SSL to connect to the server and we are using WS-Security to provide authorization.  Even so, I wanted to prevent someone from viewing the interface of the service by going to the default WSDL generated by ASP.Net (http://domain/Service.asmx?WSDL).

It turns out that this is actually pretty easy to do.  I found documentation on it related to Visual Studio Team System Application Designer, but that page mentions that it makes a change to a service's web.config file.  The answer is to remove the "Documentation" protocol from the <webServices>, <protocols> section of the config file.  I chose to do it by using a <remove> directive in the service's web.config file, but you could also do it in the machine.config file to affect the whole server.  The nice thing about this solution is that I can leave it enabled on our development server so that Visual Studio can auto-generate the client proxy, but it won't be exposed at all on the production site.

Here's my change:

<webServices>
    <protocols>
        <remove name="Documentation" />
    </protocols>
</webServices>

Using NAnt to build Compact Framework Projects

I struggled with this a while back and finally came up with a solution that works.  There are a few things that I would do differently now (I'll try to highlight them below), but I've been running this for a few months now and it produces correct output so I'm happy.

My previous experience with NAnt involved using the <solution> task which works very well.  Unfortunately, the <solution> task doesn't support the Compact Framework.  The solution I came up with is as follows: write a list of source files to compile to a file, convert any resources from .resx format to binary resources, write a list of resource files to include to a file, and finally call the command line compiler passing in these lists of files and any other options.  I'll do a quick description of each step below.

Step 1 - Get a list of source files

This is the NAnt target I use to get the list of files (this is one of those things I'd do differently now - I'd use the /recurse option to the compiler).

<target name="listFiles">
        <delete file="sourceList.txt" if="${file::exists('sourceList.txt')}" />
        <foreach item="File" property="fName">
            <in>
                <items>
                    <include name="*.vb" />
                    <include name="${subDir}/**/*.vb" />
                </items>
            </in>
            <do>
                <echo message="&quot;${fName}&quot;" append="true" file="sourceList.txt"/>    
            </do>
        </foreach>
    </target>

This deletes the file if it already exists, then uses the <foreach> task to loop through the files with a "vb" extension in the specified directory and sub-directory (contained in the subDir property) and <echo> their names to the "sourceList.txt" file.  Pretty straightforward.

Step 2 - Convert Resources

The resources step is something that completely slipped my mind at the beginning.  I was originally only building a class library (with no embedded resources) so skipping this step wasn't an issue.  It became a problem when I added a WinForms app to the solution and built it without including the resources (that didn't run so well since many control properties are stored in the resource files).  Here's the target for building the resources.

<target name="makeResources">
        <foreach item="File" property="fName">
            <in>
                <items>
                    <include name="*.resx" />
                    <include name="${subDir}/**/*.resx" />
                </items>
            </in>
            <do>
                <property name="resName" value="${path::get-file-name-without-extension(fName)}.resources" />
                <exec program="C:\NET\CFDLL\CFResGen" commandline="${fName} ${path::combine(subDir, resName)}" />
            </do>
        </foreach>
    </target>

This target uses the <foreach> task again, this time to go through all .resx files.  This time, instead of writing the file names to a file, the task uses the <exec> task to execute the CFResgen tool, which converts .resx format files to binary .resources files.  I get the filename by stripping the extension off of the .resx file and replacing it with .resources.

Step 3 - Get a list of resource files to include

This is very similar to Step 1, but here's the target for completeness' sake.

<target name="listResourceFiles">
        <delete file="sourceResource.txt" if="${file::exists('sourceResource.txt')}" />
        <foreach item="File" property="fName">
            <in>
                <items>
                    <include name="*.resources" />
                    <include name="*.png" />
                    <include name="${subDir}/**/*.resources" />
                    <include name="${subDir}/**/*.png" />
                </items>
            </in>
            <do>
                <property name="resName" value="${path::get-file-name(fName)}" />
                <echo message="/resource:&quot;${fName}&quot;,&quot;Company.Project.${subDir}.${resName}&quot;" append="true" file="sourceResource.txt"/>    
            </do>
        </foreach>
    </target>

You can see that I'm also getting all files with a .png extension (I had one image embedded as a resource).  The other difference is that when writing the information to the file, I'm writing it as "/resource:fileName, fully qualified resource name". This is the format expected by the compiler and matches the way resources are named when compiling in Visual Studio.

Step 4 - Call the compiler

The last step is to actually call the compiler, passing in the list of source files, the list of resources, plus any other options you may need.  This is another step I'd do differently - I'd use the <vbc> task instead.

<target name="compileProj">
        <mkdir dir="build\Debug\${subDir}" unless="${directory::exists('build\Debug\' + property::get-value('subDir'))}" />
        <property name="cmdLine" value="@${subDir}.rsp @sourceList.txt" />
        <property name="cmdLine" value="${cmdLine + ' @sourceResource.txt'}" if="${file::exists('sourceResource.txt')}" />
        <exec program="C:\WINNT\Microsoft.NET\Framework\v1.1.4322\vbc.exe" commandline="${cmdLine}" output="compileData_${subDir}.txt"/>
    </target>

Here, I first make a directory to put the compiled file in.  Then, I build up the command line including an options file, the source file list, and the resource file list (if it exists).  Lastly, I use the <exec> task again to execute the compiler.  The options file looks like the following:

/warnaserror
/t:winexe
/netcf
/sdkpath:c:\net\cfdll
/out:build\Debug\programName\programName.exe
/r:c:\net\cfdll\system.dll
/r:c:\net\cfdll\system.data.dll
/r:c:\net\cfdll\system.xml.dll
/r:c:\net\cfdll\system.drawing.dll
/r:c:\net\cfdll\system.windows.forms.dll
/r:c:\net\cfdll\system.windows.forms.datagrid.dll
/r:build\Debug\DataAccess\DataAccess.dll
/r:build\Debug\CustomControls\CustomControls.dll
/debug+
/optionstrict+
/imports:Microsoft.VisualBasic
/imports:System
/imports:System.Collections
/imports:System.Configuration
/imports:System.Data
/imports:System.Diagnostics
/imports:System.Drawing
/imports:System.Windows.Forms
/rootnamespace:Company.Project

 

You can see what each of these options do on MSDN.  Basically, they pass in the equivalent settings that Visual Studio uses when it compiles a project.

Wrapping Up

One final target I'll show here is the one that is actually called by NAnt.  It sets the "subDir" property and then calls the other tasks shown above.

<target name="compileMultiple">
        <property name="subDir" value="CustomControls" />
        <call target="listFiles" />
        <call target="getResources" />
        <call target="compileProj"/>
        <property name="subDir" value="DataAccess" />
        <call target="listFiles" />
        <call target="getResources" />
        <call target="compileProj"/>
        <property name="subDir" value="programName" />
        <call target="listFiles" />
        <call target="getResources" />
        <call target="compileProj"/>
    </target>

 

If anyone has any questions (or wants to see the complete files), feel free to use the Contact link on this blog.

.NET Framework 2.0 Breaking Changes

Now that the release of VS2005 and the .NET Framework 2.0 is imminent, I'm able to justify more time at work for reviewing its impact.  This list of breaking changes for 2.0 is definitely useful in that regard.

I think one of the most important changes is the fact that unhandled exceptions will now always end a process (search for “Unhandled exceptions will always be fatal to a process”on this page).  The 1.1 behavior was that unhandled exceptions on threads other than the main thread would not end a process.  I ran into this twice while working on Windows Services that used background threads.  I know I should have been catching the exceptions, but it was still design/debug time and that error handling wasn't present yet.  The symptom I experienced was that the service would run for a while, but then stop polling a message queue for any more messages.  The problem was that the polling was done in response to the Elapsed event of a non auto-reset timer - when the processing was complete, the timer was re-enabled.  When an exception was raised in the processing (which took place on a background thread as a result of the timer Elapsed event) the processing stopped before getting to the re-enable code.  So essentially the service was dead, since it was no longer polling the message queue.  This change will prevent this kind of bug from creeping in.

[Via James Manning]

Finding the source of a Microsoft DLL

Geoff Appleby posts a link to a Microsoft support page that can tell you what DLL came from what package.  Very cool.

Where'd that DLL come from?

Text Report of SQL Dependencies

This is a bookmark for myself as much as anything else, but here is Raymond Lewallen's script for generating a text based dependency report for a SQL Server database.

Sql dependency report from query analyzer

Inserting Base64 Data, Part 2

One thing I forgot to mention in yesterday's post: it is possible to have SQLXML do the conversion from Base64 to binary using an updategram.  When using an updategram, it is possible to specify a mapping schema, which lets you use attribute names in the updategram that are different than the column names in the table.  A mapping schema also lets you specify data type conversion, one of which is Base64 to binary.  Here's a brief example:

Mapping Schema

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:sqlms="urn:schemas-microsoft-com:mapping-schema">
  <xsd:element name="binaryTest">
   <xsd:complexType>
        <xsd:attribute name="someParameter" sqlms:field="someValue" type="xsd:integer" /> 
        <xsd:attribute name="attachmentData" sqlms:field="attachmentData" type="xsd:base64Binary" sqlms:datatype="image" />
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Updategram

<ROOT xmlns:updg="urn:schemas-microsoft-com:xml-updategram">
   <updg:sync mapping-schema="testMap.xml">
      <updg:before />
      <updg:after>
         <binaryTest someParameter="1" attachmentData="dGhpcyBpcyBhIHRleHQgZmlsZQ==" />
      </updg:after>
   </updg:sync>
</ROOT>

If this updategram is executed as a template query in a virtual directory configured for SQLXML access, a record will be inserted in the “binaryTest” table with the Base64 encoded data converted to binary.

I wasn't able to use this solution for my problem because I couldn't figure out how to get the updategram generated by BizTalk to use a mapping schema.

Inserting Base64 Data into an image Column

I recently had the need to insert some binary data that was encoded as Base64 into a SQL Server image column.  Since I was using BizTalk, I was hoping it would be relatively straight forward - I was wrong.  There's a bit of information here on how I came to this problem and some BizTalk stuff, so if you just want the solution, jump to here.

The Background

I was using BizTalk to pull a record from one SQL Server, do some processing on the data, and then insert the processed record into another SQL Server.  The catch was that I needed to pull along some binary data and insert it into the second SQL Server.  The data was stored in an image column in each SQL Server DB.

The Problem

Things started out easily enough.  I was using a SQL Receive adapter to get the data from the source database.  I added the BINARY BASE64 option to the FOR XML clause I was using and got the binary data as Base64.  I then used the Add Generated Items wizard to generate the schema for the SQL insert (I was using a stored procedure that had a parameter typed as image).  I was happy to see that the generated schema listed the data type of the attribute corresponding to the stored procedure parameter as “xs:base64binary”.  This led me to believe that BizTalk (or the SQL Send Adapter, or SQLXML, or something) would convert the data back to binary as part of the insert.  Not so.  When the orchestration ran, I just got an error back from SQL Server:

The adapter "SQL" raised an error message.
Details "HRESULT="0x80040e07" Description="Operand type clash: ntext is incompatible with image"

So obviously nothing was converting the Base64 data back to binary.

The Solution

I posted a question to the newsgroups, but didn't really receive a good answer.  The solution I ended up with was to slightly modify a SQL Server UDF I found on the newsgroups.  The content of the modified UDF is below:

CREATE FUNCTION base64toBin (@bin64raw varchar(8000)) 
RETURNS varbinary(8000) 
AS 
BEGIN 
 declare @out varbinary(6000) 
 declare @i int 
 declare @length int 
 declare @bin64char char(1) 
 declare @bin64rawval tinyint 
 declare @bin64phase tinyint 
 declare @bin64nibble1 tinyint 
 declare @bin64nibble2 tinyint 
 declare @bin64nibble3 tinyint 
 SELECT @bin64phase = 0 
 SELECT @i = 1 
 SELECT @length = len(@bin64raw) 
 if right(@bin64raw, 1) <> '='
  set @length = @length + 1
 WHILE @i < @length 
 BEGIN 
  SELECT @bin64char = substring(@bin64raw,@i,1) 
  BEGIN           
   IF ASCII(@bin64char) BETWEEN 65 AND 90 
    SELECT @bin64rawval = ASCII(@bin64char)-65 
   ELSE 
    IF @bin64char LIKE '[a-z]' 
     SELECT @bin64rawval = ASCII(@bin64char)-71 
    ELSE 
     IF @bin64char LIKE '[0-9]' 
      SELECT @bin64rawval = ASCII(@bin64char)+4 
     ELSE             
      IF @bin64char = '+' 
       SELECT @bin64rawval = ASCII(@bin64char)+19 
      ELSE                     
       IF @bin64char = '/' 
        SELECT @bin64rawval = ASCII(@bin64char)+16 
       ELSE 
       BEGIN 
        SELECT @bin64rawval = 0 
        SELECT @i = @length-1 
       END 
  END
  IF @bin64phase = 0 
  BEGIN 
   SELECT @bin64nibble1 = (@bin64rawval - @bin64rawval%4)/4 
   SELECT @bin64nibble2 = @bin64rawval%4 
   SELECT @bin64nibble3 = 0 
  END 
  ELSE 
   IF @bin64phase =1   
   BEGIN 
    SELECT @bin64nibble2 = (@bin64nibble2*4) + (@bin64rawval - @bin64rawval%16)/16 
    SELECT @bin64nibble3 = @bin64rawval%16 
    IF @i<5 
     SELECT @out= convert (binary(1),((16*@bin64nibble1) + @bin64nibble2)) 
    ELSE                                             
     SELECT @out= @out + convert (binary(1),((16*@bin64nibble1) + @bin64nibble2)) 
   END
   ELSE 
    IF @bin64phase =2   
    BEGIN 
     SELECT @bin64nibble1 = @bin64nibble3 
     SELECT @bin64nibble2 = (@bin64rawval - @bin64rawval%4)/4 
     SELECT @bin64nibble3 = @bin64rawval%4 
     SELECT @out=@out+ convert (binary(1),((16*@bin64nibble1) + @bin64nibble2)) 
    END 
    ELSE 
     IF @bin64phase =3 
     BEGIN 
      SELECT @bin64nibble1 = (@bin64nibble3*4) + (@bin64rawval - @bin64rawval%16)/16 
      SELECT @bin64nibble2 = @bin64rawval%16 
      SELECT @out=@out+ convert (binary(1),((16*@bin64nibble1) + @bin64nibble2)) 
    END
  SELECT @bin64phase = (@bin64phase + 1)%4 
  SELECT @i = @i + 1 
 END 
 RETURN(@out) 
END

The one change I made was to add the section that begins “if right(@bin64raw...”.  I added this because my Base64 data was more than 8000 characters long and I needed to be able to call this UDF more than once with “chunks” of the Base64 data.  Without the change, the UDF will drop the last character of the Base64 data on “chunks” in the middle.  Below is a sample that calls this UDF with chunks of 2400 characters (because of the way Base64 encoding works, the “chunk” size has to be divisible by 4).

CREATE PROCEDURE testConvert
    @someParameter int,
    @attachmentData text
AS

/*** Table schema used for test
CREATE TABLE testData(someValue int, attachmentData image, CONSTRAINT PK_testData primary key nonclustered (someValue))
***/

-- insert NULL (0x0) into the image field so that the TEXTPTR function will work
insert testData(someValue, attachmentData)
values(@someParameter, 0x0)

declare @pointer varbinary(16)
select @pointer = TEXTPTR(attachmentData) from testData where someValue = @someParameter

declare @buff varchar(2400)
declare @offset int, @imgOffset int
set @offset = 1
set @imgOffset = 0
while @offset <= datalength(@attachmentData)
begin
 select @buff = substring(@attachmentData, @offset, 2400)
 declare @img varbinary(8000)
 select @img = dbo.base64toBin(@buff)
 UPDATETEXT testData.attachmentData @pointer @imgOffset NULL @img
 set @imgOffset = @imgOffset + datalength(@img)
 set @offset = @offset + 2400
end

So that's it.  This has been working in production now for over a month, so I'm pretty happy.  Hopefully this can help someone else.