Thursday, October 28, 2010

User Story Screen

The user story screen is the starting point for all work. It tells a story from the user perspective that we can use to determine what work needs to be done. This story should be short so that the work can be done in a couple of days. More complicated stories should be broken up into smaller stories so the work can be broken into pieces. My rule of thumb is if the story is takes more than a paragraph or so, you need to break it up.

Here’s the User Story screen from VS.

User Story 

Title – A one sentence description of the story.

Assigned To – The single individual responsible for the story.

State – Indicates the current state of the story.

  • Active – The story is currently being worked on or waiting to be worked on.
  • Closed – The story was closed without being resolved.
  • Resolved – The story was completed and implemented.

Reason – The reason the item is in the current state. Matching states are in parenthesis.

  • New – A new story (Active).
  • Abandoned – The story was no longer relevant (Closed). 
  • Out Of Scope – The story does not fall within the scope of the current project (Closed).
  • Rejected – The story was not desired by management, too complex or violates system integrity (Closed).
  • Code complete and unit tests pass – Work has been completed and all unit tests pass (Resolved).

Area – The functional area of the application such as clients, services, etc.

Iteration – The iteration of the system where the task will be worked on. This may correspond to sprints but it could be version number based.

Stack Rank – The relative importance of the item to the product owner(s).

  1. System/process must support this user story / functionality
  2. System/process should support this user story / functionality
  3. System/process could support this user story / functionality

Story Points – An indicator of the complexity of the user story. This value allows for comparison between stories based on an abstract value rather than time (which is not the same between developers).

Risk – The level of risk to the project if the item is not completed.

A Simple Man’s Guide To Using VS & TFS 2010

One of the areas I personally struggle with is planning and organizing. I’m a doer. Git-r-dun! I can see a task and wiz through coding it. As long as I’m the only developer, this works great. When you’re the team lead, it’s not a good thing. What ends up happening is that everything is in your head. The team doesn’t know what to work on and the users don’t know the status of the project.

Want to know if you’re in this category? Take a day off and see what happens! If you come back and find that things have gone haywire, welcome to the club.

So what’s the solution? For me, learning Agile and Scrum basics and using VS/TFS 2010 to keep track of stuff. For the next several posts, I ‘m going to document how we’re going to use VS/TFS 2010. If you’ve done it differently, please let me know. I’ll update this post as the master to keep track of everything together.

The first step will be to create a user story for each discrete activity. Then we’ll write the test cases that insure things are working correctly. Finally, we’ll create the tasks for the actual coding. The following links cover each of the items in VS/TFS 2010 and how we’ll be using them.

User Story

Test Case

Task

Bug

Monday, June 28, 2010

Quick Script to Create Constants from All Columns Names

One thing I like to do in all my apps is create a static class that holds constants for all Column/Property names. This just avoids messing up names in places where I need to use strings. This is particularly useful when checking that attributes have been applied.

select 
     'public const string ' + COLUMN_NAME + ' = "' + COLUMN_NAME + '";'
from INFORMATION_SCHEMA.COLUMNS
where table_Name in ('Agencies', 'Customers')
group by COLUMN_NAME
order by COLUMN_NAME

The code produced looks like this:

public static class ColumnNames
{
    public const string AgencyName = "AgencyName";
    public const string CustomerName = "CustomerName";
}

I can then refer to the name using something like

VerifyPropertyHasAttribute(ColumnName.AgencyName, typeof(RequiredAttribute));

Script to Create MetadataType classes

UPDATE: Here's the same thing for EF 4.1 and Code First.

I’ve been using the DataAnnotations for a validation with MVC2 and Silverlight 4. They both rely on metadata (buddy) classes. The Entity Framework now will create metaclasses for you but LinqToSql didn’t get this feature.

This short script will create a partial class and a metaclass for the specified table.

 

declare @TableName varchar(256) = 'Agencies'
declare @EntityName varchar(256) = 'Agency'
declare @TableSchema varchar(256) = 'dbo'

declare @ColumnName varchar(256)
    , @DataType varchar(256)
    , @MaxLength int
    , @Nullable varchar(5)
    
declare @Lines table (Line varchar(1000))

insert into @Lines select 'partial class ' + @EntityName
insert into @Lines select '{'
insert into @Lines select '}'
insert into @Lines select 'public class ' + @EntityName + 'MetaClass'
insert into @Lines select '{'

declare cols cursor for 
    select COLUMN_NAME, Data_type, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE
    from INFORMATION_SCHEMA.COLUMNS
    where table_Name = @TableName and table_schema = @Tableschema
    order by ORDINAL_POSITION

open cols

fetch next from cols into  @ColumnName, @DataType, @MaxLength, @Nullable
while @@FETCH_STATUS = 0
begin
    if @DataType in ('varchar', 'char') and @Nullable = 'NO'
        insert into @Lines select char(9) + '[Required]'
        
    if @DataType in ('varchar', 'char')
        insert into @Lines select char(9) + '[StringLength(' + CAST(@MaxLength as varchar) + ')]'
    
    insert into @Lines select char(9) + 'public object ' + @ColumnName + ' { get; set; }'
    
    insert into @Lines select char(9) + ''
    
    fetch next from cols into  @ColumnName, @DataType, @MaxLength, @Nullable
end

close cols
deallocate cols
insert into @Lines select '}'

select * FROM @Lines

Saturday, April 3, 2010

Breaking? Change In Asp.Net MVC2

In MVC1, by default an empty textbox was sent as string.Empty to the controller on POST. In MVC2, it now sends the value as NULL. See ModelMetadata.ConvertEmptyStringToNull. I think there are some valid reasons for why they made the change – see Brad Wilson’s comment for more. If your system relies on string.Empty, you’ve got a problem.

There are several options for handling this. The first is using the DisplayFormatAttribute on every property that needs it. Painful. Here’s an example:

[DisplayFormat(ConvertEmptyStringToNull=false)]
public string FirstName { get; set; }

You can also write a custom binder that sets this value to false. Like so:

public class CustomStringModelBinder : DefaultModelBinder 
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {

        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);   
        if (bindingContext.ModelType == typeof(string))
        {   
            bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
            if (value == null || string.IsNullOrEmpty(value.AttemptedValue))   
                return "";
        }
        return value.AttemptedValue;
    }
}

You’ll need to add the following to the Global.asax Application_Start method to use your custom model binder.

ModelBinders.Binders.Add(typeof(string), new CustomStringModelBinder());

One thing to note, if you use the RequiredAttribute, you don’t need to worry about the null because it will invalidate the model based on the NULL.

Which option is better? Neither. Simply pick the default for ConvertEmptyStringToNull and then use DisplayFormat to handle the exceptions. Not perfect but not bad.

I do wish that this was configurable in some way instead of creating a ModelBinder.

Thursday, April 1, 2010

Navigation Using Areas in Asp.Net MVC

Asp.Net MVC2 introduced Areas as a feature part of the framework. Areas allow you to divide your application into logical units. It also makes your Url’s look more accurate.

Thinking of a generic store they might have areas such as Inventory, Payroll, Employees and Sales. Here are example ActionLinks for each area.

<%= Html.ActionLink("Home", "Index", "Home", new { area = "" }, new { })%>
<%= Html.ActionLink("Payroll", "Index", "Main", new { area = "Payroll" }, new { })%>
<%= Html.ActionLink("Inventory", "Index", "Main", new { area = "Inventory" }, new { })%>
<%= Html.ActionLink("Employees", "Index", "Main", new { area = "Employees" }, new { })%>
<%= Html.ActionLink("Sales", "Index", "Main", new { area = "Sales" }, new { })%>

Notice that the area is passed as a route value. I wrote my own overload for the ActionLink that takes the Area as an argument.

public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, string areaName, object routeValues, object htmlAttributes)
{
    RouteValueDictionary routes = new RouteValueDictionary(routeValues);
    RouteValueDictionary attributes = new RouteValueDictionary(htmlAttributes);
    routes.Add("area", areaName);
    return helper.ActionLink(linkText, actionName, controllerName, routes, attributes);
}

This makes our links look as follows:

<%= Html.ActionLink("Home", "Index", "Home", "", new { }, new { })%>
<%= Html.ActionLink("Payroll", "Index", "Main", "Payroll", new { }, new { })%>
<%= Html.ActionLink("Inventory", "Index", "Main", "Inventory", new { }, new { })%>
<%= Html.ActionLink("Employees", "Index", "Main", "Employees", new { }, new { })%>
<%= Html.ActionLink("Sales", "Index", "Main", "Sales", new { }, new { })%>

If you haven’t worked with Areas yet, your probably looking at the routes and noticing that each area link has a controller called Main with an Index method. You might think we’re calling the same controller and passing the area name as parameter. That is not what we are doing, each area has a controller named Main. Maybe the generated links will help.

http://somedomain.com/Payroll/Main
http://somedomain.com/Inventory/Main
http://somedomain.com/Employees/Main
http://somedomain.com/Sales/Main

Next thing you should notice is that the link for returning to the Home page has an empty area. We need this so that when we’re in an area we can move back up to the root. Otherwise, we’ll get a Resource Not Found error indicating showing that it’s looking for Home in that Area. This can also work in your favor. If you have a Home controller (or Main) in all of your areas, MVC will, by default, navigate within the Area and you don’t need to change your Site.Master.

Overall, areas are a great feature for keeping your applications organized.

Friday, March 19, 2010

Lose your Find / Replace window?

Working in VS2010, I suddenly lost my Find /Replace window. I found this post from Rick Strahl but unfortunately that doesn’t work for me since ALT-M is mapped as a menu shortcut.

Solution: Window –> Dock.

This will dock the window and then you can move it to your desired location.

Cheers!

Friday, March 12, 2010

Visual Studio/SQL Server Management Studio Find and Replace with Regular Expressions

I needed to format a bunch of strings in SQL Management Studio (this works for VS also) and finally took the time to figure out Find/Replace with Regular Expressions.

The first thing to note is that VS/SSMS use different regex codes (don’t know why but I’m sure they have their reasons). You can find the list of codes here.

I have list of 3 strings containing 10 digits followed by a space and then 3 letters. I wanted to replace the digits with the word Descriptor.

Here’s my data:

1234567870 ABC
1234567880 DEF
1234567890 GHI

Here’s my find expression:

[0-9]^10 {[A-Z]^3}

Here’s my replace expression:

Descriptor: \1

Here’s my result:

Descriptor: ABC
Descriptor: DEF
Descriptor: GHI

The key is the {} brackets and \1. The {} brackets tell the engine I want to keep whatever matches the expression. This is called a Tagged Expression. The \1 says place the first tagged expression here. I can create multiple tagged expressions and place by using \1, \2, etc. I can even change the order in the result because they are numbered based on their order in the original expression. You can use newlines (\n) so that you can break lines apart).

This is great for creating some testing stuff or moving non-standard data.

Sweet!