Monday, December 7, 2009

Ensuring lambdas don't leak when passed to EventHandlers

I'm writing a wrapper around a class which outputs results of commands executed into a stream buffer.  I wanted to capture the command text and return that to the caller of my wrapper.  I wanted to use a lambda and clean up my delegate after the command returned.  This contrived code sample shows how to do this improperly and properly:



static void Main()
{
    ExecuteCommand();
    Debug.Assert(CommandExecuted == null);
    ExecuteCommandLeaks();
    //This assert will fail
    Debug.Assert(CommandExecuted == null);
}

static void ExecuteCommandLeaks()
{
    CommandExecuted += new EventHandler<EventArgs>((o, e) => Console.WriteLine("Event called"));

    CommandExecuted.Invoke(new object(), new EventArgs());
}

static void ExecuteCommand()
{
    var commandCapture = new EventHandler<EventArgs>((o, e) => Console.WriteLine("Event called"));
    CommandExecuted += commandCapture;

    CommandExecuted.Invoke(new object(), new EventArgs());

    CommandExecuted -= commandCapture;
}

static EventHandler<EventArgs> CommandExecuted;


Monday, November 23, 2009

SQL Server Management Studio Generating Change Scripts

For those of you who need to lookup SQL syntax for things like alter table, this will let SQL Server Management Studio (SSMS) do the work for you giving you a baseline script to modify or check right into source control.

Generating an Alter Table Statement:

  1. In SSMS use the object explorer to navigate to a table.
  2. Right click and select design.
  3. Make your changes.
  4. From the menu select Table Designer --> "Generate Change Script..."
  5. Copy and paste the script text or click the yes button to save it to a text file.

Adding a Foreign Key:

  1. In SSMS use the object explorer to navigate to a table.
  2. Expand the table node.
  3. Right click on Keys  and select "New Foreign Key..."  Notice this brings up the table designer if it isn't already open.
  4. Fill out the new FK and click Close
  5. You should now be in the table designer.  Follow the steps from above.

Monday, November 9, 2009

Delete a Blogger Comment

To delete a blogger comment, like spam, the easiest way I have found to do it is by

  1. Opening the post I want to delete in Chrome (the technique should work in any browser, but Chrome is my favorite because it is much faster than IE and because I like the Google brand better than FireFox)
  2. Right Clicking on the page and selecting "View page source"
  3. Press ctrl+f to bring up the search box and enter "delete".
  4. Look for the delete link.  It was the second search result on the page for me and looked like this: <a href='http://www.blogger.com/delete-comment.g?blogID=5094545033872573936&postID=6517665474722511356' title='Delete Comment'>
  5. Click the link.
  6. Follow the directions on the next page confirming the deletion.
There is probably a way to make the delete button show up on the page but I don't know how.

Monday, November 2, 2009

TEDxDetroit recap

My cousin, Gabriel Mott, turned me on to TED in 2005 by linking to what is still my favorite TED talk. I was excited to be accepted as an attendee of the inaugural TEDxDetroit event on 10/21/2009 at Lawrence Tech University.

The most beneficial part for me was speaking with other attendees such as Alex Fisher, Henry Balanon, Bret Kopf, Betsy Weber and meeting new people such as Lisamarie Babik, David Benjamin, Andrew Brown, Jamie Favreau, Lee Andros (bus buddy!), Janak Mehta, Rich Sheridan, and Dug Song.

Here are my take aways:

  • Ask high quality questions.  High quality questions provoke thought.  An example could be "How can we generate y revenue through x technology?",  "How can social media help solve world hunger?"
  • What can we do now given the current technology and environment?  If our plans depend on a change (new technology, etc) what if that change doesn't manifest?  If our plans don't consider the current environment (ie. culture) how will the implementation go?
  • There are two ways to make money, by nickel and diming the competition or by taking big leaps over them.  Giants take big leaps.  Who are your giants?
  • Users are not stupid. Designs are stupid.  Observer your users in their environment and reduce their pain.
  • Some people are economic hurricanes.  Everyone has great ideas.  Those who execute on those ideas are the hurricanes.
  • Research the problem before providing the solution.  When we have ideas we think of our solution and present that.  Instead of presenting that, present the problem inspiring your solution and see what others have to say.
  • At a certain level of wealth problems are problems of perception.  This was a particularly interesting talk for me because I tend to think in functional terms instead of presentation.
    • All value is relative.
    • Persuasion is better than mandates.
    • Contextual timely and immediate.
P.S. If you would like to attend a regional TED event, TEDxLansing is seeking volunteers.

Monday, October 12, 2009

Using gmail from C# and inspecting HttpRequests

This post can help you with two things

  1. Sending mail via gmail or google apps through C#.
  2. Inspecting the contents of an HttpRequest as a string similar to HttpRequest.SaveAs.


Today while working on a code for fun project at lunchtime we wanted to see the HttpContext.Request.  Initially we were calling HttpRequest.SaveAs(context.Server.MapPath("Request.txt"),true) and life was good.  When we deployed to our test server hosted by Verio, an awesome company, we got an access denied exception when trying to write.  Our corporate firewall prevented us from changing the folder permissions, and we couldn't figure out how to do it through our ftp client, FileZilla.  We decided to email the request to ourselves with the following code:

SmtpClient mailer = new SmtpClient("smtp.gmail.com", 587);
//mailer.EnableSsl = true;
mailer.Credentials = new NetworkCredential("emailAddress@yourDomain.com", "YourPassword");
mailer.Send("sender@sender.com", "recipient@recipient.com", "snazzySubject", Request.RequestAsString());

Running that code we get: System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 216.239.59.109:587.  This is our firewall blocking us again.

At home I got "The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.7.0 Must issue a STARTTLS command first."

Uncommenting the mailer.EnableSsl line above and rerunning fixed the problem and my email arrived!

//End point #1 and begin point #2
Here is the code duplicating the request.saveAs functionality:  RequestAsString is an extension method.  In my code I put it in the System.Web namespace so it shows up for me when using System.Web.


/// <summary>
/// Turns an HttpRequest into a string for inspection.  Similar to request.SaveAs.
/// </summary>
/// <param name="request">The request to turn to a string.</param>
/// <returns>A string to inspect the HttpRequest.</returns>
public static string RequestAsString(this HttpRequest request)
{
    StringBuilder returnValue = new StringBuilder()
        .Append(request.HttpMethod + " " + request.RawUrl);
    if (returnValue.ToString().EndsWith("?"))
    {
        returnValue.Length = returnValue.Length - 1;
    }
    returnValue.Append(" " + request.ServerVariables["SERVER_PROTOCOL"])
        .Append(Environment.NewLine)
        .Append(TurnNamedValuesToLists(request.Headers, ": ", Environment.NewLine))
        .Append(Environment.NewLine)
        .Append(request.ContentEncoding.GetString(request.BinaryRead(request.ContentLength)));

    return returnValue.ToString();
}

/// <summary>
/// Splits out a NameValueCollection.
/// </summary>
/// <param name="target">The collection to be split.</param>
/// <param name="seperator">Seperates the key from the value in the output.</param>
/// <param name="terminator">Put at the end of the key and the value.</param>
/// <returns>A string of all the key value pairs.</returns>
private static string TurnNamedValuesToLists(NameValueCollection target, string seperator, string terminator)
{
    StringBuilder returnValue = new StringBuilder();
    string[] keys = target.AllKeys;

    foreach (var key in keys)
    {
        returnValue.Append(key + seperator + (string)target[key] + terminator);
    }
    return returnValue.ToString();
}

Monday, October 5, 2009

Useful Visual Studio 2008 Settings

Here are some Visual Studio 2008 settings I have turned on to speed up my development time.
  1. Default Browser - I use Chrome as my default browser, but our application is targeting IE.  To have Visual Studio launch IE instead of chrome I found out one can right click a .aspx page select 'Browse ' With' and then from the next dialog set Internet Explorer as default.
  2. Autorecover - Sometimes over bad VPN connections AutoRecover will freeze the VS User Interface.  I used to change the autorecover path but now I go to settings --> Environment --> Autorecover and uncheck the save checkbox.
  3. Source Control - Settings --> Source Control --> Environment  I check get everything when a solution or project is opened.  VS still prompts for get latest on open so you are in control, but it is a nice reminder to sync up.  For saving I have 'Prompt for check out' selected to avoid silent checkouts.  Some files like .vsmdi and some dependant files need to be checked out prior to editing for this to work.  For Editing I have 'Do nothing' so I can edit and continue without checking out files.
  4. Outlining - Settings -->Text Editor --> C# --> Advanced I uncheck 'Enter outlining mode when files open.'  This prevents regions from hiding code from me when I open the file.  I also uncheck 'Surround generated code with #region' because I don't like regions.  Ever.
  5. Debugging - Settings --> Debugging --> General I uncheck 'Require source files to match the original version'  This is another one for edit and continue debugging.  Sometimes I'll notice something I want to change, like changing a string literal to reference a constant from a helper class, but don't want to stop my debugging.
  6. Symbols - Settings --> Debugging --> Symbols I cache my symbols to c:\debugsymbols so they can be shared between VS and Windbg.

Monday, September 14, 2009

Catching inlining in the act from managed code.

This blog post shows ilining in action all without leaving the comfort of managed code.

When you compile C# code it is compiled to Intermediate Language (IL).  When the Common Language Runtime executes IL it uses a Just In Time (JIT) compiler to compile the IL to machine instructions.
Here is some C# sample code:


using System;
using System.Diagnostics;
static class Program
{
    static void Main() { Person me = new Person("Dave"); Console.ReadKey(); }
}
class Person
{
    public Person(string name) { Name = name; }
    private void SetName(string name)
    {
        Console.WriteLine(new StackTrace().GetFrame(1).GetMethod().Name);
    }
    public string Name
    {
        set { SetName(value); }
    }
}

Copy that code into notepad and save it as Program.cs.
From a Visual Studio command prompt type these lines.
csc /out:d.exe Program.cs /optimize- /debug+ 
csc /out:u.exe Program.cs /optimize- /debug- 
csc /out:o.exe Program.cs /optimize+ /debug- 

Then execute the programs:
d outputs set_None
u outputs .ctor
o outputs Main

Kinda crazy huh?

PS. if you run this program from Visual Studio it will output set_None because you run with a debugger attached and the JIT Compiler won't inline.  If you compile in Release mode and run without the debugger (ctrl+F5) you can see Main if you are fast enough ;)

Monday, September 7, 2009

Lets all fight spam

I sent myself an email last night to forward out to the team.  A while after getting to work I wondered where was my email?  It had been put in my spam folder!  Email is more critical to me than postal mail and I would love for it to be reliable.  I've had problems this year where important emails where put into my spam folder.

Sender Policy Framework (SPF) exists to help reduce spam and while I'm not sure if this will reduce the odds of emails from my domain being marked as spam I figured it won't hurt.

This blog post will describe how I setup SPF for my website which uses Google Apps for email and web content and GoDaddy's Total DNS product for Domain Name System configuration.  If you use the same infrastructure it will be easy copy and pasting.  If not you might have to do some additional reading, or switch your infrastructure :)

I ran across this page with SPF Tools including a wizard to generate an SPF record and some record testers.

Using their wizard I got this string: "v=spf1 mx ~all" spf1 means spf version 1, mx means accept mail from mail servers, and ~all means softfail all mail. So basically this would softfail all mail not sent from google's mail servers. This made sense. Being a cautious type I decided to see what Google Apps Admin Help has to say on the matter. Hmmmmmmmm they recommend "v=spf1 include:aspmx.googlemail.com ~all".  This seems to do a DNS lookup at aspmx.googlemail.com which redirects over to spf.google.com which has a list of acceptable IP addresses.  I decided to go with Google's recommended method.  I also decided to change the ~ which is a softfail to a - which is a fail.  Using the tilde seems to be a precautionary measure which reduces the usefulness of the spec.  It is interesting to note that the SPF Tool wizard generates SPF records with a ~ and so does GoDaddy's generator.......  But oh well I'll live on the wild side :)


To implement my record I login to GoDaddy's Total DNS Control and click add new text record.  For the TXT name I put "davidsilvasmith.com." and for the txt value I put "v=spf1 include:aspmx.googlemail.com -all" and a Time To Live (TTL) of 1 hour.

Then I validate my results by emailing check-auth@verifier.port25.com.

Here are my results before implementing the changes:
  SPF check:          neutral
  DomainKeys check:   neutral
  DKIM check:         neutral
  Sender-ID check:    neutral
  SpamAssassin check: ham

Here are the results after:
  SPF check:          pass
  DomainKeys check:   neutral
  DKIM check:         neutral
  Sender-ID check:    pass
  SpamAssassin check: ham

Then I emailed my work account and got my email sent to the spam folder still.  Rats.

Monday, August 31, 2009

Search all columns of all tables for a pattern

Today at work someone wanted to know how to search all columns of all tables for a pattern.  The code below uses a recursive select to generate dynamic SQL which will search all varchar columns for the specified pattern.  Not all columns are able to be cast to varchar.
declare @searchString varchar(max)
set @searchString = '%dealer sites%' --the pattern to search for


declare @queryToRun varchar(max) --internal variable.
--this variable needs to be non null otherwise it will make the recursive select return null.
set @queryToRun = ''

--create a statement to search all columns of all tables for the text.
SELECT @queryToRun = @queryToRun + 'select '''+t.table_name +'.'+c.column_name+''' from ' + QUOTENAME(t.TABLE_SCHEMA) + '.' + QUOTENAME(t.TABLE_NAME) + ' where ' + QUOTENAME(c.column_name) + ' like '''+ @searchString + ''' union '
                  FROM INFORMATION_SCHEMA.TABLES t JOIN
                  INFORMATION_SCHEMA.COLUMNS c on t.table_name = c.table_name
                  WHERE             TABLE_TYPE = 'BASE TABLE' and data_type='varchar'

--trim off the last union statement.
select @queryToRun = left(@queryToRun, datalength(@queryToRun) - datalength('union '))

--run the query.
exec (@queryToRun)

Monday, August 24, 2009

T4 - Text Template Transformation Toolkit

At the Lansing Day of .NET I saw a presentation about Microsoft's T4 (Text Templating Transformation Toolkit).  At the time I couldn't think of where I could use this tool to be more DRY because dry folks go over big at parties :)  Some of the examples given were in generating documentation and in generating a data layer.  There are tools specifically for those tasks so that didn't sit well with me.


Our current product is a web product and we sometimes use the session to pass data between our pages.  It was a lot of redundant typing and I figured T4 could help.


The template takes a list of strings and makes a static class exposing the strings as constants.  These strings are used as keys to pull data out of the session.  Some of the keys need post processing when the data is pulled out.  In this implementation I'm doing that via reflection but after writing it I figure a dictionary could do the trick and be faster.


Put this text in a .tt file and save it.  It will generate a .cs file shown below:




<#@ template language="C#v3.5" debug="true" #>
<#@ output extension="cs" #>

namespace tt
{
    /// <summary>
    /// Used to store keys to lookup session data.  This should ensure we don't have collisions and can easily refactor.
    /// </summary>
    /// <remarks>
    /// This class is autogenerated by SessionKeys.tt.  Do not modify this file!
    /// </remarks>
    public static class SessionKeys
    {
<# foreach ( string constant in GetConstants())
{
string[] inputs = constant.Split(',');

for (int i = 1; i < inputs.Length; i++)
                {
string attribute = inputs[i].Trim();
if(attribute.Length > 0)
{
WriteLine("[" + inputs[i].Trim() + "]");
}
                }

WriteLine("public const string " + inputs[0].ToUpper() + " = \"" + inputs[0] + "\";");
}
#>

    }
}

<#+
///<summary>
/// provides constants for SessionKeys.cs.  Some constants need attributes to be processed.
/// This is done via Attributes in the namespace UrbanScience.Si2.Module.LeadReporting.Utilities.
/// The format of the strings in this list should be IDENTIFIER, Attribute, Attribute, Attribute....
///</summary>
private string[] GetConstants()
{

return new string[]
{
"Name,InputAllowsWildCard",
"StartDate",
"EndDate",
"StateList, CleanMultipleInput",
};
}
#>



Here is the generated class:

namespace tt
{
    /// <summary>
    /// Used to store keys to lookup session data.  This should ensure we don't have collisions and can easily refactor.
    /// </summary>
    /// <remarks>
    /// This class is autogenerated by SessionKeys.tt.  Do not modify this file!
    /// </remarks>
    public static class SessionKeys
    {
            [InputAllowsWildCard]
public const string NAME = "Name";
public const string STARTDATE = "StartDate";
public const string ENDDATE = "EndDate";
[CleanMultipleInput]
public const string STATELIST = "StateList";
           
    }
}

Monday, August 17, 2009

Debugging win32 API errors

This blog post documents the methodology I used to diagnose a win32 API exception with a generic error message.  If you are in the same situation hopefully you'll find this helpful..

In April Tess Ferrandez blogged about Visualizing virtual memory usage and GC Heap.  I thought it would be cool if her code were modified to call Windbg programmatically and use a WPF frontend.  I found out Windbg uses dbgeng.dll!  Awesome!  I'll just write a managed wrapper around that.  While researching writing the wrapper I found CodePlex already had such a project called: mdbglib.  Great!  So I grabbed the code compiled and......Unhandled Exception!  No....... "First-chance exception at 0x7c9666c6 (ntdll.dll) in ASDumpAnalyzer.exe: 0xC0000139: Entry Point Not Found.".

My call stack looked like this: (bolded entries will be referenced later)

  ntdll.dll!_RtlRaiseStatus@4()  + 0x26 bytes
  ntdll.dll!_LdrpSnapThunk@32()  + 0x2a2b2 bytes
  ntdll.dll!_LdrpSnapIAT@16()  + 0xd9 bytes
  ntdll.dll!_LdrpHandleOneOldFormatImportDescriptor@16()  + 0x7a bytes
  ntdll.dll!_LdrpHandleOldFormatImportDescriptors@16()  + 0x2e bytes
  ntdll.dll!_LdrpWalkImportDescriptor@8()  + 0x11d bytes
  ntdll.dll!_LdrpLoadDll@24()  - 0x26c bytes
  ntdll.dll!_LdrLoadDll@16()  + 0x110 bytes
  kernel32.dll!_LoadLibraryExW@12()  + 0xc8 bytes
  mscorjit.dll!Compiler::impImportBlockCode()  + 0x5661 bytes
  mscorjit.dll!Compiler::impImportBlock()  + 0x59 bytes
  mscorjit.dll!Compiler::impImport()  + 0xb2 bytes
  mscorjit.dll!Compiler::fgImport()  + 0x20 bytes
  mscorjit.dll!Compiler::compCompile()  + 0xc bytes
  mscorjit.dll!Compiler::compCompile()  + 0x270 bytes
  mscorjit.dll!jitNativeCode()  + 0xa0 bytes
  mscorjit.dll!CILJit::compileMethod()  + 0x25 bytes
> ASDumpAnalyzer.exe!ASDumpAnalyzer.MainForm.MainForm_Load(object sender = {ASDumpAnalyzer.MainForm}, System.EventArgs e = {System.EventArgs}) Line 66 + 0x8 bytes C#

Based on the call stack I figured the code was throwing during JIT.  Looking at the method being jitted I was able to determine the Debuggee class was causing the Exception.  I figured the project was written on Windows Vista and was using a function that didn't exist in XP.  Looking around the CLI / C++ project nothing jumped out at me.  I decided to buy a C++ / CLI book, read about the win32 API calling convention: stdcall, and read about getting the parameters to LoadLibraryExW.  I also found out Visual Studio has  memory windows and a registers window.  I also found out that the ntdll.dll function names with a p in them are private, so I wouldn't be able to get their signature from MSDN like I could for Kernel32 LoadLibraryExW.
Armed with this knowledge I dug back in.
I put a breakpoint (bp) at {,,ntdll.dll}_LdrpSnapThunk@32.  This ended up getting hit all the time, so I put a hit count bp on {,,kernel32}_LoadLibraryExW@12 with a count of 59 because that is right before the app explodes.  I disabled my SnapThunk bp and enabled it after the LoadLibrary bp was hit.  Looking at the 32 bits on the stack using offsets from the esp register (esp, esp+4, esp+8........). and resolving the four 8 bit values addresses to memory locations I didn't see a function name like I was hoping.
So I started debugging assembly line by line.  After a few minutes I see 
7C917EE5  push        edi  
7C917EE6  push        eax  
7C917EE7  push        dword ptr [ebp+8] 
7C917EEA  push        dword ptr [esi+18h] 
7C917EED  push        ebx  
7C917EEE  call        _LdrpNameToOrdinal@20 (7C917EFDh)
I get very excited because there is a function name in esp when 7C917EEE executes!  This loop gets called a lot so I decide to put a bp on 7C917EEE and print the function name when it is called instead of breaking by putting this: {*(char**)esp}.  I hit F5 and watch function names fill up my output.  Right about the time I get my exception message I see DebugConnectWide in output.  Searching through the mdbglib project I see Debuggee.cpp line 56 has this call: Tools::CheckHR(DebugConnectWide(pwRemoteOptions, __uuidof(DbgClient), (void **)&debugClient ))  Comment that line rebuild, and no exception!

Monday, August 10, 2009

Recursive Selects

Last week at work I created some SQL Server functions to take a list of rows and turn them into a comma delimited string.  It feels like data formatting which should be done on the client side, but that was the method we went with.

It does show recursive selects though, which can also be used for dynamic cross tab queries.  At first this method didn't make any sense to me, but thinking about a select statement as a loop makes it make sense.


declare @siblings table (id int, firstName varchar(max))
insert into @siblings
      select 0, 'Jordan' UNION
      select 1, 'Mia'
declare @siblingString varchar(max)
set @siblingString = ''
select @siblingString = @siblingString + firstName + ', '
      from @siblings
if (datalength(@siblingString) > 2)
      select @siblingString = left(@siblingString, datalength(@siblingString) -2)

select @siblingString

Outputs 'Jordan, Mia'

Monday, August 3, 2009

Lansing Day of .NET 2009 Review

I had a great time at the Lansing Day of .NET Saturday.  I really enjoyed seeing everyone there, and we got fancy cool LDODN 2k9 beer tumblers.  There was a great after party at Jeff McWherter's that Eric Vogel helped me get to.  My favorite sessions were T4: Code Generation with Visual Studio 2008 by Steve Andrews and Improving our Craft: A Discussion on Software Estimation by Mike Eaton.

The T4 Code Generation facilities allow you to leverage the power of C# or VB.NET to write code generation scripts.  It also seemed straightforward to extend the code generators and also code generation hosts. The code generation kicks off by default when you save the file in Visual Studio, but one could write a custom host to execute it from a command line environment or during the build process..  The session showed some interesting scenarios for generating help documentation and a data layer using T4.  I don't plan on using T4 anytime soon, but I'm glad to know the tool exists and have had an overview of it's capabilities.  I've seen Steve Andrews present before and I like his style.  He mixes in keyboard shortcut tips and shows Visual Studio configuration options during the talks I've seen.


Mike Eaton's presentation on Software Estimation initiated some interesting discussion topics and thoughts to ponder.  He is an engaging speaker and had some funny cartoons.  Mike was especially adept at involving the audience in the presentation.

Monday, July 27, 2009

Foundations of C++ /CLI, Book Review

I just finished reading Foundations of C++/CLI: The Visual C++ Language for .NET 3.5 .  I read this book hoping to get a grasp of how to wrap native libraries with C++/CLI.  Unfortunately I didn't read the Amazon Editorial review and only chapter 13 covered this topic.  The book assumes the reader knows C++ and wants to learn .NET.  This made the majority of the book review for me.

This book would probably be good for someone with a C++ background wanting to learn .NET, although if one isn't going to do interop the book recommends using C# instead.

Monday, July 20, 2009

Viewing Google Login via Fiddler

I started playing around with the Google Data APIs.  I'm amazed at how easy the .NET library is to consume.  In a few minutes I had their samples connecting to Google Photo,and in about an hour I had connected to my Google Contacts and viewed all my contacts.

I wanted to know how this was working.  Firing up WireShark there was too much going on, so I tried Fiddler which worked well. It also has a cool Request Builder feature that I'm going to dive into.

Launch Fiddler and click the Request Builder tab.  From the drop down list on the left select Post.  Type in https://www.google.com/accounts/ClientLogin in the textbox, and HTTP/1.0 in the next drop down.  In the Request Headers put Content-type: application/x-www-form-urlencoded.  In the request body put accountType=GOOGLE&Email=YOUREMAIL@gmail.com&Passwd=YOURPASSWORD&service=cl

Click the Execute button.  You should get back a result 200.  Click the TextView in the Response area of Fiddler.  This lets you see the all important Auth token :)

Go here to learn about Google API authentication: http://code.google.com/apis/gdata/auth.html#ClientLogin

Monday, July 13, 2009

C# Programming Language Book Review

I just finished reading The C# Programming Language (3rd Edition).

For those interested in learning C# try Programming C# 3.0 instead.  The specification is about 650 pages and some parts describe the grammar of familiar subjects at great length without explaining how to use C#.  The best part about the book: the cool factor of having it sit on my desk.  Chicks dig nerds ;)

This was a good read now instead of earlier in my career so I could appreciate the annotations and lexicon.  The book pointed out things I didn't know C# could do, and don't see why I'd want to do, but would make good trivia (does this compile?).  Some things were interesting like the fact that the this keyword is a reference in structs.  Also I had never looked into the unsafe features of the C# language, making that chapter good reading.

Monday, July 6, 2009

Select * from table where column in %

This method allows you to filter a column based on a list supplied by the user or when the user doesn't supply anything to return all the results without using dynamic SQL.  You can use ANDs in the where clause to include additional filters.  In our application we parse a comma delimited string in SQL to get our list of user input.  SQL 2005 introduced table variables and you could probably use those instead.
--pretend this table contains all the states.
      declare @states table (identifier varchar(2))
      insert into @states
            select 'MI' UNION
            select 'CO' UNION
            select 'NY'

--this simulate states selected in the application
      declare @selectedStates table (identifier varchar(2))

--insert a state to simulate the user selecting a state.
      insert into @selectedStates --comment this line to see @allStates flip
            select 'MI'           --comment this line to see @allStates flip
declare @allStates bit

--if the user hasn't selected a state get all the results.
if ((select count(*) from @selectedStates) = 0)
      select @allStates = 1

--give back the results:
select identifier from @states
      where (identifier in (select identifier from @selectedStates) OR @allStates = 1) AND 1=1 --additional filters could go here instead of 1=1


Monday, June 15, 2009

Untrusted Project Location When Opening Solution

I downloaded source from a CodePlex project last night.  After unzipping the source to my hard drive and opening the solution I was bombarded with errors of The project location is not trusted: c:\... Running the application may result in security exceptions when it attempts to perform actions which require full trust."  I had to click OK for the solution and once for each project file.

Searching the web I ran across Colin Mackay's blog with a post explaining since Windows XP Service Pack 2 when downloading zip files an Alternate Data Streams (ADS) with a zone identifier are added in NTFS.  During unzipping the zone identifier is propagated to the solution and project files.  As Colin explains there are programs which can remove the ADS zone identifier.  I found out the best way to resolve this without downloading a tool is to right click on the properties of the archive, and at the bottom of the properties dialog in the security section click the Unblock button.  Then when the archive is unzipped the solution and project files won't have a zone identifier included.  One can also click the unblock button on each project and solution file after unzipping although this isn't as efficient.