in

Platinum Bay

Peace, Love, and...

This Blog

Syndication


.NETicated

  • Enumeration-Based Dropdowns in .NET MVC

    I have a few enumerations in DevEvents, and I found myself frequently writing code similar to the following:

    Dictionary<string, int> etlist = new Dictionary<string, int>();
    etlist.Add(
    "Code Camp", (int)EventType.CodeCamp);
    etlist.Add(
    "Conference", (int)EventType.Conference);
    etlist.Add(
    "Day of .NET", (int)EventType.DayOfDotNet);
    etlist.Add(
    "Hands-On Labs", (int)EventType.HandsOnLabs);
    etlist.Add(
    "User Group Meeting", (int)EventType.UserGroup);
    etlist.Add(
    "Workshop", (int)EventType.Workshop);
    etlist.Add(
    "Pub Night", (int)EventType.PubNight);
    etlist.Add(
    "Online", (int)DevEvents.Common.EventType.Online);
    etlist.Add(
    "BarCamp", (int)DevEvents.Common.EventType.BarCamp);
    etlist.Add(
    "Geek Dinner", (int)DevEvents.Common.EventType.GeekDinner);

    if (evnt != null)
    ViewData[
    "EventTypes"] = new SelectList(etlist, "Value", "Key", evnt.EventTypeID);
    else
    ViewData["EventTypes"] = new SelectList(etlist, "Value", "Key");

    Update - 1/12/2009: For an easier way to do the above, check out this post by Rune Jacobson.

    The problem with the approach however, is that every time I need to add a new enumeration value, I would have to modify each of these code sections, and that got very old very fast. I needed a way to generate dropdowns from the enumerations themselves. I ended up with a custom HtmlHelper to generate the dropdowns and eliminate the repetitive code.

    First, I created the base HtmlHelper:

    public static string EnumDropDown(this HtmlHelper helper, Type enumType, string id)

    The ID is used to set the HTML ID attribute, since some dropdowns were being used by jQuery. The parameter enumType is used to pass in the type of enumeration that the dropdown should be generated from. Since I need it to be an enumeration, I check as such, returning an empty string if the check fails.

    if (enumType == null || !enumType.IsEnum)
    return "";

    Next, I attempt to retrieve a value to set as default; first from ViewData, and then from the model if it has been set. I catch an empty exception at the end, which is typically a bad practice, because it is not important if the value cannot be found – the View may be an empty form.

    object defaultValue = null;

    if (helper.ViewData != null)
    defaultValue = helper.ViewData.Eval(id);

    try
    {
    if (defaultValue == null)
    defaultValue = helper.ViewData.Model.GetType().GetProperty(id).GetValue(helper.ViewData.Model,
    null);
    }
    catch (Exception) { }

    Finally, I set about to return HTML back to the View for the desired dropdown. I create a StringBuilder, append the select start tag, and iterate through all the fields on the enumeration:

    StringBuilder sb = new StringBuilder();

    sb.AppendFormat(
    "<select id=\"{0}\">", id);
    foreach (var item in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
    {

    Next, for each enumeration field, I check whether the value equals the expected default value determined above:

    string def = "";
    if (defaultValue != null && defaultValue.ToString() == item.GetRawConstantValue().ToString())
    def =
    "selected=\"selected\"";

    Finally, I append each option to the StringBuilder:

        sb.AppendFormat("<option value=\"{0}\" {1}>{2}</option>",
    item.GetRawConstantValue().ToString(), def, item.Name);
    }
    sb.Append(
    "</select>");

    return sb.ToString();

    I was then able to call my HtmlHelper from the View as follows:

    <%= Html.EnumDropDown(typeof(DevEvents.Common.EventType), "EventTypeID") %>

    I ran the code, and had a dropdown rendered with each item in the enumeration. There was one small problem however; each option display value was the enumeration field name, exactly. Instead of seeing a pretty name like “Hands-On Labs”, I saw “HandsOnLabs”. Of course this is to be expected based on the generation method used, but how do I enable a more elegant UX?

    The solution I used was to create a custom Attribute for my custom enumerations, storing a single property called DisplayName:

    [AttributeUsage(AttributeTargets.Field)]
    public class EnumDisplayNameAttribute : Attribute
    {
    private string _displayName;

    public EnumDisplayNameAttribute(string displayName)
    {
    _displayName = displayName;
    }

    public string DisplayName
    {
    get
    {
    return _displayName;
    }
    }
    }

    I then applied that attribute to my custom enumerations as follows:

    public enum EventType
    {
    [
    EnumDisplayName("User Group")]
    UserGroup = 0,
    [
    EnumDisplayName("Code Camp")]
    CodeCamp = 1,
    [
    EnumDisplayName("Day of .NET")]
    DayOfDotNet = 2,
    [
    EnumDisplayName("Conference")]
    Conference = 3,
    [
    EnumDisplayName("Hands-On Lab")]
    HandsOnLabs = 4,
    [
    EnumDisplayName("Workshop")]
    Workshop = 5,
    [
    EnumDisplayName("Pub Night")]
    PubNight = 6,
    [
    EnumDisplayName("Online")]
    Online = 7,
    [
    EnumDisplayName("BarCamp")]
    BarCamp = 8,
    [
    EnumDisplayName("Geek Dinner")]
    GeekDinner = 9
    }

    Finally, in my HtmlHelper, I checked for the presence of this attribute on each enumeration field, and set the display name accordingly:

    EnumDisplayNameAttribute[] attributes =
    (
    EnumDisplayNameAttribute[])item.GetCustomAttributes(typeof(EnumDisplayNameAttribute), false);

    if (attributes.Length > 0)
    sb.AppendFormat(
    "<option value=\"{0}\" {1}>{2}</option>",
    item.GetRawConstantValue().ToString(), def, attributes[0].DisplayName);
    else
    sb.AppendFormat("<option value=\"{0}\" {1}>{2}</option>",
    item.GetRawConstantValue().ToString(), def, item.Name);

    Here is the final completed code for the HtmlHelper:

    public static string EnumDropDown(this HtmlHelper helper, Type enumType, string id)
    {
    if (enumType == null || !enumType.IsEnum)
    return "";

    object defaultValue = null;

    if (helper.ViewData != null)
    defaultValue = helper.ViewData.Eval(id);

    try
    {
    if (defaultValue == null)
    defaultValue = helper.ViewData.Model.GetType().GetProperty(id).GetValue(helper.ViewData.Model,
    null);
    }
    catch (Exception) { }

    StringBuilder sb = new StringBuilder();

    sb.AppendFormat(
    "<select id=\"{0}\">", id);
    foreach (var item in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
    {
    string def = "";
    if (defaultValue != null && defaultValue.ToString() == item.GetRawConstantValue().ToString())
    def =
    "selected=\"selected\"";

    EnumDisplayNameAttribute[] attributes =
    (
    EnumDisplayNameAttribute[])item.GetCustomAttributes(typeof(EnumDisplayNameAttribute), false);

    if (attributes.Length > 0)
    sb.AppendFormat(
    "<option value=\"{0}\" {1}>{2}</option>",
    item.GetRawConstantValue().ToString(), def, attributes[0].DisplayName);
    else
    sb.AppendFormat("<option value=\"{0}\" {1}>{2}</option>",
    item.GetRawConstantValue().ToString(), def, item.Name);
    }
    sb.Append(
    "</select>");

    return sb.ToString();
    }

    What do you think? Would you have done it differently?

    Updated:

    In thinking about this, the next logical step would be to store the enumeration values in the database so they could be updated at runtime. While I’m not going to set about to do this right away, I would imagine pulling them when the application starts and caching them using a SQL cache dependency. Not too many things should be cached in application memory, but this list should stay relatively small. Thoughts?

    Posted Jan 10 2009, 02:47 AM by Steve with 9 comment(s)
    Filed under:
  • 52’er

    I was notified last week that I have been selected as an INETA NORAM speaker! I am quite honored, and excited to be a part of such a great team.

    Being New Years and all, I thought I would set a goal this year. Last year, I gave 48 presentations at 32 engagements. While most seem to think this is a lot, I would like to expand on that number this year. My goal? 52 engagements, 20 more than last year. So far I have scheduled 13 which brings me to 25% of my goal.

    If you need a speaker, if no one else can help, and if you can find me, maybe you can bring in: ME!

    Previous and upcoming engagement list: http://www.platinumbay.com/about.aspx

  • Quotes

    I like quotes, and those of you who follow me on Twitter see me post them from time to time. I've decided, in part due to an open request from Phil Haack for an inspirational quote, to catalog a few of my favorite quotes here:

    "Nothing will ever be attempted if all possible objections must first be overcome."
    - Samuel Johnson

    "Winning isn't everything, but wanting to win is."
    - Vince Lombardi

    "If everything seems under control, you're just not going fast enough."
    - Mario Andretti

    "Even if you are on the right track, you'll get run over if you just sit there!"
    - Will Rogers

    "What lies behind us, and what lies before us are tiny matters compared to what lies within us."
    - Ralph Waldo Emerson

    "If you do not hope, you will not find what is beyond your hopes."
    - St. Clement of Alexandra

    "Go confidently in the direction of your dreams. Live the life you've imagined."
    - Henry David Thoreau

    "Happiness is not achieved by the conscious pursuit of happiness; it is generally the by-product of other activities."
    - Aldous Huxley

    "Sometimes I think sensitive people are more capable of reflection because they have to shut the world out to function."
    - @mfeathers

    "What I think is interesting is that the people who are awed by greatness often become great themselves."
    - @mfeathers

    "We don't see things as they are -- We see things as we are."
    - Anais Nin

    "When we are unable to find tranquility within ourselves, it is useless to seek it elsewhere."
    - Francois de La Rochefoucauld (1613 - 1680)

    "Success is a lousy teacher. It seduces smart people into thinking they can't lose."
    - Bill Gates

    "If you don't risk anything you risk even more."
    - Erica Jong

    "The world is moving so fast these days that the man who says it can't be done is generally interrupted by someone doing it."
    - H.E. Fosdick

    "You cannot live a perfect day without doing something for someone who will never be able to repay you."
    - John Wooden

    "If you don't set goals for yourself, you are doomed to work to achieve the goals of someone else."
    - Brian Tracy

    "Never lose the childlike wonder. It's just too important. It's what drives us."
    - Randy Pausch (1960-2008)

    "If you lead your life the right way, the karma will take care of itself. The dreams will come to you."
    - Randy Pausch (1960-2008)

    "There is only one success - to be able to spend your life in your own way."
    - Christopher Morley

    "It's fine to celebrate success but it is more important to heed the lessons of failure."
    - Bill Gates

    "We can do no great things, just small things with great love. It is not how much you do, but how much love you put into doing it."
    - Mother Teresa

    "I always learn more from failure than from success."
    - Nick Malik

    "Never doubt that a small group of thoughtful committed citizens can change the world. Indeed it's the only thing that ever has."
    - Margaret Mead

    "Change your thoughts and you change your world."
    - Norman Vincent Peale

    "Reality is a lot like an expensive wine, it won't appeal to children."
    - Unknown

    "Imagination is more important that knowledge"
    - Albert Einstein

    "I do not believe my energy and enthusiasm to be simply a factor of youth, but I am still young."
    - Unknown

    "No one is useless in this world who lightens the burden of it to anyone else."
    - Charles Dickens

    "It was in my heart to help a little because I was helped much."
    - Kahlil Gibran

    "We can't solve problems by using the same kind of thinking we used when we created them."
    - Albert Einstein

    "Never trade reputation for money."
    - Warren Buffet

    “Learn from yesterday, live for today, hope for tomorrow.”
    - Albert Einstein

  • Windows Live Writer

    So I finally caved into pressure and am writing this post from Windows Live Writer. After some hassle with the install on Win2K8 (thanks to @ninob for pointing me here), I set about to write my first post. I do what I normally do; open the program and hit Maximize. Clearly Windows Live Writer wasn't designed to be maximized (see screenshot), at least not at 1920x1200. And yes, I am going to be highly critical of it, at least compared to my experience using Word 2007 for writing posts.

    image

    No test would be complete without the obligatory code snippet. First results were that all the formatting were lost. Weird. Word does this out of the box. Fortunately I found the Paste from Visual Studio add-in, which gives me this:

    public static string LabelFor(this HtmlHelper helper, string target, string text)
    {
        return String.Format("<label for='{0}'>{1}</label>", target, text); 
    }

    Now to see how everything looks online...

  • DevEvents – The Generic Repository

    Let it be said that I strongly dislike code duplication. For that matter, I also strongly dislike repetitive typing. Yet as I started to build out DevEvents, I found myself frequently duplicating similar code across object repositories, and typing the same base statements. In these cases, I typically create a base class to take care of the duplicated functionality, and inherit the dependant classes from the base. With the object repositories however, each duplicated section of code was slightly dependant on the object type. Not to be deterred, I ended up creating a custom generics-based repository implementation that virtually eliminated the code duplication. But let's start from the beginning.

    DevEvents is built on top of a LINQ to SQL model which lives in its own assembly. That assembly is then referenced from a Components layer in which the repositories live. A typical repository implementation might look like this:

    public class OldLogRepository : IOldLogRepository
    {
        private DEDataContext db;
    
        public OldLogRepository()
        {
            db = new DEDataContext();
        }
    
        internal OldLogRepository(DEDataContext context)
        {
            db = context;
        }
    
        public int GetLogEntryCount()
        {
            return db.Logs.Count();
        }
    
        public void ClearLogEntriesBefore(DateTime dateTime)
        {
            var query = (from le in db.Logs
                        where le.EventDate <= dateTime
                        select le);
    
            db.Logs.DeleteAllOnSubmit(query);
        }
    }

    I feel this is a pretty standard repository implementation, and it helps promote loose coupling at several levels. First, the repository itself is interface-based which means it can be swapped out if necessary. Second, the internal constructor allows for dependency injection of a non-default data context.

    My frustration arose when I had to duplicate a lot of the standard functionality across dozens of repositories, such as Count, Create, Delete, Update, Select, etc. The first idea was to create a base class to house that functionality, but as shown in the ClearLogEntriesBefore method above, there was some object specific logic in the methods. I realized however, that the method could be broken up into different parts: the query, the context, and the table. A quick check of the model code confirmed this, and I set about to create a generic repository implementation to reduce or eliminate the amount of redundant code. I started with an abstract generic class:

    public abstract class BaseRepository<TModel> : IBaseRepository<TModel>

    Next, I created the core fields and constructor:

    protected DEDataContext db;
    protected Table<TModel> _table;
    
    public BaseRepository(Table<TModel> table)
    {
        db = new DEDataContext();
        _table = table;
    }

    Note that the default constructor tables a parameter of type System.Data.Linq.Table<TEntity>. This is the base type of all entity objects, and allows the base class to handle generic table operations on the correct table.

    So far I have taken care of the context and the table parts of my original duplicated methods. Next, I needed to solve the query problem. When I examined the method signature for operations such as Where and Select, I discovered that they all took a parameter of type Expression<Func<TModel, bool>>. What this means is that it is possible to pass parameters as a known base type. The base Contains method could now be written as:

    public bool Contains(Expression<Func<TModel, bool>> predicate)
    {
        return _table.Any(predicate);
    }

    From the top level repository class, I could then call Contains as follows:

    return Contains(le => le.SomeProperty == someValue);

    Using this methodology I was able to create a number of base methods, such as Count. The next step was basic CRUD operations; Create, Select, Update and Delete.

    Create is fairly straightforward:

    public void Create(TModel model, bool save)
    {
        _table.InsertOnSubmit(model);
        if (save)
            _table.Context.SubmitChanges();
    }
    
    public void Create(IEnumerable<TModel> models, bool save)
    {
        _table.InsertAllOnSubmit(models);
        if (save)
            _table.Context.SubmitChanges();
    }

    You might be wondering about the save parameter. In LINQ to SQL, entities added to the context are not saved to the database until SubmitChanges() is called. There are certain instances in the application where it is preferable to add a group of entities, and then commit them to the database.

    Delete and select got a little more complicated, since I am optionally passing a predicate of type Expression<Func<TModel, bool>>. The answer lies in the fact that IQueryable types are just that until they are invoked, a query. This means that I can refine the query, such as is done in the Delete method:

    protected void Delete(Expression<Func<TModel, bool>> predicate)
    {
        var list = (from e in _table
                    select e);
    
        if (predicate != null)
            list = list.Where(predicate);
    
        _table.DeleteAllOnSubmit(list);
        _table.Context.SubmitChanges();
    }

    You will notice that the Delete method is marked as Protected. This is to prevent it from being called outside of the repository, thereby enforcing interaction with the top-level repository methods to perform repository operations. For that matter, the db field in BaseRepository should be marked as private to ensure it is the single point of responsibility for the data context, but there is some cleanup to do, and I digress.

    Select is similar, except that it is implemented across multiple methods: LoadByQuery, LoadAllByQuery, LoadTopByQuery and LoadAll, all of which have overrides to optionally take a DataLoadOptions object. There are also a couple specialized methods to pull data in a serializable format. LoadAllByQuery is shown here implementing query refinements:

    protected IQueryable<TModel> LoadAllByQuery(Expression<Func<TModel, bool>> predicate)
    {
        return LoadAllByQuery(predicate, null);
    }
    
    protected IQueryable<TModel> LoadAllByQuery(Expression<Func<TModel, bool>> predicate, DataLoadOptions dlo)
    {
        if (dlo != null)
            db.LoadOptions = dlo;
    
        var list = (from e in _table
                    select e);
    
        if (predicate != null)
            list = list.Where(predicate);
    
        return list;
    }

    With the generics-based repository implementation in place, I can now rewrite the original LogRepository as follows:

    public sealed class LogRepository : BaseRepository<Log>, ILogRepository
    {
        public LogRepository()
            : base(new DEDataContext().Logs)
        {
        }
    
        public void ClearLogEntriesBefore(DateTime dateTime)
        {
            Delete(l => l.EventDate < dateTime);
        }
    }

    First, you will notice that there are significantly fewer lines of code (eleven versus twenty-three), and what is there is much less complex. Second, the Count method is no longer included. Since the Count method is in the base class and marked as Public, it can be called directly from the instantiated object.

    Here is another method from the Log repository, made simpler through its interaction with the base methods:

    public IQueryable<Log> GetLogEventsPaged(int pageID)
    {
        return LoadAllByQuery(null)
                .Skip(((pageID - 1) * DEContext.Current.PageSize))
                .Take(DEContext.Current.PageSize);
    }

    I have found a generics-based repository to be incredibly useful and efficient for my purpose. So my question to you, dear reader, is what do you think? How would you improve upon this implementation to make it better?

  • What’s In Your Quick Launch Bar?

    Heartland Developer Evangelist Jeff Blankenburg blogged today about his quick launch bar and the apps he has positioned there. He also encouraged others to do the same, I'd imagine out of pure curiosity.

    Here is mine, broken up because I run at 1900x1200:

    And here is it in list view with names, and sorted alphabetically:

    The crazy part? I use all this stuff! In case you're wondering, I run Windows Server 2008.

    So what does your quick launch bar look like?

  • Geek Food Drive

    It is estimated that over 33.5 million people in the United States and Canada will go hungry during the holidays this year. According to my data, which is a bit old, there are well over 180,000 registered user group members in the United States and Canada.

    My challenge is simple: for all user groups to hold a food drive at their meeting this month. There is plenty of need, especially this year!

    To this end, I've put up a simple website: http://www.GeekFoodDrive.com

    I'm coordinating with sponsors to provide swag based on donations relative to attendees, to be fair to all groups large and small.

    If you can help at all, let me know! Together, we can make a serious dent in hunger this holiday season!

    Posted Dec 07 2008, 08:13 PM by Steve with no comments
    Filed under:
  • Visualizing Geography in SQL Server 2008

    This is just too cool not to post. Many of you know about the DevEvents project I'm working on. If not, feel free to ping me. Part of the web application involves geo-context. Basically events are displayed to users (by default) based on their proximity to the event locations. To accomplish this I have several tables in the database with various geography related data. One of these tables takes advantage of the new SQL Server 2008 data type: Geography. This field essentially stores latitude and longitude, and SQL Server 2008 is then able to provide geography-related queries against it, like distance between two point.

    But the really cool part for me so far is when you select data that contains the Geography data type. Along with the standard Results and Messages pane, there is a new 'Spatial results' pane. Clicking on this tab reveals a map-like visualization.

    For the visualization below, I selected all points in Maine, Washington, Arizona, and Florida, and chose the Bonne projection. While I'd love to see all the points mapped, the visualization is limited to 5,000 results.

    Anyway, just thought this was pretty cool!

  • Presentation: Beyond Hello World – jQuery In-Depth

    On Tuesday, I had the privilege of presenting a session on jQuery at the www.NotAtPDC.com extension to the Microsoft PDC conference. I had a blast presenting my new-found love of jQuery to an audience of 47 folks, and given the level of interest I am publishing the project source.

    There were a number of great questions that came up; here are the responses for a couple of them:

    • The jQuery scripts are included by default in the ASP.NET MVC Beta. When you create a new project based on the Beta, they are in the scripts directory and are automatically referenced in the Master page.
    • jQuery itself can be used in any web application, and is not dependant on ASP.NET MVC.

    Resources listed in the presentation:

    Thanks also to John Resig, who is the lead developer on the jQuery project, who attended my presentation and was able to handle some of the more intricate questions regarding jQuery.

    NOTE: There is no security validation in the sample code, such as ensuring only authorized users can delete speakers. Do not use this code in production.

    Project Source Download (266.2k)

    Posted Nov 01 2008, 06:51 AM by Steve with no comments
    Filed under:
  • Visual Studio 2008 JavaScript Debugging

    Tonight, or this morning as the case may be, I was running into an issue with jQuery and the getJSON method. Basically, I was calling the method, with the proper parameters, but nothing was happening. The server method wasn't being called, and JavaScript debugging alerts weren't being fired either. After fiddling with the JavaScript for awhile, I decided to check out the JavaScript debugging feature which was added to Visual Studio 2008.

    Since I'd never used it before, I did the most logical thing I could think of, I searched. It turns out that JavaScript debugging is very easy to setup.

    First, go into your browser and enable script debugging. For Internet Explorer 7, go to Tools | Options | Advanced, and uncheck 'Disable script debugging (Internet Explorer)' and 'Disable script debugging (Other)'. For Firefox, JavaScript debugging appears to be enabled by default.

    Next, in Visual Studio, type Ctrl+Alt+P to open the 'Attach to Process...' dialog, and select either firefox.exe or iexplore.exe depending on which browser you are using.

    Finally, set breakpoints in your JavaScript code, and perform the action in the browser to get to the breakpoints. After that, you can use the standard Step Into and Step Over to navigate through your JavaScript. Visual Studio also provides debugger tooltips to explore your JavaScript objects, which is what ultimately led me to the response of the XmlHttpRequest object, and the realization that I had mis-named a server method parameter.

    With the popularity of jQuery, and other JavaScript libraries, I foresee this feature being widely used among web developers.

  • MSDN Freedom Roadshow

    Are you around Pittsburgh? No? You should get there anyway! This Friday the MSDN Freedom Roadshow is coming to town with a whole day of learning including .NET 3.5, ASP.NET MVC, and AJAX. This is your chance to come see for yourself what all the excitement is about from the local Microsoft evangelists and MVPs.

    Where:

    Microsoft – Pittsburgh, PA

    Address:

    20 Isabella Street

    Second Floor

    Alcoa Business Services Center

    Pittsburgh, PA 15212

    When:

    9:00 am to 4:00 pm

    Doors open at 8:30 am

     

    For more information and to register for this event, please visit:

    http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032388335&culture=en-US

    Posted Sep 16 2008, 03:31 PM by Steve with no comments
    Filed under:
  • Team Foundation Answer Files for SQL Server

    In a previous post, I mentioned how the installer answer files simplify the installation of SQL Server for Team Foundation. Several folks have since asked me to elaborate on these files. Answer files are INI files preconfigured with the settings required for the SQL Server installation. The beauty of the answer files is that they eliminate the possibility of human error during critical component installations.

    There are several answer files, depending on what type of installation you are performing:

    Single-Tier Installation

    • SQL2005ForATDT.ini – installation file to install all the necessary SQL Server components for Team Foundation Server

    Dual-Tier Installation

    • SQL2005ForAT.ini – installation file for the Application Tier to install Reporting Services
    • SQL2005ForDT.ini – installation file for the Data Tier to install the SQL Engine, Analysis Services, etc.

    Inside the answer file(s) you will be using, you may want to change a couple parameters, including the setup user and company, and the instance name for SQL Server.

    The command to run setup using the answer file is as follows. It is important to note that the PIDKEY should be left blank for MSDN versions of SQL Server:

    <Installation Root>\setup.exe /qb /settings <Answer File Path> PIDKEY=<ProductKey>

    For a more detailed list of hands-free installation of all the required pieces, check out this series of posts by Grant Holliday.

  • Unit Test Deployment Woes

    One of the common complaints I hear about Unit Testing in Visual Studio centers around test deployment. By default, when you run a unit test, the assemblies get copied to another location to be run. The assemblies and test results are copied to the following folders:

        <Users Documents>\Visual Studio 2008\Projects\<Project Name>\Test Results

    There are then subfolders based on the username, computer name, date and time, and further sub-folders representing the inputs and outputs of the test(s).

    For most scenarios, this behavior is transparent and doesn't cause problems. If your assembly has dependencies however, unit tests can fail as the dependencies are not deployed automatically. You can attach deployment dependencies by double-clicking the LocalTestRun.testrunconfig file in the solution root, going to the Deployment tab, and adding any necessary files or directories that should be deployed with the assembly.

    But this is a bunch of extra work, and can be viewed as 'friction' by some. Fortunately, there is another option. Many of the folks who complain about the deployment feature of Unit Tests point to NUnit as having an option to turn deployment on or off.

    As it turns out, Unit Tests have the same option, and it was right under my nose the whole time. In the same Deployment tab, there is a checkbox at the top titled "Enable Deployment". By un-checking this options, you can tell the test framework not to deploy assemblies to another location before running the unit tests.

    If you would like to make non-deployment the default option, you can modify the LocalTestRun.testrunconfig file in the "C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Testing Tools\TestProjectCommon\Templates\RunConfig\1033" directory by changing the Deployment node to:

        <Deployment enabled="false" />

    There is one caveat however; code coverage, remote execution, and device test projects all require deployment, so turning it off will disable this functionality.

  • Add Reference and the GAC

    One common misconception regarding Visual Studio that I run across quite frequently involves the Add Reference dialog and the GAC.

    Adding a reference to a Visual Studio project is as simple as right-clicking on the References folder, choosing "Add Reference…", and selecting the reference you wish to add from the dialog box that appears. However, and here is the confusion, assemblies that have been registered in the GAC will not appear in this list. This is because the Add Reference dialog does not enumerate the assemblies installed in the GAC. Rather, it is path-based and uses several registry keys to locate assemblies with which to populate the dialog.

    Registry Keys:

    The registry keys used by Visual Studio to populate the Add References dialog are:

    • [HKEY_LOCAL_MACHINE]\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders
    • [HKEY_CURRENT_USER]\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders

    To add your assembly folder, create a sub key with a descriptive name, and set the default REG_SZ value to the folder path. If Visual Studio is open, it must be restarted for the added folder path to be enumerated. Notice that both the Local Machine and Current User registry hives are listed above. You can add assembly folders to either key to scope them if needed.

    Example:

    [HKEY_CURRENT_USER\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders\MyAssemblies]@="C:\\MyAssemblies"

    As a closing note, remember that it is not recommended to install assemblies to the GAC unless they need to be shared among multiple applications running on the system.

  • Removing My Visual Studio Tips and Treats Download

    As many of you know, I have a Visual Studio Tips and Treats presentation with over 140 tips. It has been an enormously popular presentation and one that I enjoy presenting. As part of that presentation I offered my eBook of these tips for download from my website. As of today, that download will no longer be available.

    It has come to my attention that Sara Ford, the source of many of my tips, has written an MS Press book titled "Microsoft Visual Studio Tips: 251 Ways to Improve Your Productivity." The real story though is that Sara has announced she will donate 100% of the author book royalties to create a scholarship fund at the Mississippi Gulf Coast Community College for anyone living in her hometown of Waveland, Mississippi, which was destroyed during Hurricane Katrina. The Save Waveland Scholarship Fund will give preference to math and computer science majors.

    In light of this, I am removing my eBook from the Download section of my blog, and recommending everyone purchase a copy, or two, of her book instead. You can pre-order Sara's book on Amazon.com.

    Go Sara Go!

    Original Post:

    Microsoft Visual Studio Tips Book Helps Hurricane Katrina Survivors Rebuild Lives on Sara Ford's Blog

  • May the Source Stay With You Contest

    I am announcing a new code contest, offering a prize of a $50 online gift certificate to the first person who emails me a compiled managed assembly that I can't decompile, rebuild, and run.

    The prize is courtesy of Steve Andrews Consulting, offering Application Lifecycle Management and Microsoft Visual Studio Team System training, mentoring, consulting and assessments as well as Team Foundation installation, configuration, and custom development.

    Rules:

    • Must be a managed assembly with CLR headers – class library, console application, or Windows forms only
    • Must be compiled, do not send source code
    • Assembly must use native .NET libraries only – no dependencies
    • Maximum size of 100KB per assembly
    • You must own the copyright to the assembly
    • Only one assembly may be submitted per entry
    • Up to three entries per person allowed
    • Submission must include first and last name
    • The contest is free, and you do not need to buy anything to enter
    • The closing date for entries is August 31, 2008, or 50 submissions, whichever comes first.
    • A winning notification, if a winner is found, will be sent by September 30, 2008
    • By submitting an entry, you release me from all liability related to unauthorized decompilation

    I will work through each assembly as they arrive, and send source code plus a rebuilt version for verification.

    Note: Details regarding assembly submissions, including protection mechanism may be used in upcoming blog posts, conference presentations and released through other venues.

    To submit your entry:

    Email: steve[at]platinumbay[dot]com
    Subject: Will It Hack Submission

    Posted Aug 20 2008, 01:29 PM by Steve with 5 comment(s)
    Filed under:
  • Defining Community: My Quest

    I have been working on my own personal beliefs as to what community is and what community should be. For those who read this, please understand I am still very early in my journey and what is contained below is only my current thinking on this matter. I have by no means reached a full understanding or drawn concrete conclusions as to what I believe community to be. I am also not making any sort of geographic comparisons. I have enjoyed every group I have visited or spoken at, and ultimately a group will provide what its members need or it will fail.

    Background

    I have always enjoyed people. In fact, I really enjoy hanging out with people, learning from other people, and bringing people together into community. In my view, we are all just 6.6 billion people trying to live our lives. When you look around, almost everything you see is here because there are people; roads, signs, houses, soda, clothes, etc. Therefore, to me, people are the most important things in the world.

    Defining Community

    Through my passion for people, I have come to believe that community is about more than just user group meetings, code camps, and attendance levels. In my opinion, those characteristics define an association or a club. And while to some extent the level of participation of the attendees in organized events is a factor, I believe community is defined by what happens outside of the organized events. I would summarize this as relational dynamics, or the inter-personal relationships between members of the community.

    The problem with trying to define community, however, is that "Community [is] defined similarly but experienced differently by people with diverse backgrounds."(1) Despite the difficulty in defining community, the study was able to identify 18 common characteristics among the respondents. These characteristics include locus, sharing, action, ties, responsibility, unity, services and survival:

    Characteristic

    My Definition

    Locus

    A community must have a physical location in relative proximity to its members. In lieu of a physical location, a common virtual location such as Twitter and IRC rooms can suffice. Face-to-face interactions are required at some point.

    Sharing

    A community exists to provide for common interests and shared perspectives.

    Action

    A community must hold events and take action to provide activities to its members.

    Ties

    A community must have social ties and relationships among its members.

    Responsibility

    Individual members of a community must be able to take a personal sense of responsibility in the well-being of the community as a whole.

    Unity

    Community requires fellowship among its members.

    Services

    Community should make available common services and programs for its members.

    Survival

    A community must be adaptable and resourceful to ensure its own survival.

     

    Lessons Learned

    I found myself taking away several important concepts from the time I have spent at various user groups and code camps in the east region.

    Extracurricular Interaction

    Extracurricular interaction and communication is extremely important for community. Imagine a new home development with a homeowners association. If the only interaction the neighbors of this development had was at the HOA meetings, would you really call the neighborhood a community? Of course not, it takes interaction outside of organized events to foster relationships and community. One thing I have found that supports this is to invite attendees to a geek dinner after every monthly meeting. I feel this is a fantastic way to help build relationships and promote interaction among a community's members.

    Leadership

    I have learned that leadership is more important than I had previously thought. Leadership in fact plays a fundamental role in the success of a true community. While I had heard this said previously, I did not understand the extent to which this is important. An effective community requires passionate and dedicated leaders who not only strive to develop community as a whole, but who also work to bring other members up into more active roles, who then become leaders in their own right.

    Along with leadership comes the understanding that community is not for the leaders, it is "by the community, for the community." I believe all of the groups I have had the pleasure of participating in already do a pretty good job of this and strive to provide relevant and worthwhile events for their attendees.

    Time

    Community is also not something that happens overnight. Relationships take time to grow and develop and have to be fostered. During this time, one of the beneficial steps I have seen user groups take is to invite all of their attendees out for food and/or drinks after each event, at the attendees cost. This is a wonderful way to help promote the interpersonal relationships required for community.

    There is also an evolution that happens within user groups as time progresses. This evolution can occur in virtually every aspect of a user group, and it is a very good thing. It helps the group stay fluid, involved, and meeting the needs of its attendees.

    Communication

    A community must be willing to openly communicate its thoughts, concerns and beliefs. One of the best examples I have seen of this communication were some discussions that took place at the CodeStock conference in Knoxville Tennessee on August 9th, 2008. While some folks disagree about the "Open Spaces" designation that was given to these discussions, I feel that what matters most is not whether it conformed to any one person's ideal or followed some technical formula, but rather the benefit it had on the individuals who participated. I myself felt immensely privileged to have taken part in those discussions with so many of my peers that I know and respect. The free flow of ideas, mutual respect of differing opinions, the flow of ideas from one to the next, no topic being off limits, and the knowledge that was imparted were extraordinary.

    If you would like to view one of these discussions, Wally McClure (More Wally!) has provided several videos on his blog about CodeStock:
    http://aspnetpodcast.com/CS11/blogs/asp.net_podcast/archive/2008/08/15/asp-net-podcast-show-122-codestock-openspaces-discussion-regarding-orm.aspx

    http://aspnetpodcast.com/CS11/blogs/asp.net_podcast/archive/2008/08/20/asp-net-podcast-show-123-community-discussion-at-codestock.aspx

    References

    1. "What Is Community? An Evidence-Based Definition for Participatory Public Health" by Kathleen M. MacQueen, PhD, MPH, Eleanor McLellan, MA, David S. Metzger, PhD, Susan Kegeles, PhD, Ronald P. Strauss, PhD, Roseanne Scotti, MA, Lynn Blanchard, PhD and Robert T. Trotter, II, PhD
      American Journal of Public Health 1929-1938,December 2001, Vol 91, No. 12
  • How did you get started in software development?

    A certain meme has been going around asking developers how they got started in software development. While I'm a little late to the game, here's my story:

    How old were you when you started programming?

    I was around 15 years old when I started playing with HyperCard and Filemaker Pro on a Mac IIci. I loved it from the start; my dad would literally have to unplug the computer for me to go to bed at night. I really didn't start getting into commercial development though until I was around 19-20 years old.

    How did you get started in programming?

    I built a couple websites during high-school using Microsoft Publisher and Microsoft FrontPage, and while I now feel these were the worst tools possible, they enabled me to visually design a web page, and then view the underlying markup. From there I moved on to glorified notepads (Dreamweaver, Homesite, Interdev, and Visual Studio). After moving back to the Philadelphia area, I bought an HP desktop and sat in front of it all day every day for about six months and just soaked up as much as I could.

    What was your first language?

    My first language was HyperTalk back in the HyperCard days. My first real development languages were Visual Basic and ASP.OLD.

    What was the first real program you wrote?

    It's been so long now that I don't remember. I built my first website in Microsoft Publisher, the second in Microsoft FrontPage, and from there, again, I moved onto glorified notepads. The first real project was probably a client website.

    What languages have you used since you started programming?

    Visual Basic ~6, Cold Fusion, ASP.OLD, Java, HTML, CSS, JavaScript, VBScript, T-SQL, etc. Not sure all of those are 'languages' per se, but…

    What was your first professional programming gig?

    My first gig was from a guy who called me up to sell me on Amway. While I wasn't interested in 'sales', he also co-owned a small software company and gave me my start.

    If you knew then what you know now, would you have started programming?

    Absolutely. For me, programming is like the only thing in life that's logical. Even when computers screw up for seemingly illogical reasons, under the covers there are some bits that can explain it. I can only tell people that it's the way my brain is wired; I don't think I could quit this business if I tried.

    If there is one thing you learned along the way that you would tell new developers, what would it be?

    For starters, find what really gets you passionate in software development, whether that's a specific technology, an industry, or a particular focus, whatever. It's really the underlying passion that counts.

    Second, understand that a career in technology means you must constantly be learning, growing, and rethinking. Technology, as well as patterns, practices and methodologies, are always in a state of constant flux. If you're not willing to commit yourself to this, you may want to find another career.

    Third, if you have the passion, don't worry so much about degrees or certifications. For me, I'd rather hire someone with a thirst for knowledge, passion in their heart, and an aptitude for programming over someone with an 'education' any day.

    Fourth, get involved in the community: attend local user groups, join the online forums, participate in community or open-source projects, etc. Not only do you get a chance to give back, but you learn so much along the way.

    What's the most fun you've ever had ... programming?

    I don't think there is any one project I can single out. The most fun I've had programming however have been client projects that significantly impacted their business. I also enjoy little personal projects, either because they were 'cool', or they helped automate and simplify something in my life, or taught me something new.

  • Selected for DevTeach Montréal 2008

    I received the news today that I have been selected as a presenter for DevTeach Montréal 2008, taking place in Montréal, QC from December 1st to December 5th of this year. I am super-excited to have been selected, and I am looking forward to some great interactions with the attendees and fellow speakers.

    I will be presenting three sessions:

    • Automation with MSBuild 3.5 and Team Build 2008
    • Building Custom Team Foundation Server Reports
    • Writing Awesome Code with VS2008 Developer Edition

    For more information about DevTeach and to register for the event, please visit the following link:
    http://www.devteach.com

    I hope to see you there!

  • MVP TV

    On Thursday, August 21st, I will be appearing as the first presenter on the new MVP TV from 12:00 PM to 1:00 PM eastern time. Here are the details:

    All the Interaction of the MSDN Chats but with the richer experience of Live meeting and hosted by the recognized world technology leaders –the Visual Studio Team System Microsoft MVP's. In this opening session of MVP TV, please join one of the most celebrated and recent Team System MVP Steve Andrews covering off Automation with MSBuild 3.5 and Team Build 2008. Did you know that .csproj and .vbproj files are really MSBuild files?  More than build processes though, MSBuild is a full-featured automation language. It includes structured control flow, variables, refactorability, error handling, logging, and powerful extensibility. You can easily integrate MSBuild into your own enterprise processes and start adding value right away.  We'll also look at how Team Foundation Build extends on MSBuild and adds robust integration with Team Foundation Server. This is one show you will not want to miss!

    To get more information and register for this event, visit the following link:
    http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032386225&EventCategory=4&culture=en-US&CountryCode=US

    I hope to see you there!

    Posted Aug 15 2008, 06:50 PM by Steve with no comments
    Filed under: ,
  • Using a Single Strong Name Key across Multiple Projects

    When strongly naming project, the Visual Studio default behavior is to copy the source key file into each and every individual project. This might not be ideal in all cases as you may want to be able to change strong name keys across all the projects at a future time.

    Fortunately, there is a workaround using the linked files trick.

    First, create your *.snk file in the solution directory. If the project is under source control, be sure to add the *.snk file as a solution file to the solution node in Visual Studio. You can do this by right-clicking on the solution node in the Solution Explorer tool window, choosing "Add Existing Item…" and selecting the *.snk file.

    Then, in each project you wish to sign, right-click on the project node and choose Add > "Existing Item…" Browse to and select your *.snk file, but here's the twist. Instead of just clicking Add, use the dropdown arrow on the right side of the Add button and select Add as Link. This adds a reference to the original file location without copying it locally. At compile time, linked files are treated as local (to the project) files.

    Finally, right-click on the project and choose Properties. Navigate to the Signing tab, check the "Sign the assembly" checkbox, and from the dropdown list, select the fully-qualified name (FQN) that is displayed. Then type Ctrl+Shift+S (Save All) to save. The important thing to remember here is that even though the FQN was displayed in the dropdown, a relative path is stored in the *.*proj file.

    If you would like to validate that the relative path is stored, you can right-click on the project node, choose "Unload Project", right-click again, and choose Edit… In the first PropertyGroup, there is a node named AssemblyOriginatorKeyFile. You will see it is set to a relative path.

    Hope this helps

  • I’m a Team System MVP

    Last week (yes, I've been quite busy), I received an email from Microsoft announcing that I had been selected to be a Microsoft MVP for Team System. w00t! I am super excited as this is certainly a product and a product team I am very passionate about. My hope is that by receiving the MVP award I can do more of what I've been doing, because as a lot of you know I am somewhat of a community junkie <grin>

    Thank you Dani

    First and foremost I have to thank Dani Diaz, my Microsoft Developer Evangelist, who was instrumental in helping me focus my energy, providing mentoring, and in supporting my community efforts. He also nominated me for the award.

    Thank you others

    There are plenty of others who have helped me get here (and I'm sure I'll miss some): all the user group leaders who let me speak at their events, the Philly .NET User Group, Jason Gaylord, Don Demsak, Grant Holliday, Kevin Goff, Chris Love, and John Baird.

    How?

    The most common question I get now is how does someone go about getting the MVP award. There are a few points I'd like to make on this topic. First, the MVP award is given "to thank individuals for their exceptional contributions to technical communities". In essence, it has to be earned through community participation. This includes stuff like helping out in the newsgroups, speaking, and writing. Second, there are no hard and fast criteria to build an MVP plan off of.

    What's Next?

    I certainly have no plans to slow down. I'd like to maintain pace of about 3 presentations a month, as well as to start speaking at larger conferences. I am also talking to an online publisher about having a weekly column loosely focused on Team System and ALM. Who knows, maybe even a book is in order?

    But for now, who needs an MVP speaker to talk about Team System and ALM?

    P.S. - Congrats to all the other MVPs awarded this round, especially John Baird!

    Posted Jul 09 2008, 01:41 PM by Steve with 1 comment(s)
    Filed under: ,
  • DevSource Article: DevEnv Command Line Switches

    My second article has been published on DevSource.com. This one inspired by a phone call with a developer experiencing problems with add-ins in their development environment. It seems a lot of developers aren't aware of the command line options with Visual Studio, and I thought I'd cover the topic.

    From the abstract: "Visual Studio includes several command-line switches that you can use to have more control over how VS operates."

    http://www.devsource.com/c/a/Add-Ons/Visual-Studio-Command-Line-Switches/

  • DevSource Article: Automation with MSBuild

    I am very excited to announce that last Thursday my first article was published on DevSource.com. While it has taken a bit of time away from blogging, I had a blast doing it. From the abstract: "The MSBuild utility is a powerful replacement to older make-style programs. You use it every day, whether you realize it or not. Why not make the most of it?"

    My second article, detailing the command line switches for Visual Studio, is currently under review and will hopefully be published later this week.

    You can check out the first article here, and I'd love to get some feedback on mi numero uno:

    http://www.devsource.com/c/a/Add-Ons/Automation-with-MSBuild/

  • Team System Link Love - 9

    Just checked the reports in FeedDemon, and I think it's only fair that if you haven't blogged in over a year, you're coming off my list.

    General

    Visual Studio and Team System 2008

    Team Foundation 2008 SP1

    Events

    Rosario

    Internal Dogfooding

    Visual Studio Extensibility

    Team Foundation Web Access

    Power Tools

    Installation and Administration

    MSBuild and Team Build

    Testing and Performance

    Visual Studio Tips

    Tools

    Posted May 03 2008, 02:31 PM by Steve with 2 comment(s)
    Filed under:
More Posts « Previous page - Next page »
Powered by Community Server (Commercial Edition), by Telligent Systems
© Platinum Bay | Some Rights Reserved Creative Commons License

Disclaimer: The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Feel free to challenge me, disagree with me, or tell me I'm completely nuts in the comments section of each blog entry, but I reserve the right to delete any comment for any reason whatsoever (abusive, profane, rude, or annonymous comments) - so keep it polite, please.