Tuesday, January 31, 2006

 

Article of the Week 12

American Kestrel

Title

In Defense of Not-Invented-Here Syndrome

Author

Joel Spolsky

Description

This two and a quarter page article is quite good and, I think quite applicable to the non-profit organization that I work for. They are a legal organization, not in the business of software development.

After spending years using outsourced solutions, they realized that they only way they could get the quality applications they needed to meet their ever-growing needs would be to develop solutions in-house. Now they have an entire team of software developers who can barely keep up with all of the requests coming in.

Favorite Quote(s)

"If it's a core business function -- do it yourself, no matter what...If you're a pharmaceutical company, write software for drug research, but don't write your own accounting package."

Suggestions?

Do you have a suggestion for the Article of the Week? I would love to hear about it. Please drop me a line and tell me about your favorite online articles/essays. If I like it enough, it may show up in next weeks AotW.


 

C# Code Snippets Missing From Visual Studio 2005

Apparently Visual Studio 2005 does not come with a complete set of C# code snippets (VB.NET does).

You can find them online here:

Visual Studio 2005 Code Snippets

Thursday, January 26, 2006

 

MSBuild: How to Write a Task

I am working on writing my first task for MSBuild. Here are a couple of helpful links I found along the way:

MSDN's How To: Write a Task

MSBuild Team Blog's How To: Implementing Custom Tasks - Part I

Monday, January 23, 2006

 

Article of the Week 11

Title

Peer Reviews in Software: A Practical Guide - Chapter 2 - Sections 1 & 2

Author

Karl Wiegers

Description

Peer Reviews in Software by Karl WiegersOK, I have already written a nice long description for today's AotW. Unfortunately, I managed to close the browser window I was working in, without saving the blog post. Aaaaaaaaaaargh!!! Rather than run out of my room screaming, I will try and make this short, sweet, and use Notepad2 from now on.

This week's article/essay is neither an article nor an essay. It is the first two sections of the second chapter of Karl Wiegers' book, Peer Reviews in Software. The sections are titled Scratch Each Other's Backs and Reviews and Team Culture. The entire chapter is a bit too long to be an AotW (14 pages). However, the first two sections contain a lot of good information and are a much shorter read (2 pages).

Reading through these sections reminded me that our team needs to be more deliberate about conducting code reviews. We have time set aside for them each week, but, as is often the case, other things tend to get in the way. Code reviews promote idea sharing, and can increase the quality of code.

A big "Thank you!" goes out, to Jeff Atwood for blogging about Karl's work and pointing out that these sample chapters are available.

Favorite Quote

"In a healthy software engineering culture, team members engage their peers to
improve the quality of their work and increase their productivity."

Suggestions?

Do you have a suggestion for the Article of the Week? I would love to hear about it. Please drop me a line and tell me about your favorite online articles/essays. If I like it enough, it may show up in next weeks AotW.


Saturday, January 21, 2006

 

Amazon.com = Ugly Address Bar

The new Recommended Software Development Books page is now available. This is the section you will find a list of books that have assisted me over the course of my career.

All of the book links lead to Amazon.com. I felt that Amazon.com URLs are long and ugly, with a face only a mommy URI could love. To solve cure this case of the uglies, I created the book.asp page.

Book.asp sits in the root of my website and accepts a single query string parameter, ASIN. The ASIN is Amazon.com's unique identifier for all of its books. Book.asp grabs the ASIN and creates a dynamic Amazon.com URL, including my associateID. Then it redirects the user to the newly created URL. Compare the two:

Ugly URL: http://www.amazon.com/exec/obidos/redirect?link_code=as2&path=ASIN/0596006993&tag=tod1dsthought-20&camp=1789&creative=9325

Pretty URL: http://www.tod1d.net/book.asp?asin=0596006993

Eventually, I will convert book.asp from classic ASP to ASP.NET and enable URL rewriting. After which, the URL will look something like this:

Beautiful URL: http://www.tod1d.net/book/0596006993

If you feel like you get some value from this site and you would like to assist me with paying for the cost of hosting, please click on one of the books in the new section, or one of the URLs above, then purchase something from Amazon.com within the next 30 days. It does not have to be any of the books I recommend.

Thursday, January 19, 2006

 

Sharing Authentication Across ASP.NET

Back in October, I posted a question on the microsoft.public.dotnet.framework.aspnet newsgroup. The title/subject was "Sharing Authentication Across ASP.NET". I did not receive a response. Five days later I replied to the post, stating that I had found a solution, and then made the mistake of telling people they can contact me for the solution. What I should have done is blogged the problem and the solution.

Below you will find a copy of the problem as I described it in my newsgroup posting, as well as a copy of the solution e-mail that I send out to those who request it.

Problem

Hi All,

I have two ASP.NET applications which I am trying to have share forms
authentication. But I am running into problems.

App A is an ASP.NET 2.0 Beta 2 application. App B is an ASP.NET 1.1
application (Telligent's Community Server) compiled with VS.NET 2003.

App B runs in a virtual sub-directory of App A. Both applications run
fine. Both site's ASP.NET tabs are set appropriately (A = 2.0.5X B =
1.1.X)

I have done a lot of research and I believe both applications are setup
to share the same authentication cookie.

Here are the steps I took:

1. Added identical to the root web.config of each app.
Example:



<!-- Keys shortened for brevity -->
<machineKey
validationKey="5FC1F907ADE8C5800DB3B1F195B8E...EADFF5E78070CAA"
decryptionKey="7D27FEC08...CF3771C74CE3"
validation="3DES" />


2. Changed in each root web.config to be identical.
Example:



<authentication mode="Forms">
<forms name=".CommunityServer"
loginUrl="security/Login.aspx"
protection="All" timeout="20"
path="/" />
</authentication>


3. In the App A web.config I added the following:



<location path="main">
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>


4. In the App B web.config I added the following:



<authorization>
<deny users="?" />
</authorization>


According to the sites I have read on how to do this, the above changes
should be enough. I try the following:

1. When attempting to get to the /main directory of App A, I am
redirected to the login.

2. I successfully login. Using Tracing, I can see that my
.CommmunityServer cookie has been set.

3. I attempt to get to the virtual sub-directory (App B). I am
redirected to the login page.
4. Without logging in again, I go to the /main directory of App A and I
get there without being redirected. Viewing the Tracing output on the
page, I can see that my cookie is still set.

I have put the following code into the Application_AuthenticateRequest
event handler of App B's Global.asax file:

----------BEGIN CODE-------------------------



protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
bool cookieFound = false;

HttpCookie authCookie = null;
HttpCookie cookie;
string cookieNames = "";
for(int i=0; i < Request.Cookies.Count; i++)
{
cookie = Request.Cookies[i];

cookieNames = cookieNames + cookie.Name + "\n";
if (cookie.Name == FormsAuthentication.FormsCookieName)
{
cookieFound = true;
authCookie = cookie;
break;
}
}

// If the cookie has been found, it means it has been issued from either
// the windows authorisation site, is this forms auth site.
if (cookieFound)
{
// Extract the roles from the cookie, and assign to our current principal,
// which is attached to the
// HttpContext.
FormsAuthenticationTicket winAuthTicket =
FormsAuthentication.Decrypt(authCookie.Value);
string[] roles = winAuthTicket.UserData.Split(';');
FormsIdentity formsId = new FormsIdentity(winAuthTicket);
GenericPrincipal princ = new GenericPrincipal(formsId,roles);
HttpContext.Current.User = princ;
}
else
{
// No cookie found, we can redirect to the Windows auth site if we
// want, or let it pass through so
// that the forms auth system redirects to the logon page for us.
throw new ApplicationException(@"Invalid login from here.
FormsCookieName:"
+ FormsAuthentication.FormsCookieName + "\n" +
"CookieNames:" + cookieNames+ "\n");
}

}


-----------------END CODE----------------------------

The cookie with the name ".CommunityServer" is found, but when the line
calling "FormsAuthentication.Decrypt(authCookie.Value);" executes, I
get the following error:

-----------BEGIN ERROR-------------------------------
Bad Data.
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.

Exception Details: System.Security.Cryptography.CryptographicException:
Bad Data.

Source Error:

Line 100: // HttpContext.
Line 101: //throw new ApplicationException("CookieName: " +
authCookie.Name + "\n" + authCookie.Value);
Line 102: FormsAuthenticationTicket winAuthTicket =
FormsAuthentication.Decrypt(authCookie.Value);
Line 103: string[] roles = winAuthTicket.UserData.Split(';');
Line 104: FormsIdentity formsId = new FormsIdentity(winAuthTicket);

Source File: c:\dev\cs_bsinterns\web\global.asax.cs Line: 102

Stack Trace:

[CryptographicException: Bad Data.
]
System.Security.Cryptography.CryptoAPITransform._DecryptData(IntPtr
hKey, Byte[] rgb, Int32 ib, Int32 cb, Boolean fDone) +0

System.Security.Cryptography.CryptoAPITransform.TransformFinalBlock(Byte[]
inputBuffer, Int32 inputOffset, Int32 inputCount) +805
System.Security.Cryptography.CryptoStream.FlushFinalBlock() +40
System.Web.Configuration.MachineKey.EncryptOrDecryptData(Boolean
fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length) +139
System.Web.Security.FormsAuthentication.Decrypt(String
encryptedTicket) +114
CommunityServerWeb.Global.Application_AuthenticateRequest(Object
sender, EventArgs e) in c:\dev\cs_bsinterns\web\global.asax.cs:102

System.Web.SyncEventExecutionStep.System.Web.HttpApplication+IExecutionStep­.Execute()
+59
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&
completedSynchronously) +87

---------------------------------------------------------------------------­-----
Version Information: Microsoft .NET Framework Version:1.1.4322.573;
ASP.NET Version:1.1.4322.573

-----------END ERROR---------------------------------

Solution

I added the following code to the login page of the primary
application, right after the code that authenticates the user



// Create cookie for CommunityServer
string userName = txtUserName.Text.Trim();
RememberEmail(userName);
HttpCookie userCookie = null;
if (Request.Cookies ["TrustedUserName"] == null)
{
userCookie = new HttpCookie("TrustedUserName");
userCookie.Value = txtUserName.Text.Trim();
userCookie.Expires = DateTime.Now.AddMinutes(20);
Response.Cookies.Add(userCookie);
}
else
{
Response.Cookies["TrustedUserName"].Value = userName;
Response.Cookies["TrustedUserName"].Expires = DateTime.Now.AddMinutes(20);
}



I then create a page titled CheckLoggedIn.aspx and added the following
code to the Code Behind .cs file:



string cookieName = "TrustedUserName";
HttpCookie authCookie = Context.Request.Cookies[cookieName];


if (null == authCookie)
{
Response.Redirect("../security/login.aspx");
}
else
{

LoginUser( authCookie.Value);

Response.Redirect("forums/");
}
}

private void LoginUser(string userName)
{
User userToLogin = null;

userToLogin = Users.FindUserByUsername(userName);
LoginUserStatus loginStatus;

if ( userToLogin.Username == null)
{ // User is not currently in CS database.

// Add user to database.
User newUser = new User();
newUser.Username = userName;
newUser.Email = userName;
newUser.AccountStatus = UserAccountStatus.Approved;
newUser.Password = "123456";
newUser.IsAnonymous = false;
Users.Create(newUser, false);

// Assign newly created user to userToLogin
userToLogin = newUser;
}

if (userToLogin != null)
{
loginStatus = LoginUserStatus.Success ;
}
else
{
loginStatus = LoginUserStatus.InvalidCredentials;
throw new ApplicationException("Invalid Username: " + userName);
}

bool enableBannedUsersToLogin =
CSContext.Current.SiteSettings.EnableBannedUsersToLogin;

// Change to let banned users in
//
if (loginStatus == LoginUserStatus.Success ||
(enableBannedUsersToLogin && loginStatus == LoginUserStatus.AccountBanned))
{
// Are we allowing login?
// TODO -- this could be better optimized
if (!CSContext.Current.SiteSetting s.AllowLogin && !userToLogin.IsAdministrator)
{
throw new CSException(CSExceptionType.UserLoginDisabled);
}

FormsAuthentication.SetAuthCookie(userToLogin.Username, false);

// Redirect to forums
Response.Redirect("forums");

}
}

Tuesday, January 17, 2006

 

Article of the Week 10

Title

Tips on Optimizing SQL Server Database Design

Author

Uknown

Description

This week's article is intended to get the database optimization juices flowing. Probably not a lot of new ideas in here, but it was a great refresher for me.

In my case, it reminded me that I may want to denormalize some of the Results Case specific data that I get by using a SELECT statement with 13 joins (ouch). I should take some time to review and refactor some of the more complex stored procs to see where there is room for improvement.


Thursday, January 12, 2006

 

Reach Out And Touch Me

After nearly a year and a half, I finally setup a way for you to contact me directly.

I used an old Active Server Pages form that was leftover from the old NetSalad.com site. I have not worked with ASP in awhile. I was going to create the contact form using ASP.NET 2.0 but the ASP script was handy, and it ASP pages are so much more lightweight than ASP.NET pages. It seemed like the appropriate solution for this task. I like the fact that I can edit the pages easily with Notepad2 and I don't have to compile anything.

Monday, January 09, 2006

 

The Article of the Week 9

Title

Five Worlds

Author

Joel Spolsky

Description

Joel believes are the five worlds of software development. They are:

  1. Shrink-wrap
  2. Internal
  3. Embedded
  4. Games
  5. Throwaway

He goes on to give a description of each world and the type of programming environment you can expect.

You will use different methodologies based on which world you are coding in. Most of the books you read, regarding programming methodology, do not specify which world the author is developing in. What the author doesn't tell you is that he normally works as a consultant for large corporations. This is unfortunate for an inexperienced programmer who reads this book and blindly applies the methodology to the intranet site he is working on for a 10 person non-profit organization.

Picture of Send To context menu

When I began programming, I was obsessed with determining the "best practice" for each process I would tackle. I would spend a quarter or more of the project doing nothing but researching which methodology I should use. It didn't matter what methodology I had used on my last project, I could never be completely convinced that it was the "best practice". The primary reason was that every time I read a book, article, newsgroup posting, or listened to someone speak a user group meeting, I would be confronted with another "best practice" that replaced the one I had learned only a day earlier.

I nearly drove myself and my manager nuts trying to figure out what methodology to use any given day of the week. Thankfully he was very patient. He used to be a full-time programmer, and had been disillusioned enough with "best practices" to have become very practical and productive. With his help I eventually came to the realization that there is no such thing as "best practice". The closest we can come is "better practice", but even that comes down to personal preference.

These days I still spend a lot of time researching new ways of improving my process, especially in the area of automation. I try to move past the internal argument of whether it is better to use a DataSet or a strongly typed, custom collection, as quickly as possible. I am more concerned with which method will allow me to be the most productive, while allowing for the least amount of bugs.

Favorite Quote

"Whenever you read one of those books about programming methodologies written by a full time software development guru/consult, you can rest assured that they are talking about internal, corporate software development."


Sunday, January 08, 2006

 

Send To Menu Customization

I love TextPad, but it is a bit feature rich for what I use it for. Rather than spend $30 to view and edit .txt files, I decided to give Notepad2 a try. Someone on my team recommended it as an alternative to TextPad.

I honestly don't know a lot about Notepad2, but it works well for what I need. More than a couple of steps above the Notepad included with Windows XP, which doesn't even have a "go to line number" feature.

Picture of Send To context menu

Problem

After uninstalling TextPad, I noticed that the TextPad shortcut was still in the Send To context menu that comes up when you right click a file. I liked the fact that the Send To option was available for just about any file type I right clicked. I also noticed that Notepad2 did not ad itself to the Send To context menu.

Solution

After doing some searching on the web I found the solution at Life Hack. Apparently there is a Send To folder that you can access under C:\Documents and Settings\<userid>\SendTo\. You will need to have the "show hidden files and folder" checked, under Tools -> Folder Options -> View.


Thursday, January 05, 2006

 

Cheat Sheets for Dumb Programmers

If you are a dumb programmer like myself, you will find the following helpful.

Jeff Atwood over at Coding Horror has found a few excellent resources for cheat sheets. I don't use PHP and MySQL, but I printed up the CSS, Html Character Entities, JavaScript, and RGB Hex Colour Chart cheet sheets. All four are hanging on the wall behind me. I also made copies for each of my team members.

Enjoy.

Wednesday, January 04, 2006

 

MSBuild: Aspnet_merge.exe Exited With Code 1

Problem

After adding a Web Deployment Project to my Visual Studio 2005 Web Project and attempting to build, I ran into the following error message:

ASPNET_MERGE.EXE EXITED WITH CODE 1

Which makes the problem as clear crystal covered in raw eggs. Thankfully, Jason Lautzenheiser ran into the same issue and posted the solution.

Apparently aspnet_merge.exe does not like it when you have classes with the same name. In my case it was the fact that I had a Default_aspx page class in both my root directory and a sub-directory. This seems like it would be a fairly typical problem. I am surprised that aspnet_merge does not handle this automagically.

Solution

I made sure that each of my Default_aspx page classes were unique. In this case I changed the name of one page class from Default_aspx to MyDefault_aspx.

Tuesday, January 03, 2006

 

Introducing Tod1d.net

Welcome to the new URL! If everything is working correctly your address bar should begin with http://www.Tod1d.net/blog/.

That's right, I finally broke down and bought tod1d.net and Tod1d.com. Since I don't expect to be using this site for commercial purposes, unless you consider self-promotion and knowledge sharing commercial, I settled on To1d.net for the time being.

This is just a taste of what's coming this year. The new design for the site is around the corner.

 

The Article of the Week 8

Title

Getting Things Done When You're Only a Grunt

Author

Joel Spolsky

Description

Have you ever worked on a team where no one, including management, had ever read Peopleware or a single one of Joel Spolsky's essays?

This essay is for the individual within a team of programmers who would like to affect positive change with regards to improving processes and overall quality of work.

I like to think of this essay as a blueprint for "leading by example". "Leading by example" can be very powerful. Often, it occurs when you did not set out with the intention of doing so. It takes time to establish that the ideas you are setting before your team have value.

My own experience has shown that if I discover an idea that I believe could improve our current processes, I am better off trying to implement it within my own projects, before I share it with the group. If the idea has a positive impact within my project, it is an easy thing for me to share this new idea with my team during a code review. When you can back up a new idea with concrete results, it is much easier for that idea to gain traction among your peers.

Occasionally I struggle with time management issues while implementing new ideas. I have to be careful to stay focused on the priority of writing code and completing a project, and not become consumed with making the new idea work. I believe Joel’s solution is a good one. He dedicated the first seven hours of each day to the job that was expected of him (coding), and reserved the last hour of each day for improvements.

Favorite Quote

"A lot can be done to improve a project just by one person doing it."


Content copyright ©2003-2006 Tod Birdsall