David Savage is a .NET Software Engineer in Rochester, New York.
Don't wanna be here? Send us removal request.
Text
Simplicity is Golden
Steve Krug said it ...
Don't make me think!
What do the following companies have in common?
Apple
Microsoft
Amazon
Facebook
Google
Well, besides the fact that they make a lot of money; they followed this rule! Apple made it easy to use a phone. Microsoft made it easy to use a computer. Amazon made it easy to buy books. Facebook made it easy to connect with friends. Google made it easy to search.
They weren't the first. They were, at times, mocked for their approach. Competitors boasted - More Features! - Prettier Pictures!
They made it simple to use their product. You could open Google and spend more time figuring out what you were trying to search for than how to search for it.
Think about games you have played. Were they easy to learn? Were they hard to master? I'd say for every game I ever got "addicted" to, the answer was yes in both cases.
But it goes bad...
Never forget where you come from...
Success has a habit of going to our heads. We build something good, and then we want to make it better. It is at this point that we most often forget about keeping it simple.
Make it easy to use; but capable of much more.
(this is as-of 2/3/2012)
Take a look at Google.com in Chrome. Now take a look at it in IE.
This is the classic example of forgetting what makes your product so great.
Simplicity
The old way: Menu links shown across the top of the page.
The "enhancement": An image you have to hover over to view a menu which you can navigate from.
I don't have to explain how it takes more steps to get to "News Search". But this is to the core of what making your site less-usable through enhancement is all about.
Lets make it better by enhancing the navigation
I'm still waiting for us to come up with an enhancement to the hyperlink.
11 notes
·
View notes
Text
A Guide to Sexy Exceptions in .NET
System.NullReferenceException: Object reference not set to an instance of an object.
Whether you are a rookie developer or a weathered code monkey, you have seen this exception at some point in your work. Chances are, if you are the latter of the two, you have dealt with tracking the issue down and dealing with it. My goal, is to make that better.
The pain is that this exception does not tell you more than "hey guy, something was null" - if your lucky and running code with debugging symbols, you get a line number. But it is up to you figure out what is null, why it is null, and how to fix it.
Beyond the immediate remedy to your bug, it should become immediately apparent that you have an exception design issue.
An exception design issue is when an exception thrown to a user does not provide immediate actionable information without further investigation.
Here is What I Think
As hinted to above, my opinion of a good error is one that tells you:
What happened.
Why it happened.
How (if) it can be fixed.
Take a simple form where you fill out some info. You wouldn't (I hope) show the user a message like:
Form is not complete
We can do better than that:
The value you provided for Field X is invalid and must conform to this format.
Why should exceptions be treated any differently?
Consumers of your application could be users. They could be developers. They could be hackers from Mars. Just because the users are more savvy, does not mean they can telepathically debug your application.
Your goal should be to provide the best experience to all of your users, especially when your application fails.
How About an Example
One of the best examples I like to use is around using appSetting values from a configuration file. Assume the following:
We are looking for an appSetting value in our config file called "ApplicationName"
We expect the value to be a string, and not empty/null
We do not have any error trapping if the value is not there.
The config file does not contain the key "ApplicationName"
So, given this, if we fire up our application we should get an exception. Most likely, we get a NullReferenceException.
Stop For a Moment
Remember who your users could be and put yourself in their shoes. Without the source code, do they have any idea what really is happening and what to do to fix it? - NO!
The most they can possibly know is that somewhere, someone forgot to put a check for null values in place.
Lets Make This Better
If our code looks like this...
string appName = ConfigurationManager.AppSettings["ApplicationName"].ToString()
We know that the .ToString() call is what is throwing our exception. For sake of the demonstration, we will leave this line as-is.
try { string appName = ConfigurationManager.AppSettings["ApplicationName"].ToString() }
catch(Exception) { throw new Exception("There was an error obtaining the ApplicationName from configuration."); }
Now What Do We Know?
We know WHAT has happened. We don't know WHY or HOW we can fix it yet. So, we still need to make this better.
WHY should tell the user the reason for the situation.
Lets tweak it...
...
catch(Exception) { throw; }
catch(NullReferenceException)
{
throw new ConfigurationErrorsException("There was an error reading ApplicationName config value; the setting was not found.");
}
...
As you can see, I have done a couple things here to clarify the error that is thrown.
I have singled out the situation where a NullReferenceException is thrown. Ignore the low-hanging fruit here, this is just for sake of the example.
I have changed the exception type to a ConfigurationErrorsException. If this code is referenced elsewhere, we have a way to distinguish this exception from others.
Completing the Picture
So, we have the WHAT and the WHY taken care of, now we have to tackle the HOW. This can be the hardest part of the puzzle.
HOW should tell the user what they can do to fix the problem.
The hard part about this is not overdoing it. You don't need 3 paragraphs of possible solutions. When more than one solution could exist, give the user the most likely resolution as a possible fix.
Lets tweak it...
...
catch(Exception ex)
{
throw new ConfigurationErrorsException(String.Format("An unexpected error occurred when trying to read the ApplicationName setting from configuration. {0}", ex.Message), ex);
}
catch(NullReferenceException)
{
throw new ConfigurationErrorsException("There was an error reading ApplicationName config value; the setting was not found. To resolve this, add an appSetting to your configuration with valid string value. This value cannot be empty.");
}
Now we are looking much better. There are two keys here...
We expanded the first catch to let the user know that something happened that we were not expecting. We explain what was being done and what the unexpected event was. At this point, it is all the information we have.
We expanded on our second exception and explained that the user can resolve the issue by adding the missing setting.
This gives us a WHAT, WHY, and HOW.
Summing It Up
Good exception design is something that can go a long ways towards improving the user experience with your application. I realize there is quite a bit of low-hanging fruit in this post but I ask that you look past it and understand the concept I am trying to convey.
Further Reading
If you are more concerned with general practices with throwing exceptions, check out a few of these resources.
Design Guidelines for Exceptions
Exception Handling Best Practices
Throwing better SOAP exceptions
I will be following up to this at a later point with some other general practices I like to follow.
31 notes
·
View notes
Quote
It's a dirty seceret that much of what we admire in the design world is a byproduct not of 'strategy' but of common sense, taste, and luck. Some clients are too unnerved by ambiguity to accept this and create gargantuan superstructures of bullshit to provide a sense of security.
Michael Bierut
0 notes
Text
IE9 - Good, Bad, Ugly
IE9 has been out for about a week now. I wanted to jot down some of the things I like/dislike about it.
The Good
Performance
The interface
Tabs scale to to the space available
No surprises, despite the rendering/display issues
The Bad
Having to click + hold the back button to see a list of previous sites. How am I supposed to know to do that as a user?
When you configure it to open new windows in tabs, you expect it to re-use the existing window when an outside program requests a page; IE seems to like to open lots of new windows, despite this.
The Ugly
When you configure it to open new windows in tabs, pop-up blocker will not work. Case-in-point: go to accuweather.com and check weather for your area. In many cases this should cause a "Netflix" popup. Without the tab configuration, it is blocked by the popup blocked.
Lots of rendering issues; the cycle continues.
5 notes
·
View notes
Text
Convert FormsIdentity To ClientFormsIdentity
Background If you have been using Client Application Settings (CAS), like me, you are well aware that it is a great framework. But like anything else, it has a few limitations. In this case we are referring to an issue that you may encounter when working in a Single Sign-On (SSO) environment.
The issue materializes when you have an authenticated user that requests a page on your site where your Profile information is provided via CAS. The reason this is an issue is that ASP.NET, when using Forms authentication, will populate the HttpContext.Current.User.Identity property with a FormsIdentity. However, with CAS you are going to need a ClientFormsIdentity. You are going to need to craft some magic to get that FormsIdentity into a ClientFormsIdentity and attached to Thread.CurrentPrincipal.
Scenario So, lets set some assumptions here for what we are working with:
You have written an ASP.NET web service which uses Forms authentication
Your authentication is provided by an SSO service, which also has CAS enabled
An authenticated client calls your service.
Our task is to get the User's Profile (settings), which are provided by CAS, to be populated.
The Issue CAS Settings (Profile) properties are associated with the current Thread. The problem with this is that ASP.NET does not impersonate the logged in user. This is also an older feature of .NET which will require your Application Pool in IIS to be configured to use the Classic pipeline - which we are not going to do.
For some applications this may not even be viable, such as WCF DataServices which requires the integrated pipeline. So, what we need to do is take our authenticated FormsIdentity, turn it into a ClientFormsIdentity and attach it to Thread.CurrentPrincipal.
An additional note is that the CAS settings will not load unless the current Thread's principal derrives from the ClientFormsIdentity, which is the identity type set when you use CAS's Membership.ValidateUser. This method sets the Thread.CurrentPrincipal property.
The Solution To solve this, we will need to take our HttpContext.Current.User.Identity and snag the authentication cookies from it.
var identity = HttpContext.Current.User.Identity as FormsIdentity; CookieContainer container = new CookieContainer(); foreach (String key��in HttpContext.Current.Request.Cookies) { var cookie = HttpContext.Current.Request.Cookies[key]; container.Add(new Cookie(cookie.Name, cookie.Value, cookie.Path, String.Empty)); }
This simply creates a new CookieContainer which we can use with CAS. After we have this, it is as easy as simply creating our new identity and silently re-authenticating it.
ClientFormsIdentity ident = new ClientFormsIdentity(string.Empty, string.Empty, Membership.Provider, "ClientForms", true, container);
Thread.CurrentPrincipal = new ClientRolePrincipal(ident); ident.RevalidateUser();
The beauty with CAS is that even though we do not have the username or password, it will use the authentication cookies that were given to us to re-validate them. After the Thread.CurrentPrincipal value is set, we will be able to access the Properties from our CAS settings provider.
2 notes
·
View notes
Text
Resolving NullReferenceException with ClientApplicationServices / ProfileService
If you have been (or trying to) work with Client Application Services, you may have chased this issue like I did this morning.
The Setup You are working with Client Application Services, specifically the Profile Service. You have either added a new profile property or you are just trying to get synched up for the first time. You select Load Web Settings, you enter a valid username/password, it waits for a second and then you get this....
The Solution The real culprit, in my opinnion, is a bug in the profile service which should at least fail a bit more gracefully. You can resolve the error by updating (or adding) the following configuration section to your web.config ...
System.Web.Extensions/Scripting/webServices/profileService
Upon further inspection...
By default, no profile properties are available. To make a profile property available to a client application, add the property name to the readAccessProperties attribute of the profileService element. To enable a client application to update a profile property value, add the property name to the writeAccessProperties attribute.
So, armed with knowledge we can now add a coma delimited list of properties in the readAccessProperties attribute of the profileService configuration entry.
<profileService enabled="true" readAccessProperties="FirstName,LastName,EmailAddress" />
2 notes
·
View notes
Text
Migrated to Tumblr tonight
Greatly impressed with how easy it was to migrate to tumblr this evening.
0 notes