Monday, November 28, 2011

WPF Wizard Control

There doesn't seem to really be anything in the way of a wizard control for WPF. Developer Express has one for Winforms, but when it came to WPF, their recent advice is to look at this project until they create one.

I took that project (not a control; merely an example of a wizard implementation) and made it into a reusable user control. It's available on codeplex. Just as with my last post, I hope someone can make use of it.

Thursday, November 03, 2011

Programmatically Creating SSIS Packages

The last post was specific; this one very general.

I created a project on codeplex that is a functional import system using the SSIS runtime. If you are heading down the road of using the SSIS API, the project may be very helpful in helping to figure out just how.

I sure hope it saves somebody a bunch of time.

Thursday, October 13, 2011

Programmatically Creating an SSIS Package with a Flat File Connection Source

I'm using the SSIS API to programmatically create (and execute) a package. The requirement for the first package is to get .csv data from a flat file into a SQL Server table. So I start checking into the MS docs, looks easy enough. Not so fast. When it comes to a flat file connection, there seems to be holes in the API. I ended up having to use one class that (according to the docs) I shouldn't be referencing in my code, but there doesn't seem to be any other way to make the flat file connection work because it simply won't load column metadata from the file. Won't do it.

I found exactly one other sample of doing this (which was helpful), but that sample's method of reading the flat file's columns seemed less than optimal.

Following is a working sample. Sorry for formatting; I simply don't know any way to prevent the software from stripping out my formatting...

The data file looks like the following. Column names in first row.
"this","that"
"data","more data"
"other data","you get the point"

The SQL table is like yay:
CREATE TABLE [dbo].[bubba](
[this] [varchar](max) NULL,
[that] [varchar](max) NULL
) ON [PRIMARY]

The method below uses this helper class which makes use of the TextFieldParser class which seems very useful. I have no idea why it's in the Microsoft.VisualBasic.FileIO namespace, but whatever works:


class FlatFileColumnReader

{

    public List<string> Columns( string path, char delimiter, FieldType ft )

    {

        var tfp = new TextFieldParser( path )

        {

            TextFieldType = ft

        };

        tfp.Delimiters = new string[] {delimiter.ToString()};

        return tfp.ReadFields().ToList();

    }

}



Here is the (big fat, but working) method. Requires following usings. The referenced assemblies were found on my machine in C:\Program Files (x86)\Microsoft SQL Server\100\SDK\Assemblies. Note that they're in the x86 dir. Thus, my application is set to target x86.
using System.Data.Common;
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.VisualBasic.FileIO;


public Microsoft.SqlServer.Dts.Runtime.Package Generate( Microsoft.SqlServer.Dts.Runtime.Application app )

{

    /// Objects

    Microsoft.SqlServer.Dts.Runtime.Package _package;

    Executable _dataFlowTask;

    IDTSComponentMetaData100 _dataSource;

    IDTSComponentMetaData100 _dataDest;

    CManagedComponentWrapper _sourceInstance;

    CManagedComponentWrapper _destinationInstance;

    ConnectionManager _conMgrSource;

    ConnectionManager _conMgrDest;



    /// Create package and data flow task

    _package = new Microsoft.SqlServer.Dts.Runtime.Package();

    _package.DelayValidation = true;

    _dataFlowTask = _package.Executables.Add( "STOCK:PipelineTask" );  // PipelineTask is a DataFlowTask ??

    var pipe = (MainPipe)( (Microsoft.SqlServer.Dts.Runtime.TaskHost)_dataFlowTask ).InnerObject;

    pipe.Events = DtsConvert.GetExtendedInterface( new ComponentEvents() as IDTSComponentEvents );  // my ComponentEvents() just writes some stuff to debug for now



    /// Create connections

    _conMgrSource = _package.Connections.Add( "FLATFILE" );

    _conMgrSource.Properties["Format"].SetValue( _conMgrSource, "Delimited" );

    _conMgrSource.Properties["Name"].SetValue( _conMgrSource, "Flat File Connection" );

    _conMgrSource.Properties["ConnectionString"].SetValue( _conMgrSource, @"C:\temp\Eeemport\bubba.txt" );

    _conMgrSource.Properties["ColumnNamesInFirstDataRow"].SetValue( _conMgrSource, true );

    _conMgrSource.Properties["HeaderRowDelimiter"].SetValue( _conMgrSource, "\r\n" );

    /// If you set the delimiter like this, it'll look correct if you open the resulting package in the UI, but it won't execute (unless you click "Reset Columns")

    //_conMgrSource.Properties["RowDelimiter"].SetValue( _conMgrSource, "{CR}{LF}" );

    _conMgrSource.Properties["TextQualifier"].SetValue( _conMgrSource, "\"" );

    _conMgrSource.Properties["DataRowsToSkip"].SetValue( _conMgrSource, 0 );



    _conMgrDest = _package.Connections.Add( "OLEDB" );

    // This provider wouldn't work

    //_conMgrDest.ConnectionString = @"Provider=Native OLE DB\SQL Server Native Client 10.0;Data Source=.\SQLEXPRESS;Initial Catalog=FASClient;Integrated Security=True";

    _conMgrDest.ConnectionString = @"Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=FASClient;Data Source=.\SQLEXPRESS";

    _conMgrDest.Name = "OLE DB Connection";

    _conMgrDest.Description = "OLE DB Connection";

    _conMgrDest.Properties["RetainSameConnection"].SetValue( _conMgrDest, true );



    /// Create the columns in the flat file connection

    var flatFileConnection = _conMgrSource.InnerObject as IDTSConnectionManagerFlatFile100;

    var fileColumns = new FlatFileColumnReader().Columns( @"C:\temp\Eeemport\bubba.txt", ',', FieldType.Delimited );

    for ( int i = 0; i < fileColumns.Count; i++ )

    {

        /// This object (IDTSConnectionManagerFlatFileColumn100) is not supposed to be referenced by my code according to doc:

        /// http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.dts.runtime.wrapper.idtsconnectionmanagerflatfilecolumn100.aspx

        var column = flatFileConnection.Columns.Add();

        /// Last column delimiter must be newline.

        /// If you select "," for the column delimiter in the designer for a Flat File Connection, and the row delimiter is newline, it does this same thing...

        column.ColumnDelimiter = ( i == fileColumns.Count - 1 ) ? "\r\n" : ",";

        column.TextQualified = true;

        column.ColumnType = "Delimited";

        /// Here's one benefit of creating my own columns:

        /// My destination column in Sql Server is varchar.  The columns seem to be defaulted to DT_WSTR which won't go into a varchar column w/o being

        /// manually changed or run through a data converter component.

        column.DataType = DataType.DT_TEXT;

        column.DataPrecision = 0;

        column.DataScale = 0;

        ( (IDTSName100)column ).Name = fileColumns[i];

    }



    /// Create Data Flow Components

    _dataSource = pipe.ComponentMetaDataCollection.New();

    _dataSource.Name = "Flat File Source";

    _dataSource.ComponentClassID = app.PipelineComponentInfos["Flat File Source"].CreationName;

    _dataSource.ValidateExternalMetadata = false;



    _dataDest = pipe.ComponentMetaDataCollection.New();

    _dataDest.Name = "Sql Server Destination";

    _dataDest.ComponentClassID = app.PipelineComponentInfos["SQL Server Destination"].CreationName;



    ///// Create design instances

    _sourceInstance = _dataSource.Instantiate();

    _sourceInstance.ProvideComponentProperties();



    /// I think this junk must come after ProvideComponentProperties() above

    _dataSource.RuntimeConnectionCollection[0].ConnectionManager = DtsConvert.GetExtendedInterface( _conMgrSource );

    _dataSource.RuntimeConnectionCollection[0].ConnectionManagerID = _conMgrSource.ID;



    _sourceInstance.AcquireConnections( null );  // do we need to do this since we created our own columns?

    _sourceInstance.ReinitializeMetaData();

    _sourceInstance.ReleaseConnections();



    _destinationInstance = _dataDest.Instantiate();

    _destinationInstance.ProvideComponentProperties();



    /// I know SetComponentProperty can only be called after ProvideComponentProperties()

    /// To see available component properties, open an existing package (the XML) with an existing component of that type

    _destinationInstance.SetComponentProperty( "BulkInsertTableName", "[dbo].[bubba]" );

    _dataDest.RuntimeConnectionCollection[0].ConnectionManager = DtsConvert.GetExtendedInterface( _conMgrDest );

    _dataDest.RuntimeConnectionCollection[0].ConnectionManagerID = _conMgrDest.ID;

    _destinationInstance.AcquireConnections( null );

    _destinationInstance.ReinitializeMetaData();

    _destinationInstance.ReleaseConnections();



    //// Hook the path from source to dest

    var path = pipe.PathCollection.New();

    path.AttachPathAndPropagateNotifications( _dataSource.OutputCollection[0], _dataDest.InputCollection[0] );



    /// Do stuff with the virtual input (whatever the @#$% that is)

    var virtualInput = _dataDest.InputCollection[0].GetVirtualInput();

    foreach ( IDTSVirtualInputColumn100 column in virtualInput.VirtualInputColumnCollection )

    {

        _destinationInstance.SetUsageType( _dataDest.InputCollection[0].ID, virtualInput, column.LineageID, DTSUsageType.UT_READONLY );

    }



    /// MapColumns();

    foreach ( IDTSInputColumn100 inputColumn in _dataDest.InputCollection[0].InputColumnCollection )

    {

        var outputColumn = _dataDest.InputCollection[0].ExternalMetadataColumnCollection[inputColumn.Name];

        outputColumn.Name = inputColumn.Name;

        _destinationInstance.MapInputColumn( _dataDest.InputCollection[0].ID, inputColumn.ID, outputColumn.ID );

    }



    //_package.Validate( _package.Connections, null, null, null );



    return _package;

}

Friday, May 06, 2011

File or Folder Delete Utility

I originally put this utility together for one very specific purpose: to delete all files in a given folder whose name started with a number (cleaning up after other processes). It has since been used for other purposes, so I made it easier to extend by using the "pipeline" pattern just like is commonly done with data filtering. File or folder names from the requested directory are passed through filters (Filters.cs). Those meeting filter requirements are processed (either logged or deleted).

The currently implemented filters are:
  • Date (always taken into account)
  • Starts with numeric
  • Name is a guid
It will target either files or folders. Launch the utility with no parameters to see instructions.

To extend with another filter:
  • Add methods to Filters.cs following same pattern as existing (returns IEnumerable of FileInfo for files; IEnumerable of string for folders).
  • Add parameter metadata for the new filter parameter in AppParams.cs.PARAM_STRINGS
  • Add a "mode" property for the new filter in AppParams.cs (Like existing NumericMode).
  • Add a method to read the new param from the command line and set the new property you created above (like existing SetNumericMode).
  • Update the help text that prints when no params or bad params are passed to the utility in Program.cs

Test before using, of course, but I hope it's of use to somebody. If you just want the utility, get it here; if you want the code, it's here.

Wednesday, December 22, 2010

C# Decimal to English Money Converter



I needed a way to convert a money value (decimal) into the kind of English text that appears on a legal document (like a check or a contract), e.g., 1245.36 becomes "One Thousand Two Hundred Forty Five and 36/100 Dollars."
After searching for awhile, I couldn't find anything. Maybe somebody else will make use of this (took a bit longer than I estimated). Has a limitation on millions (I'm not working on a government contract), but this can easily be changed. Requires .Net 4 (uses Tuples).

Sorry for poor code formatting.

First the decimal extension class that contains a method used by the actual converter class and also has a method to call the converter itself:


public static class DecimalExtension
{

/// <summary>
/// Evaluates just the portion of the value to the right of the decimal place and returns it as a 2 character string.
/// Returns 00 if the value is a whole number.
/// </summary>
/// <returns>The value to the right of the decimal place. Returns 0 if the value is a whole number.</returns>
public static string GetDecimalNumbers( this decimal number )
{
int divint = Convert.ToInt32( Decimal.Floor( number ) );
decimal decValue = number - divint;

var result = decValue.ToString();

if ( result.Length > 1 )
{
result = result.Substring( 2 );
if ( result.Length > 2 )
{
result = result.Substring( 0, 2 );
}
else if ( result.Length < 2 )
{
result += "0";
}
}
else
{
result = "00";
}

return result;
}

public static string ToEnglishMoney( this decimal d )
{
return new DecimalToEnglishMoney( d ).ToString();
}

}

Now for the actual converter class:


public class DecimalToEnglishMoney
{

private enum DigitPlace
{
Ones = 0,
Tens = 1,
Hundreds = 2
}

private string _result;

public DecimalToEnglishMoney( decimal d )
{
_result = FormatWholePortion( d );
_result += FormatDecimalPortion( d );
}

public override string ToString()
{
return _result;
}

private string FormatDecimalPortion( decimal d )
{
var result = string.Empty;
var decimalPortion = d.GetDecimalNumbers();
result += string.Format( " and {0}/100 Dollars", decimalPortion );
return result;
}

private string FormatWholePortion( decimal d )
{
var result = string.Empty;
var wholePortion = (int)Math.Floor( (double)d );
var groups = FormatToNumberGroups( wholePortion );
groups.ForEach( g =>
{
result += (result == string.Empty) ? string.Empty : " ";
var formattedChunk = FormatChunkToEnglish( g.Item2 );
var amountDescriptor = (g.Item1 == string.Empty) ? string.Empty : " " + g.Item1; // "Thousand"
result += formattedChunk;
result += (amountDescriptor == string.Empty) ? string.Empty : amountDescriptor;
} );
return result;
}

/// <summary>
/// Returns the chunk name (blank, Thousand, Million) and the chunk number
/// </summary>
/// <param name="i"></param>
/// <returns>"Million" "NNN"</returns>
private List<Tuple<string, string>> FormatToNumberGroups( int i )
{
var result = new List<Tuple<string, string>>();
var asString = i.ToString();
var count = 1;
while ( asString.Length > 0 )
{
var chunkStartPos = (asString.Length < 3) ? 0 : asString.Length - 3;
var chunkLength = (asString.Length < 3) ? asString.Length : 3;
var chunk = asString.Substring( chunkStartPos, chunkLength );
switch ( count )
{
case 2:
result.Add( new Tuple<string, string>( "Thousand", chunk ) );
break;
case 3:
result.Add( new Tuple<string, string>( "Million", chunk ) );
break;
default:
/// First chunk is blank and we're not expecting anything > than million
result.Add( new Tuple<string, string>( string.Empty, chunk ) );
break;
}
count++;
asString = (asString.Length > 3) ? asString.Substring( 0, asString.Length - 3 ) : string.Empty;
}
result.Reverse();
return result;
}

public string FormatChunkToEnglish( string chunk )
{
Debug.Assert( (chunk.Length <= 3) && (chunk.Length >= 1), "Expecting 1-3 digit portion of a number to format to english." );

var onesDigit = OnesDigit( chunk );
var tensDigit = TensDigit( chunk );
var hundredsDigit = HundredsDigit( chunk );

var onesDigitFormatted = FormatDigit( onesDigit, DigitPlace.Ones, chunk );
var tensDigitFormatted = (tensDigit == string.Empty) ? string.Empty : FormatDigit( tensDigit, DigitPlace.Tens, chunk );
var hundredsDigitFormatted = (hundredsDigit == string.Empty) ? string.Empty : FormatDigit( hundredsDigit, DigitPlace.Hundreds, chunk );

var result = onesDigitFormatted;
if ( tensDigitFormatted != string.Empty )
{
result = ( result == string.Empty ) ? tensDigitFormatted : string.Format( "{0} {1}", tensDigitFormatted, result );
}
if ( hundredsDigitFormatted != string.Empty )
{
result = string.Format( "{0} {1}", hundredsDigitFormatted, result );
}

return result;
}

#region These all deal with the 1-3 digit chunks

private string HundredsDigit( string chunk )
{
return (chunk.Length > 2) ? chunk.Substring( chunk.Length - 3, 1 ) : string.Empty;
}

private string TensDigit( string chunk )
{
return (chunk.Length > 1) ? chunk.Substring( chunk.Length - 2, 1 ) : string.Empty;
}

private string OnesDigit( string chunk )
{
return chunk.Substring( chunk.Length - 1 );
}

private bool ContainsTeen( string allDigits )
{
return TensDigit( allDigits ) == "1";
}

#endregion

private string FormatDigit( string digit, DigitPlace digitPlace, string allDigits )
{
/// Param must be string due to the way it's constructed
Debug.Assert( digit.Length == 1, "Expecting to format single digit while converting number to english, but received multiple digits" );

switch ( digit )
{
case "0":
return string.Empty;
case "1":
switch ( digitPlace )
{
case DigitPlace.Ones:
return ContainsTeen(allDigits) ? string.Empty : "One";
case DigitPlace.Tens:
return FormatTeen(allDigits);
case DigitPlace.Hundreds:
return "One Hundred";
}
break;
case "2":
switch ( digitPlace )
{
case DigitPlace.Ones:
return ContainsTeen(allDigits) ? string.Empty : "Two";
case DigitPlace.Tens:
return "Twenty";
case DigitPlace.Hundreds:
return "Two Hundred";
}
break;
case "3":
switch ( digitPlace )
{
case DigitPlace.Ones:
return ContainsTeen(allDigits) ? string.Empty : "Three";
case DigitPlace.Tens:
return "Thirty";
case DigitPlace.Hundreds:
return "Three Hundred";
}
break;
case "4":
switch ( digitPlace )
{
case DigitPlace.Ones:
return ContainsTeen(allDigits) ? string.Empty : "Four";
case DigitPlace.Tens:
return "Forty";
case DigitPlace.Hundreds:
return "Four Hundred";
}
break;
case "5":
switch ( digitPlace )
{
case DigitPlace.Ones:
return ContainsTeen(allDigits) ? string.Empty : "Five";
case DigitPlace.Tens:
return "Fifty";
case DigitPlace.Hundreds:
return "Five Hundred";
}
break;
case "6":
switch ( digitPlace )
{
case DigitPlace.Ones:
return ContainsTeen(allDigits) ? string.Empty : "Six";
case DigitPlace.Tens:
return "Sixty";
case DigitPlace.Hundreds:
return "Six Hundred";
}
break;
case "7":
switch ( digitPlace )
{
case DigitPlace.Ones:
return ContainsTeen(allDigits) ? string.Empty : "Seven";
case DigitPlace.Tens:
return "Seventy";
case DigitPlace.Hundreds:
return "Seven Hundred";
}
break;
case "8":
switch ( digitPlace )
{
case DigitPlace.Ones:
return ContainsTeen(allDigits) ? string.Empty : "Eight";
case DigitPlace.Tens:
return "Eighty";
case DigitPlace.Hundreds:
return "Eight Hundred";
}
break;
case "9":
switch ( digitPlace )
{
case DigitPlace.Ones:
return ContainsTeen(allDigits) ? string.Empty : "Nine";
case DigitPlace.Tens:
return "Ninety";
case DigitPlace.Hundreds:
return "Nine Hundred";
}
break;
default:
return string.Empty;
}
return string.Empty; // not a good sign when you have to add code just to make it compile
}

/// <summary>
/// </summary>
/// <param name="allDigits">Either 2 or 3 characters long and the tens digit is a one</param>
/// <returns></returns>
private string FormatTeen( string allDigits )
{
Debug.Assert( (allDigits.Length == 2) || (allDigits.Length == 3) );
switch ( OnesDigit(allDigits) )
{
case "0":
return "Ten";
case "1":
return "Eleven";
case "2":
return "Twelve";
case "3":
return "Thirteen";
case "4":
return "Fourteen";
case "5":
return "Fifteen";
case "6":
return "Sixteen";
case "7":
return "Seventeen";
case "8":
return "Eighteen";
case "9":
return "Nineteen";
default:
return string.Empty;
}
}

}

Wednesday, September 08, 2010

Implementing Session Timeout Check in MVC

This took me awhile to get right; hope it saves some time for somebody else. I simply want to decorate a given controller (at the class level) with an attribute that will cause a redirect to login page (with a session timeout message) on session timeout while using forms authentication.

Here's the attribute:


     public class CheckSessionAttribute : ActionFilterAttribute 

     {

         public override void OnActionExecuting( ActionExecutingContext filterContext )

         {

             if ( filterContext.HttpContext.Session.IsNewSession )

             {

                 FormsAuthentication.SignOut();

                 filterContext.Controller.TempData[Constants .TEMPDATA_KEYS .TIMEOUT] = "Your session has timed out.  Please login again to continue." ;

                 filterContext.Result = new RedirectResult ( "/" );

             }

         }

     }

 

 


Now you need only check the presence of that TempData key on your logon view in order to show a proper timeout message instead of the standard login message.
Note the use of RedirectResult. The old Response.Redirect will do a proper redirect, but won't terminate the original request (even with the overload containing the parameter that tells it to do so). Response.Redirect should really raise an error when used in an MVC app.

Thursday, August 12, 2010

Fix for Extremely Slow Development Browser Performance

I'm running Windows 7 with Visual Studio 2010. When I would debug web applications (where VS would use Cassini, the development web server), browser performance was extremely slow. We're talking almost a second for retrieval of every image (all on my local machine). Most of the time it seemed like IE didn't experience the problem, but something changed and it too seemed to stop working.
After coming across this post and doing what it recommended, the problem went away. I've no idea why that configuration change in Firefox would affect IE, but all my problems vanished.
Maybe this will help some other poor chap; it got to the point where I just couldn't work at all.

Friday, April 23, 2010

Detecting Session Timeout in ASP.Net WebForms

[Edit]
Disregard all this crap. It's simply not possible to detect session timeout using cookieless sessions. Detecting a post with a redirect does in fact trigger the condition in Application_EndRequest below (in my defense, it seemed to work in a test project and was deceiving).
[/Edit]

Everyone is starting new projects with MVC these days, but for all those maintenance projects in ASP.Net WebForms, here is what I've settled on as the best way to detect session timeout. I've had 1 or 2 other methods in production that didn't cause problems, but were not quite correct.

I was quite surprised when initially investigating this issue to see how many ways people had come up with to do it.

I had a completely different version of this up on this post for quite awhile that didn't work when using cookieless sessions.

Here's the assumption: if a user's session has timed out, but they've issued an HTTP POST, that indicates a session timeout. If you're not using cookieless sessions, that's all there is to it; check this somewhere in a page base class:
Request.HttpMethod.Equals( "POST" ) && (Session.IsNewSession)
The problem with cookieless sessions is, you never get to that condition on a page. In either case, Session.IsNewSession by itself is completely useless because that may be true on an initial GET (not a session timeout condition). With cookieless sessions, sometimes it will be true and the request will be a GET (by the time it gets to your page) when the user posts after a session timeout. This is what happens when using cookieless sessions: the user posts up, ASP.Net detects a session timeout and issues a 302 telling the browser to go to the same place, but use GET. Thus, on any page, there is absolutely no way to know whether a timeout condition has occurred.

Consequently, the way I handled this problem is by detecting the above stated condition (user's session has timed out, current request is a post) in Global.asax. A Response.Redirect or Server.Transfer works fine with this. The problem that will occur with this approach is that the flag for session timeout will be set, but before the user with the timeout round trips back to the server with a GET, another user will hit Session_Start and receive an incorrect session timeout condition. Also, the user who timed out might, for whatever reason, not make the round trip back to the server. In this case, again, a user issuing his first request will see a session timeout. I'm willing to accept that, on rare occasion, this happens. Knowing users, he will simply try again, get the page he requested, and think little of it.

Global.asax code:
     public  class  Global  : System.Web.HttpApplication 

     {

 

         private  bool  _sessionTimedOutCookieless = false ;

 

         void  Application_EndRequest( object  sender, EventArgs  e )

         {

             /// This condition occurs when using cookieless sessions (but perhaps not with "AutoDetect" - that mode is evil) 

             /// The redirect will occur within the context of this same application instance, so the private variable will still 

             /// be alive. 

             if  ( Request.HttpMethod.Equals( "POST"  ) && (Response.StatusCode == 302) )

             {

                 _sessionTimedOutCookieless = true ;

             }

         }

 

         void  Session_Start( object  sender, EventArgs  e )

         {

             if  ( _sessionTimedOutCookieless || (SessionTimedOutWithCookies()) )

             {

                 _sessionTimedOutCookieless = false ;

                 Session.Add( "timeout" , true  );

             }

         }

 

         private  bool  SessionTimedOutWithCookies()

         {

             return  Request.HttpMethod.Equals( "POST"  ) && (Session.IsNewSession);

         }

 

     }

 

 




In a base page class:

         protected  void  Page_PreInit()

         {

             if  ( Session["timeout" ] != null  )

             {

                 _timedout = true ; // do something about this wherever 

                 Session.Remove( "timeout"  );

             }

         }

 

 

Friday, February 19, 2010

EF4 & SQL Server Date / DateTime Columns

When using EF4 against SQL Server 2008 (though this would apply to 2005 as well), I kept getting this error on insert:

System.Data.SqlClient.SqlException: The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value


I had 2 date columns in the table I was attempting to insert into to begin with. One was just a LastDateModified column with a default of GetDate() so I was mentally filtering that column out and focusing on the other column. I changed that column from date to datetime which didn't fix the problem. I then changed that column to DateTime(2,7) and it still didn't fix it. Running a trace on the SQL that EF4 was attempting to insert revealed the problem. EF4 isn't smart enough to see that my LastDateModified column has a default and shouldn't bother sending a value when the programmer doesn't set one. It sent some huge min date that didn't fit into a datetime column.

Thus, the workaround is to either set any columns I won't be explicitly setting in code to type date(2,7) or to explicitly set the value of those columns in code to DateTime.Now. In any case, you can't use the SQL Server 2008 date column data type (which sucks).

Saturday, December 12, 2009

Entity Framework IEnumerable Lambda Join Sample

I figured this out from a sample, but it wasn't at all clear. The docs provide no sample, and the parameter names are totally confusing. If you're using EF, and you're wondering how the Join IEnumerable extension method works with your entities, here is the simplest example:


var parent_child = entities.Parent.Join

 (

   entities.Child,            // child entity

   parent => parent.ParentId, // function providing parent key

   child => child.ChildId,    // function providing child key

   ( parent, child ) => new   // projection (anonymous object returning what you want)

   {

      ChildId = child.ChildId,

      WhateverElseFromChild = child.WhateverElse,

      WhateverElseFromParent = parent.WhateverElse,

      ParentId = parent.ParentId

   }

       // pc here is anonymous object created above

 ).Where( pc => pc.Condition == "some condition" ).FirstOrDefault();


Tracing this against SqlServer showed very clean SQL. The exact same SQL produced by this linq equivalent:



var parent_child = ( from parent in entities.Parent

                                join child in entities.Child on parent.ParentId equals child.ChildId

                                where parent.Condition == "some condition"

                                select new

                                {

                                        ChildId = child.ChildId,

                                        WhateverElseFromChild = child.WhateverElse,

                                        WhateverElseFromParent = parent.WhateverElse,

                                        ParentId = parent.ParentId

                                }).FirstOrDefault();

Thursday, November 19, 2009

Handling Extensionless URIs in a WebForms Application

[Edit: I wrote this just a few days before finding out that WebForms 4 does routing out of the box]

ASP.Net MVC is all the rage, but I just needed to add extensionless functionality to a WebForms application. I'm going to host this application in IIS7, so that's how I'm describing it; there is more work involved for IIS6.

This all seems obvious now, but it took me awhile to figure out.

I started by adding a class that supported IHttpHandler. I need to have a handler (rather than a module) because I want to set a value in session, and that's not yet available within a module (at that phase in the IIS pipeline).


public class OnDemandHandler : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
public void ProcessRequest( System.Web.HttpContext context )
{
; // add something to session
}

public bool IsReusable { get { return true; } }
}

<httphandlers>
<add verb="GET" path="*" type="MyNamespace.OnDemandHandler, MyDll" validate="false">



That works fine, but it handles everything. How do I handle aspx pages, or images, or anything else? Copy in all the base handlers from the web.config at the machine level to my web.config? Move my httpHandler config element from my web.config to the machine web.config so it can fall last in order there and only run after all the base handlers have run? Use a module instead of a handler? I was having a hard time figuring out what to do until I decided to go check out a config of an MVC project. After all, they are doing what I want to do:


<httphandlers>
<add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
</httphandlers>
<httpmodules>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
</httpmodules>



Again, it seems obvious now; use a module that checks for extensionless URIs, tack on my own pre-determined extension (just like they're using *.mvc), and send it off to my handler. For URIs with extensions, let them pass on normally.

So here's how it looks in the end:


public class OnDemandModule: IHttpModule
{

#region IHttpModule Members
public void Dispose()
{
}

public void Init( HttpApplication app )
{
app.BeginRequest += new EventHandler( OnBeginRequest );
}
#endregion

private void OnBeginRequest( object src, EventArgs e )
{
HttpContext context = ( src as HttpApplication ).Context;
var uriPath = context.Request.Path;
if ( UriIsExtensionless( uriPath ) )
{
/// Add the .inst extension so our handler will process it
context.Response.Redirect( context.Response.ApplyAppPathModifier( uriPath + ".inst" + context.Request.Url.Query));
}
}

/// <param name="uriPath">like /blah or /something.aspx. Does not contain possible query params</param>
/// <returns></returns>
private bool UriIsExtensionless( string uriPath )
{
var result = false;
if ( uriPath[0].Equals( '/' ) )
{
var restOfURI = uriPath.Substring( 1 );
if ( restOfURI.Length > 0 )
{
return !restOfURI.Contains( '.' );
}
}
return result;
}

}

public class OnDemandHandler : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
public void ProcessRequest( System.Web.HttpContext context )
{
context.Response.Write("OnDemandHandler fired");
context.Response.End();
}

public bool IsReusable { get { return true; } }
}

<!-- for cassini -->
<system.web>
<httphandlers>
<add verb="GET">
path="*.inst"
type="MyNamespace.OnDemandHandler, MyAssembly"
validate="false" />
</httphandlers>
<httpmodules>
<add name="OnDemandModule" type="MyNamespace.OnDemandModule, MyAssembly">
</httpmodules>
</system.web>

<!-- for IIS7 -->
<system.webserver>
<modules runallmanagedmodulesforallrequests="true">
<add name="OnDemandModule">
type="MyNamespace.OnDemandModule, MyAssembly" />
</modules>
<handlers>
<add name="OnDemandSurvey">
verb="GET"
path="*.inst"
preCondition="integratedMode"
type="MyNamespace.OnDemandModule, MyAssembly" />
</handlers>
</system.webServer>

Monday, November 16, 2009

Getting the SQL (and other goodness) from a running transaction

I had run a bunch of SQL scripts against a SQL Server database during a deployment. All tests after the updates checked out. I went to close SSMS and got the message, "There are uncommitted transactions. Do you wish to commit these transactions before closing the window"?

WTF? How could I have failed to commit a transaction yet testing all checked out? Well that's a mystery I haven't yet figured out, but I did find a good query (that I had to alter somewhat to make work) for getting the text of the command that had not yet been committed:


SELECT
  st.Session_id, req.Blocking_Session_ID [Blocker], req.Wait_Type,
  req.Wait_Time [WaitTimeMS], req.Wait_Resource, req.open_transaction_count,
  req.percent_complete, dt.transaction_id, dt.database_transaction_begin_time,
  case when dt.database_transaction_type = 1 then 'RW'
    when dt.database_transaction_type = 2 then 'R'
    when dt.database_transaction_type = 3 then 'Sys'
    else 'Unknown'
end [TranType],
  case when dt.database_transaction_state = 1 then 'Not Initialized'
    when dt.database_transaction_state = 3 then 'Initialized, but no logs'
    when dt.database_transaction_state = 4 then 'Generated logs'
  when dt.database_transaction_state = 5 then 'Prepared'
    when dt.database_transaction_state = 10 then 'Committed'
    when dt.database_transaction_state = 11 then 'Rolled Back'
    when dt.database_transaction_state = 12 then 'In process of committing'
  else 'Unknown'
end [TranState],
  req.Status, req.Command, stxt.objectid [ExecObjID],
(SUBSTRING(stxt.text, req.statement_start_offset/2,( CASE WHEN req.statement_end_offset = -1 then LEN(CONVERT(nvarchar(max), stxt. text)) * 2
        ELSE req.statement_end_offset
      end -req.statement_start_offset)/2)) [SubText],
  stxt.text, req.statement_start_offset
FROM
  sys.dm_tran_database_transactions dt (nolock)
  inner join sys.dm_tran_session_transactions st (nolock) on dt.transaction_id = st.transaction_id
  inner join sys.dm_exec_requests req (nolock) on st.transaction_id = req.transaction_id
  CROSS APPLY sys.dm_exec_sql_text(req.sql_handle) [stxt]
where
  dt.database_id = db_id() and st.is_user_transaction = 1


This query wouldn't work in my situation (would barf "Incorrect syntax near '.'" at the second to last dot), but it will work on a database with compatibility level 90 or better.

Friday, October 09, 2009

IE8 Text Alignment Bug

Below demonstrates a bug in the current version of IE8 (8.0.6001.18702). Two duplicate tables one on top of the other, but the text is not aligning correctly in the second (text-align:left is ignored). This is admittedly an edge case since it only seems to happen with an image in the th element, and the image must apparently be of certain dimensions.












36
This sentence should left align with bob below.











41
bob

Wednesday, September 30, 2009

F# Forward composition operator

I was trying to understand the difference between the plain pipe operator (|>) and the forward composition operator (>>) in F# when the F# forum pointed me to this blog post. After reading the blog post, I still didn't quite understand. I distilled it down to perhaps the simplest possible sample which I think is much easier to understand what it does and why:

let squares wrist =
List.map (fun x -> x*x) wrist

let sum wrist =
List.fold (+) 0 wrist

// Forward composition operator
// don't need to declare any value: it's implied.
let functionComp = squares >> sum

// Equivalent function as above using pipe operator.
// Note how above is more terse
let pipage wrist = wrist |> squares |> sum

let wrist = [1;2;3]

// results are the same
let fc = functionComp wrist
let p = pipage wrist

Friday, June 19, 2009

The Question for .Net Web Developers

Here's what I think is the most important question right now for the .Net developer considering new applications. It may be invalid, but here goes:

Silverlight, or MVC?

To me, silverlight turns the browser into a desktop application; when I worked on silverlight, I no longer thought about GET/POST whatsoever. Yes, I still get data from services, but I'm almost completely unaware of HTTP. Moreover, it seemed to lend itself to very clean applications from a pure OO perspective; same exact OO style on client and server.

With MVC, I'm thinking GET/POST over HTTP all the while. I'm getting back to basics in that regard (stepping back to the way things were before MS took desktop development and jammed it into the Internet with WebForms).

IMO, blending silverlight with MVC is a bastardization (don't take it personally if you're doing it). URLs need only have meaning in silverlight applications as a shortcut to start your application. In any case, I consider WebForms legacy for non-trivial applications. Still very useful when needed, but legacy.

So this is my question for today's .Net developer: "should we think about our new application like we did with desktop apps, or should we consider it RESTful?" Obviously, there are myriad other considerations; I just think this may be the most pertinent.

Wednesday, April 15, 2009

Workaround for changes to DataSet.Merge

We have a formerly open source system (nSurvey) running in production. None of us know too much about it, but we had to learn a little something when we recently upgraded a parent site to (finally) run against .Net framework 2.0. We tried to set the nSurvey site (which had to be a child site) to use framework 1.1 which is possible depending on what you have in the web.config of the parent site (parent as in parent in IIS). In our case, it had AJAX entries which have attributes causes framework 1.1 to blow up. As such we had to upgrade the nSurvey project to Visual Studio 2005 (framework 2.0).

DataSet.Merge changed between .Net frameworks 1.1 and 2.0 when DataSets have namespaces causing the merge to fail. There is a workaround, but the place where it's described on MS docs is totally freaking hosed and makes no sense.

Here is what we did which works fine for us (we're not about to go making big changes in this monster of a system):
string cachedNamespace = dataSet1.Namespace;
dataSet1.Namespace = string.Empty;

string cachedNamespaceChild = dataSet2.Namespace;
answerDataSet.Namespace = string.Empty;

dataSet1.Merge( dataSet2 );

dataSet1.Namespace = cachedNamespace;
dataSet2.Namespace = cachedNamespaceChild;

Wednesday, February 11, 2009

A Functional Deployment Template

We had been using Excel spreadsheets to build out our deployment plans. The biggest chunk of the plan was usually all the database scripts (I prefer listing them separately for better troubleshooting if one fails). Perhaps there is a better way to deploy database changes (SQL Server), but I'm not aware of it.
Anyhow, spreadsheets were not up to the task for a couple reasons:
  • We wanted to be able to have plain written documentation (paragraphs) in addition to some kind of "steps" checklist (we needed to start making complete deployment documents). It's possible to to this in Excel, but certainly not optimal.
  • Editing large scripts in Excel cells can prove clunky. A line length limit makes it nearly impossible (if you try to paste in a script with a line too long nothing happens - no error and no pastey) to edit some large scripts.
We thought about several possible tools, but what we really wanted was a Word template with some tables in it (for steps). But one of the columns of the table needed to have a button (or something) that would pop up a separate editing window for editing the scripts. The edited script would then "collapse" back into it's cell (basically like an Excel cell but with a better mechanism for editing large scripts).

I searched for a fair amount of time and came up with absolutely nothing. I did find a few deployment templates, but nothing with any functionality to them. So I went ahead and implemented such a beast using VSTO over a Word template. Perhaps someone will stumble upon this and find it useful. Implementing it was more challenging than one might think. Just like anything else, if you're not very familiar with VSTO, even small things are going to be difficult, but dynamically creating and persisting WinForms controls is actually sorta tricky.

Anyway, here's what it looks like:


Thursday, February 05, 2009

Stranahan's Colorado Whiskey [off topic]

I was going to put this in an email, but I figured, what the heck. Maybe some whiskey connoisseur will find it.

I have gift cards from Christmas, and I keep going back to Tipsy's (the mega-liquor-store near my house) to attempt to get some Leopold's (whose flavored whiskeys are still awesome) but they never have it - Leopold's production is low and demand high. So I asked an employee if I could get a rain-check or something. He asked if I wanted Stranahan's explaining that some just came in but it's going fast. He directed me to the front desk where I asked for a little form to place a request and the next employee said, "for Stranahan's?" Then a customer came up with a bottle of Stranahan's Colorado Whiskey. The manager and customer started raving about it. I asked them what it compares to, and they couldn't tell me. The manager said, "it tastes like something that costs way, way more." I said, "it already costs $60." He just said, "I know" and then said you can't believe how good it is. The customer said he'd open it and let me try if he could.

So I figured what the heck, I got a gift card, I'll try some. They were right. I've never had 250 y/o whiskey, but I'd imagine it tastes something like this stuff. Toward the middle of my glass I swear there was no burn on the tongue whatsoever. And it doesn't compare with anything (the first sip tasted completely foreign). It just tastes... clean and fresh. It's amazing. Apparently, there's much more to making smooth whiskey than age.

IMO this stuff is like the next level. Nothing I've had is anything close to as good.

Friday, January 30, 2009

Silverlight the Right Choice for Web Applications

I've written about how I think that web applications are a hack. In that post I didn't think Silverlight was fully the answer, but at this point I think it's the best alternative.

I couldn't be more strongly in favor of using Silverlight for web applications (as opposed to web sites which only provide content) wherever possible. Of course even content can be a good use of Silverlight as the NYT reader shows. If an MS shop's target audience doesn't include IT goons who somehow put Silverlight in a different category than Acrobat Reader, I don't think you have any other choice for new applications.

My coworkers are spending many hours trying to make one AJAX codebase work on different browsers. After awhile it just gets demoralizing (and my employer is wasting tons of maintenance time). My mind is boggled by all the hackage necessary to make AJAX popup "windows" drag/drop via HTML (sure Javascript makes it happen, but it's still just HTML). With Silverlight you're working on a platform designed for this kind of thing. It's designed for dragging and dropping and animations and all the other things needed for applications (not to mention streaming).

Yes, you can make all those things happen with Javascript, and I fully understand the power of functional languages, but who would rather maintain a line of business application written in Javascript in favor of C#? Not to mention the aforementioned ever growing browser compatibility issues.

How do loosely typed Javascript objects compare with full featured Silverlight controls with UI and logic contained in one distinct entity combined with design time configuration? Not very well on the maintainability scale.

That's how I cast my vote, anyway. Long live Silverlight. I doubt it will be a technology that fades away, but I hope it's user base grows and it continues to stay healthy in the future.

SSIS Gotchas

SQL Server Integration Services is very finicky (read “gives indecipherable errors and will make your life miserable”). I wrote some packages using VS2008 / SQL Server 2008. Following is a list of gotchas I ran across. Note that there’s no frigging way you can figure any of these out from the error messages alone:
  • Restoring a database renders connections in packages unusable (even though everything looks good and testing the connection is fine). Thus, every time you restore a database that a package is pointing to (which I was doing repeatedly during testing), you must go into the package, add a new connection from a data source (by right clicking in the connection manager area of each package) and add in the same source that’s already being used (giving it a different name). Then you must go into each task item in the package and point to the new connection. If that task had mappings (as from a flat file to a table), you must re-map everything. Enjoy!
  • If you’re executing a package from .Net code, only the .Net connection types will run.
    • There are 3 different forms of essentially the same connection type: “Native OLE DB”, “.Net providers for OleDb”, and “.Net Providers”
    • The “.Net Providers” didn’t seem to work at all; the “Native OLE DB” providers would not execute when attempting to run the packages from a web page on a different server; both the “Native OLE DB” and “.Net providers for OleDb” worked from my local machine.
    • It seems the proper choice for calling packages from code is “.Net providers for OleDb”.
    • The “.Net providers for OleDb” will not execute from code directly from within visual studio (i.e., you can’t right click on the package and click execute) so you can't debug directly.
    • The .Net connections ignore their timeout property. They simply won't work when running a long query.
    • Enjoy!
  • You can't include a package that deals with a table that doesn't yet exist.
    • So you can’t have a package that refers to a table that hasn’t yet been created. The package will give errors all over the place. So, for instance (IIRC), you can’t deploy a package that refers to a table that will be created by another package b/c it won’t validate on deployment.
  • Deployment of packages is a nightmare. There are myriad issues surrounding the demonic property of packages, “ProtectionLevel”.
    • Under most circumstances, you would want to make your connection string properties configurable at the server. That way, you can move the packages around or point them to different servers w/o having to redeploy them. In my case, I had no end of problems with this (and the packages need only run annually) so I just deployed and ran this way:
      • For deployment, I would just do file | Save copy of [package] as and copy the package to the server running integration services. This would just embed the connection settings and not rely on any package configuration files. Then, for executing the packages from code, I would just refer to the server I copied the packages to.
    • At one point, I had a package that had a bad path in one of its connection’s properties. This seemed to prevent all packages from working.
    • Most destructive: Changing the connections and re-deploying (as described above) doesn't overwrite the connections. So I changed the connections from production back to QA, verified everything multiple times, redeployed (which prompts for confirmation whether I want to overwrite the existing packages), and ran a package. Much to my surprise & chagrin, the package executed against production! In this case, the existing packages must be deleted. In Reporting Services, there is some “overwrite data sources” property, but I couldn’t find this in SSIS.
  • Another disturbing issue: packages run where they’re called from. If, in code, you execute some SQL against a particular server, that SQL will execute on the server. Confusingly (b/c I was importing and exporting data), when you execute a package, it executes on the machine where it was called, not on the server where the package exists!
  • Warnings on package objects show up all over the place. There's no figuring out why they don't consistently work. E.G., login failure when there is nothing wrong with the connection. I think setting the property “BypassPrepare” to true helped.
  • Unlike the predecessor technology, DTS, there is no way to switch a connection vis-a-vis data flow objects w/o losing mappings. Enjoy!
  • Names of all new packages are Package1 – you must rename them and delete file Package1 manually.
  • If you have an extra line break at the end of a script task, it crashes. You can copy/paste that script into management studio and it will work fine.
  • Closing / reopening the project sometimes causes connection references from tasks to be lost.
  • Yet another disturbing issue: I was not able to get it down to a reproducible set of steps, but the project was reverting my script changes. I found that I had to check my script changes into sourcesafe immediately after saving every time (I suspect that the reverting took place somewhere in deploying).
The fun part is that you can’t compare versions of packages in sourcesafe b/c a simple change to a script task changes all sorts of stuff in the dtsx file.