Thursday, November 18, 2010

String Array UDT for Oracle, and Something Better

For work I was creating a simple website that allowed a user to batch multiple items together.  I was planning on taking the id’s of the items that they batched, and passing them into an Oracle SP, so they could be used in a “Where in” statement.  In MS-SQL you can hack together a solution doing some fancy string parsing, and the Execute query function, but you have lots of issues if your csv string is longer than 8000 characters.  I wanted to do it the “right” way in Oracle using a UDT (User Defined Type).



Basically to be able to pass an Oracle UDT as a parameter to a SP from C#, you have to create a C# class for your UDT which implements IOracleCustomType, INullable, and a factory to create the objects. First, here is the Oracle Code for the UDT and the SP:



create or replace

TYPE STRINGARRAY_UDT AS varray(3000) OF VARCHAR2(30);


create or replace

PROCEDURE TEST_ARRAY

(

  IN_MODELS IN STRINGARRAY_UDT,

  OUT_MODELS OUT SYS_REFCURSOR

) AS


BEGIN

  OPEN OUT_MODELS FOR

    SELECT * from Items WHERE id IN (SELECT * FROM table(IN_MODELS));

END TEST_ARRAY;



The C# code is rather simple as well, but you have to create a class for each UDT that you want to use.  This is a real pain (which leads me onto something better).  If you want to create a UDT with types that are not nullable, you have to maintain your own OracleStatus array to determine if the element is null or not, and pass it into the OractleUdt.SetValue and OracleUdt.GetValue.  The StringArrayUDTFactory get’s called by Oracle to create the object, it then uses reflection to call the FromCustomeObject or ToCustomObject methods, depending on whether you’re passing the UDT to oracle, or retrieving it from Oracle.



public class StringArrayUDT : IOracleCustomType, INullable
{
[
OracleArrayMapping()]
public string[] Array { get; set; }

public StringArrayUDT()
{
Array =
null;
}

public StringArrayUDT(string[] array)
{
Array = array;
}

#region INullable Members

public bool IsNull { get { return Array == null; } }

#endregion

#region IOracleCustomType Members

public void FromCustomObject(OracleConnection con, System.IntPtr pUdt)
{
OracleUdt.SetValue(con, pUdt, 0, Array);
}

public void ToCustomObject(OracleConnection con, System.IntPtr pUdt)
{
Array = (
string[])OracleUdt.GetValue(con, pUdt, 0);
}

public override string ToString()
{
if (IsNull)
return "NULL";
else
{
return "StringArrayUDT('" + String.Join("', '", Array) + "')";
}
}

#endregion
}

/* StringArrayUDTFactory Class
** An instance of the StringArrayUDTFactory class is used to create
** StringArrayUDT objects
*/
OracleCustomTypeMapping("MySchema.STRINGARRAY_UDT")]
public class StringArrayUDTFactory : IOracleCustomTypeFactory, IOracleArrayTypeFactory
{
#region IOracleCustomTypeFactory Members

public IOracleCustomType CreateObject()
{
return new StringArrayUDT();
}

#endregion

#region IOracleArrayTypeFactory Members

public System.Array CreateArray(int numElems)
{
return new String[numElems];
}

public System.Array CreateStatusArray(int numElems)
{
return new OracleUdtStatus[numElems];
}

#endregion
}



Once all of this gets created, you can add your class as a parameter directly to an OracleCommand object for an SP.  But after doing all that work, with all of it’s custom code, my co-worker showed me a better way for this example, done entirely in Oracle.  The bulk of the work is done in this SplitByChar function:



FUNCTION SPLITBYCHAR

(

    p_list varchar2,

    p_del varchar2 := ','

) return split_tbl pipelined

is

    l_idx    pls_integer;

    l_list    varchar2(32767) := p_list;

    l_value    varchar2(32767);

begin

    loop

        l_idx := instr(l_list,p_del);

        if l_idx > 0 then

            pipe row(substr(l_list,1,l_idx-1));

            l_list := substr(l_list,l_idx+length(p_del));

        else

            pipe row(l_list);

            exit;

        end if;

    end loop;

    return;

END SPLITBYCHAR;



And then our Test Function becomes this:


create or replace


PROCEDURE TEST_ARRAY


(


  IN_MODELS IN varchar2,


  OUT_MODELS OUT SYS_REFCURSOR


) AS


BEGIN


  OPEN OUT_MODELS FOR


    SELECT * from Items WHERE id IN (SELECT * FROM table(SPLITBYCHAR(IN_MODELS)));


END TEST_ARRAY;

Where IN_MODELS is a csv string.  This puts a little more work on the Oracle database, but is much better on the application side of things.

Tuesday, October 19, 2010

Creating a ToDictionaryList Extension

ToDictionary

.Net 3.5 introduced the Enumerable.ToDictionary Method. It allows you to take a list of items, and convert them into a dictionary based on some key given as a lambda expression. For example, this code snippet for Snippet Compiler will put a weeks worth of dates into a Dictionary<int,DateTime>, so you could do a dictionary lookup by day:

public static void RunSnippet()
{
DateTime time = new DateTime(2228, 3, 22);
List<DateTime> items = new List<DateTime>();
items.Add(time);
items.Add(time.AddDays(1));
items.Add(time.AddDays(2));
items.Add(time.AddDays(3));
items.Add(time.AddDays(4));
items.Add(time.AddDays(5));
items.Add(time.AddDays(6));

var dayByDay = items.ToDictionary(d => d.Day);
foreach (var dayKeyValue in dayByDay)
{
WL(dayKeyValue.Key + " " + dayKeyValue.Value);
}
}

Which results in this output:

22 3/22/2228 12:00:00 AM
23 3/23/2228 12:00:00 AM
24 3/24/2228 12:00:00 AM
25 3/25/2228 12:00:00 AM
26 3/26/2228 12:00:00 AM
27 3/27/2228 12:00:00 AM
28 3/28/2228 12:00:00 AM
Press any key to continue...



ToDictionaryList


But what if the key you want to use, is not unique? You’ll end up getting this warning:

System.ArgumentException: An item with the same key has already been added.
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)

I had this same issue and decided to create a ToDictionaryList() extension method that would return a Dictionary<key,List<item>>. Here is an example using the ToDictionaryList() grouping the dateTimes by day:



public static void RunSnippet()
{
DateTime time = new DateTime(2228, 3, 22);
List<DateTime> items = new List<DateTime>();
items.Add(time);
items.Add(time.AddHours(1));
items.Add(time.AddMinutes(1));
items.Add(time.AddDays(1));
items.Add(time.AddHours(26));

var results = items.ToDictionaryList(d => d.Day);
foreach (var result in results)
{
foreach (var item in result.Value)
{
WL(result.Key + " " + item);
}
}
}


Which results in this output:

22 3/22/2228 12:00:00 AM
22 3/22/2228 1:00:00 AM
22 3/22/2228 12:01:00 AM
23 3/23/2228 12:00:00 AM
23 3/23/2228 2:00:00 AM
Press any key to continue...


Basically it is two dictionary entries, one for the 22nd which is a List<DateTime> of 3 DateTimes, and one for the 23, which contains two items. You can even write a lambda expression for the type of item to be in the list. So in the last example, if you changed "var results = items.ToDictionaryList(d => d.Day);" to "var results = items.ToDictionaryList(d => d.Day, d => d.Hour); " item would in int and which would result in this output:

22 0
22 1
22 0
23 0
23 2
Press any key to continue...


The Code


Here is the code required to make the ToDictionaryList Possible:


public static class Extensions
{
private static Func<TElement, TElement> Instance<TElement>()
{
return delegate(TElement x) { return x; };
}

public static Dictionary<TKey, List<TSource>> ToDictionaryList<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
return source.ToDictionaryList(keySelector, Instance<TSource>());
}

public static Dictionary<TKey, List<TElement>> ToDictionaryList<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
{
Dictionary<TKey, List<TElement>> dictionary = new Dictionary<TKey, List<TElement>>();
List<TElement> elements;
foreach (TSource local in source)
{
if (!dictionary.TryGetValue(keySelector(local), out elements))
{
elements = new List<TElement>();
dictionary.Add(keySelector(local), elements);
}
elements.Add(elementSelector(local));
}

return dictionary;
}
}



The Instance Method is used to return a delegate function that returns a single item of the given type (Basically a (c => c)). The ToDictionaryList() will loop through all items in the list, adding them to a new list if the key doesn’t already exist in the dictionary, or add the item to the already existing list in the dictionary. Pretty simple really.

Smile

Wednesday, May 5, 2010

Correctly Creating Classes Using xsd.exe

Not a lot of people are familiar with the xsd.exe that ships with Visual Studio. It allows you to create XML/classes from XML/classes. The following are the 4.0 xsd.exe capabilities:
XDR to XSD
Generates an XML schema from an XML-Data-Reduced schema file. XDR is an early XML-based schema format.
XML to XSD
Generates an XML schema from an XML file.
XSD to DataSet
Generates common language runtime DataSet classes from an XSD schema file. The generated classes provide a rich object model for regular XML data.
XSD to Classes
Generates runtime classes from an XSD schema file. The generated classes can be used in conjunction with System.Xml.Serialization.XmlSerializer to read and write XML code that follows the schema.
Classes to XSD
Generates an XML schema from a type or types in a runtime assembly file. The generated schema defines the XML format used by System.Xml.Serialization.XmlSerializer.
It I’m a big fan of using xml schemas (XSDs) to generate classes that can be used to serialize and deserialize objects to and from XML (the XSD to Classes functionality listed above). If your schema changes, just rerunning xsd.exe for the correct schema updates the classes. No manually changes have to be made, including serialization code. It’s a beautiful thing. The problem has been, how do you set up your classes to automatically recompile with changes to the schema, and how do you deal with schemas that import other schemas?

XSDs Importing other XSDs

A common issue that developers of XSDs run into is violating the DRY principle repeatedly with XSD types. For example, let’s say you create a calendar meeting request service that has two XSDs, one for the request XML and one for the response XML. You’ve defined a xs:complexType “Meeting” that includes the date and location:
  <xs:complexType name="Meeting">
    <xs:sequence>
      <xs:element name="Location" type="xs:string"/>
      <xs:element name="Date" type="xs:date"/>
    </xs:sequence>
  </xs:complexType>
But you want to use it in both the request and the response XML. You could just copy and past it into both XSD files, and it will validate just fine, but if you use xsd.exe to generate your classes, it’s going to create two classes of type Meeting, which will cause a compiler error. You could have a separate namespace for each class, but then you’re definitely violating DRY. The answer is to place the Meeting type in a separate XSD and then reference it from both your request and your response XSD. This results in the XSDs below

C:\Solution\Project\Types.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Types"
targetNamespace="http://www.Example.com/Types"
elementFormDefault="qualified"
xmlns="http://www.Example.com/Types"
xmlns:mstns="http://www.Example.com/Types"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
  <xs:complexType name="Meeting">
    <xs:sequence>
      <xs:element name="Location" type="xs:string"/>
      <xs:element name="Date" type="xs:date"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>


C:\Solution\Project\Request.xsd
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:myTypes="http://www.Example.com/Types">
  <xs:import namespace="http://www.Example.com/Types" schemaLocation="Types.xsd" />
  <xs:element name="Request">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="RqstMeeting" type="myTypes:Meeting"/>
        <xs:element name="RqstName" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

C:\Solution\Project\Response.xsd
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:myTypes="http://www.Example.com/Types">
  <xs:import namespace="http://www.Example.com/Types" schemaLocation="Types.xsd" />
  <xs:element name="Response">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Accepted" type="xs:boolean"/>
        <xs:element name="AlternateMeeting" type="myTypes:Meeting" minOccurs="0"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>


Now we’ve defined our Meeting type in one file, and reused it in both our Request.xsd and Response.xsd.


Getting xsd.exe To Import XSDs

Now that the type has been defined in another file, the xsd.exe will generate this error if you attempt to create the create the Request XML:
C:\Solution\Project>xsd.exe Request.xsd /c
Schema validation warning: Type 'http://www.Example.com/Types:Meeting' is not declared.
Warning: Schema could not be validated. Class generation may fail or may produce incorrect results.
Error: Error generating classes for schema 'C:\Solution\Projects\Request'.

- The datatype 'http://www.Example.com/Types:Meeting' is missing.
If you would like more help, please type "xsd /?".
This is due to the fact that the xsd.exe does not use the schemaLocation hint to find the imported schema. You’ve got to include it as a parameter. in your xsd.exe call:
C:\Solution\Project>xsd.exe Types.xsd Request.xsd /c
This will generate one file call Request.cs that has a Request class, and a Meeting class. Now we just need to create the Response class and we’re good to go. But wait… running “C:\Solution\Project>xsd.exe Types.xsd Response.xsd /c” will create a different file, Response.cs, that contains a Response class and a duplicate Meeting class. Now we’re stuck with another compiler error and no longer DRY.


Getting xsd.exe To Not Create Duplicate Classes

This is a simple fix, but it took me a long time to figure out. You have to use xsd.exe to compile all of your classes at once, so rather than running two separate commands, you just need to run one:
C:\Solution\Project>xsd.exe Types.xsd Request.xsd Response.xsd /c
Now you have one file, Response.xsd, with all three classes in it.

Getting Visual Studio 2010 To Auto Recompile XSD Generated Classes

Using the Project Build Events, you can set the project to always recompile the XSD classes each time you build the project. It is also helpful to rename the file so it isn’t always the name of the last XSD file passed to xsd.exe. Here are the Pre-build event command line values required to auto build the XSD classes and rename the file to XsdGeneratedClasses.cs:

EDIT suggested by Jamie instead of the Registry hack (it worked back in the day for me, don't know if it still does), he suggests using "$(TargetFrameworkSDKToolsDirectory)xsd.exe" to find the path of the xsd.exe. Thanks Jamie!

"$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A\@InstallationFolder)\bin\NETFX 4.0 Tools\xsd.exe" "$(ProjectDir)Request.xsd" "$(ProjectDir)Response.xsd" "$(ProjectDir)Types.xsd" /c /o:"$(ProjectDir)"
move "$(ProjectDir)Types.cs" "$(ProjectDir)XsdGeneratedClasses.cs"

Now whenever the project get’s built, the XSD generated classes will always be rewritten by xsd.exe



Extending XSD Generated Classes

Don’t forget that classes created by xsd.exe are all partial classes. It’s a good idea to add default constructors and logic in a separate partial class in a different file. It’s especially helpful for initializing arrays since xsd.exe generated classes use arrays and not ArrayLists or Generic Lists. This allows you to add logic, that won’t be changed when the class is regenerated.


Serializing/Deserializing XSD Generated Classes

Now your code for Serializing and Deserializing your objects is as simple as this:
To Serialize:

XmlSerializer s = new XmlSerializer(typeof(Request));System.IO.TextWriter w = new System.IO.StreamWriter(@"C:\Request.xml");s.Serialize(w, new Request());w.Close();


To Deserialize:

XmlSerializer s = new XmlSerializer(typeof(Request));Request request;System.IO.TextReader r = new System.IO.StreamReader("request.xml");request = (Request)s.Deserialize(r);r.Close();

Wednesday, April 21, 2010

How To Configure AllMargins Extension For Visual Studio 2010

UPDATE: Removed non AllMargins registry settings. Thanks David Pugh for letting me know.


UPDATE 2: Productivity Power Tools now combines most of the major features of AllMargins, due to David Pugh himself porting it over. Consider using it instead.


AllMargins is a VS 2010 extension that basically replaces Rockscroll from VS2005 and VS2008. It combines multiple extensions into one “All” inclusive extension. Here is an example of what mine looks like with a few features pointed out:

image

It looks pretty good now, but when you first install it, it is configured for a white background, rather than black. You can change the colors via the registry at \HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Text Editor, but I couldn’t find any documentation online as to what color controls what. I downloaded the code from code.msn.microsoft.com and discovered how it all worked.

AllMargin Extensions

AllMargins contains multiple extensions, but all of their configurable settings are located in the registry at \HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Text Editor. I’ve listed them by extension below along with the introduction and usage text I’ve pulled together from the readme files in the projects. Just edit the appropriate registry setting and open up a new window in VS2010, to see any changes you’ve made.

CaretMargin

This extension adds a margin to the OverviewMargin that shows the location of the caret and all words that match the word the caret is on.

You will see colored rectangles drawn that correspond to the location of the caret and all words that match the word the caret is in or adjacent to.

Registry Name Description
CaretMargin/MarginEnabled Should the CaretMargin be displayed
CaretMargin/MarginWidth Width of the CaretMargin (in pixels)
CaretMargin/CaretColor Color used to draw the location of the caret. Not used if Alpha = 0
CaretMargin/MatchColor Color used to draw the location of matching words. Not used if Alpha = 0


OverviewMarginImpl

This extension creates a margin displayed along the right edge of the editor. It, like the vertical scroll bar, logically maps to the entire file. By default, it acts as if all elided regions are expanded (though the portions of the file contained in a elided regions are highlighted). The OverviewMargin also can highlights the portions of the file that are above, contained in and below the text shown in the editor.

The OverviewMargin is a container for other margins (that all use a common mapping between a location on the screen and a position in the file). This extension includes two of these contained margins: the OverviewChangeTracking margin (which acts like the change tracking margin from the left side of the editor, but applied to the entire file) and the OverviewMark margin (which allows 3rd parties to draw colored marks to indicate positions of interest in the file).

Moving the mouse into the OverviewMargin will, after a brief delay, show a tooltip. The default tip for the OverviewMargin shows the contents of the file at the corresponding location but other margins contained in the OverviewMargin may provide more interesting tips.

Left clicking in the OverviewMargin to scroll to center the corresponding location in the editor. Left clicking in an elided region will center the(collapsed) elided region in the editor.

Double-left-clicking in the OverviewMargin will expand any elided regions that contain the corresponding location and then center that location in the editor.

Right-clicking in the OverviewMargin brings up an option menu that allow you to change:
Whether or not the OverviewMargin acts as if all elided regions are expanded (if they are not, then the OverviewMargin uses the same mapping between a location in the margin and a position in the file as the vertical scroll bar).
Whether or not the OverviewMargin will show the default tip when the mouse is inside the margin.

Registry Name Description
OverviewMargin/ChangeTrackingMarginEnabled Should the change tracking margin be displayed
OverviewMargin/MarkMarginEnabled Should the mark margin be displayed
OverviewMargin/ExpandElisionsInOverviewMargin Are elisions expended in the overview margin?
OverviewMargin/OverviewMarginPreviewSize Number of lines to show in the default tip for the overview margin. If 0, do not show any tip by default
OverviewMarginImpl/ChangeTrackingMarginWidth Width of the ChangeTrackingMargin (in pixels)
OverviewMarginImpl/MarkMarginWidth Width of the MarkMargin (in pixels)
OverviewMarginImpl/ElisionColor Color used to highlight elided text as ARGB. Not used if Alpha = 0.
OverviewMarginImpl/OffScreenColor Color used to highlight portions of the file not shown in the editor as ARGB. Not used if Alpha = 0.
OverviewMarginImpl/VisibleColor Color used to highlight portions of the file shown in the editor as ARGB. Not used if Alpha = 0.


Structure Adornment

This extension displays vertical lines in the editor to help show the block structure of the code file. Different colors are used to indicate different types of blocks.

Moving the mouse over one of the colored lines will, if the line extends off the top of the editor, create a tooltip at the top of the view that provides some context on what is out of view.

Registry Name Description
StructureAdornment/ClassColor Color used to show the scope of class definitions as ARGB
StructureAdornment/ConditionalColor Color used to show the scope of conditionally executed code as ARGB
StructureAdornment/LoopColor Color used to show the scope of loop bodies as ARGB
StructureAdornment/MethodColor Color used to show the scope of method bodies as ARGB
StructureAdornment/UnknownColor Color used to show the scope of unknown blocks as ARGB
StructureAdornment/Enabled Should the structure adornments be shown?


StructureMargin

This extension adds a margin to the OverviewMargin that shows the block structure of the code file. Different colors are used to indicate different types of blocks.

Moving the mouse over the structure margin will create a tooltip that provides some context on what is in the file at that location.

Registry Name Description
StructureMargin/ClassColor Color used to show the scope of class definitions as ARGB
StructureMargin/ConditionalColor Color used to show the scope of conditionally executed code as ARGB
StructureMargin/LoopColor Color used to show the scope of loop bodies as ARGB
StructureMargin/MethodColor Color used to show the scope of method bodies as ARGB
StructureMargin/UnknownColor Color used to show the scope of unknown blocks as ARGB
StructureMargin/MarginEnabled Should the StructureMargin be displayed
StructureMargin/MarginWidth Width of the StructureMargin (in pixels)
StructureMargin/MethodEllipseColor Color used to draw the ellipse that shows the scope of method bodies as ARGB. Not used if Alpha = 0


My Settings

Here are my setting that I use. If you want to use them, just save them as a .reg file to add/update them in the registry


Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Text Editor]
"StructureAdornment/ClassColor"="#FF5F7D7C"
"StructureAdornment/ConditionalColor"="#FF005300"
"StructureAdornment/LoopColor"="#FFFF0000"
"StructureAdornment/MethodColor"="#FF222290"
"StructureAdornment/UnknownColor"="#FF362844"
"StructureAdornment/Enabled"="True"
"OverviewMargin/ExpandElisionsInOverviewMargin"="True"
"OverviewMargin/PreviewSize"="20"
"OverviewMarginImpl/ElisionColor"="#404252FF"
"OverviewMarginImpl/OffScreenColor"="#FF000000"
"OverviewMarginImpl/VisibleColor"="#88444444"
"OverviewMargin/ChangeTrackingMarginEnabled"="True"
"OverviewMarginImpl/ChangeTrackingMarginWidth"="9"
"OverviewMargin/MarkMarginEnabled"="True"
"OverviewMarginImpl/MarkMarginWidth"="8"
"CaretMargin/Enabled"="True"
"CaretMargin/CaretColor"="#AADDFFFF"
"CaretMargin/MatchColor"="#FF6F8474"
"CaretMargin/MarginWidth"="7"
"StructureMargin/MarginEnabled"="True"
"StructureMargin/MarginWidth"="25"
"StructureMargin/MethodEllipseColor"="#20DDDDDD"
"StructureMargin/ClassColor"="#FF38E4DB"
"StructureMargin/ConditionalColor"="#FFAF22C8"
"StructureMargin/LoopColor"="#FF0AC034"
"StructureMargin/MethodColor"="#FFE86D0E"
"StructureMargin/UnknownColor"="#FFE8DE1B"

Thursday, February 4, 2010

Testing a Windows Service From Within Visual Studio

When working with Windows Services, you can’t set the windows service project as the “Startup Project” and start debugging. You get a Windows Service Start Failure dialog box telling you that you "Cannot start service from the command line or a debugger. A Windows Service must first be installed (using installutil.exe) and then started with the Server Explorer, Windows Services Administrative tool or the Net Start Command.

In order to get around this issue, a common approach I’ve seen is to add a windows form to the windows service project, and then in the project’s Program.Main() method, comment out the ServiceBase.Run() and add this code to run the windows form instead:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());

This will result in your TestForm running instead of your actual service and you can add whatever features that you want to the TestForm Form to be able to debug your service (IMHO it’s also good to add some cleanup functionality to this test form, like reprocess transaction 9999). If the Windows Service ever needs to be rebuilt, the changes to Program.cs have to be reverted. The result is a Program.cs file that get checked into source control looking like this:

static class Program

/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;

// More than one user Service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = new ServiceBase[] {new Service1(), new MySecondUserService()};
//
ServicesToRun = new ServiceBase[] { new Service() };

ServiceBase.Run(ServicesToRun);

//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new TestForm());

}
}

and being changed to look like this:

static class Program

/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;

// More than one user Service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = new ServiceBase[] {new Service1(), new MySecondUserService()};
//
ServicesToRun = new ServiceBase[] { new Service() };

//ServiceBase.Run(ServicesToRun);

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());

}
}

whenever it needs to be ran in Visual Studio. This can become really annoying if you’re doing this multiple times a day on different services, or your source control provider doesn’t support multiple simultaneous checkouts. It can be even worse if the build for the service takes a long time, and you forget to comment/uncomment the right lines, and there is always the risk that you check in the Windows Form version of the service.

So, because I’m a lazy programmer (If I have to do something twice, that’s one too many times) I figured out a solution utilizing the System.Diagnostics.Debugger.IsAttached property. I created a simple static class with a static constructor that accepts two parameters. One for the services that should be ran if we aren’t starting this in Visual Studio, and another for the Windows Form Type to use as the test form if debugging. It ended up looking like this:

public static class ServiceApplication

/// <summary>
/// Deteremines if the service is being ran in debug mode.
/// If it is, the testFormType is ran, if not, the service is started normally
/// </summary>
/// <param name="servicesToRun">The services to run</param>
/// <param name="testFormType">The Form type that is the test form for the service</param>
public static void Run(ServiceBase[] servicesToRun, Type testFormType)
{
if (System.Diagnostics.Debugger.IsAttached)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form testForm = (Form)Activator.CreateInstance(testFormType);
Application.Run(testForm);
}
else
{
ServiceBase.Run(servicesToRun);
}
}
}

And now my Program.cs looks like this:

static class Program

/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceApplication.Run(new ServiceBase[] { new Service() }, typeof(TestForm));
}
}

No more editing the program.cs file! It just works the way I want it to!