Monday, October 13, 2008

Limiting Concurent Users in ASP.Net Sites

I've recently finished implementing a membership model enhancement for my current companies application. This model has a notion of an organization, which then has one or more user accounts. This allows users at a single organization to share data between their accounts.

With this enhancement, a business requirement came in to prevent multiple physical users from sharing a single user account. When I sat down to solve this problem, the first thing I did was fire up google and see what other people have done. All of the recommendations I found was focused on preventing a user from logging to the site and worked like this:
  1. User tries to login, site checks for some identifying piece of information from the client like a guid stored in a cookie.
  2. If there is no cookie then create one.
  3. The site then takes the cookie and looks up in a table (in memory or DB for example) and checks if this cookie is the same one for this user.
  4. If there is no entry in the table or the cookie is the same the user is able to login.
  5. If the cookie doesn't match the user is locked out.

This is an ok algorithm, but its main weakness is that it assumes that we know when a user logs off, which is impossible in an ASP.Net page. One idea and I forgot where I saw this was to use ASP.Net cache to store this entry, we can then set it to auto expire so that if a user gets locked out it would only be for a couple of minutes.

When I thought about this model, the biggest thing I didn't like is that it punishes the user, if for example his browser crashes. It would be even worse if it crashes because of my site and then I prevent him from logging in for five minutes.

I wanted to come up with a solution which works like many of the IM's out there. I wanted a last one in wins model. So how did I achieve this within Asp.Net?

  1. User logs in, and a new Guid is generated, and stored in a non-persistent cookie, and its stored in HttpContext.Current.Cache, using the user's login as a key.
  2. A client side timer is initialized in the main page of the application. This works nicely for us since the user never navigates away from this page currently; however, this same approach could be adapted to other navigation structures.
  3. This timer kicks of a call to a very simple Ajax enabled WCF service.
  4. The service checks the guid stored in the user's cookie.
  5. The services looks into cache for the guid, if no guid is found the value in the cookie is stored. This handles the case of the cache being flushed out.
  6. If the cookie doesn't match the cached item, then a false value is returned to the client.
  7. If the cookie matches the value in the cache a true value is returned.
  8. The client then navigates to an error page if the value is true, it also tracks any dialogs that were opened and navigates them as well.

This algorithm allows us to enforce the requirement to limit concurrent users, while still preventing frustration of a user being locked out because their browser crashed.

Enjoy

- Josh

ps. If I ever get around to building any diagrams I will update this.

Response.Redirect() & ASP.Net Pipeline

I've just finished debugging a fun a bug related my nHibernate code. Essentially I was getting an error that indicated that I was trying to associate a collection with more then one active nHibernate session.

This made no sense within my architechure. I am using a static session manager which essentially creates and caches nHibernate sessions on demand. It stores the session within HttpContext or within CallContext depending upon the environment its loaded in. I then implemented a very simple HttpModule which would flush and close the session after the request is done processing. I was relying upon the PostRequestHandlerExecute event to do this cleanup.

What I discovered is that in one of my pages which had a Response.Redirect() in it, this event is never actually being called. The solution is simple. Use the EndRequest event instead.

One concern I have within the context of my nHibernate architecure is the idea of doing work on the database after the page has finished executing for the user. What happens if an error occurs? I might have shown the user that everything was ok but in fact it wasn't. I'd be curious if anyone has any advice about this one?

Enjoy
-Josh

Wednesday, October 1, 2008

Preparing for IE8

My boss recently installed IE8 Beta 2 just to see what would happen. Of course he decided to go and navigate to our application which resulted in the usual phone call we all dread:

Boss: So Josh I just installed IE8.
Me: Huh huh, and how'd it go?
Boss: Our site looks like crap...

And I am sure you can imagine the rest of the conversation. Our site is optimized for IE7 standards mode, which isn't exactly compatible with IE8 standards mode.

There are a couple of ways we can resolve this. We can invest the time to bring our site up to IE8 standards mode compliance (and in the process hopefully add support for the other browsers which we don't support); however, this takes time which costs money which new startup companies don't always have the luxury of.

Another solution is for the site to lock in to a particular rendering engine. This is a brilliant addition to the architechure of IE, and I hope other browsers follow suit. Its the same concept of being able to lock in your client application to a particular version of .net.

I won't regurgitate how to do this. A really good article is available here.

Tuesday, September 23, 2008

Logging to the database with Enterprise Library

Update:

I have since started my own implementation which is describered in the following articles:

================================

So I've been using the Enterprise Library's logging block since v1; however, I've always logged to mostly unstructured or semi-structured locations such as email, files or the event log. I've had a lot of success using the block for logging exception messages or basic application events.

In all the apps I've ever written I've always had a need for a more robust strongly structured data store to facilitate reporting, and I've always resorted to rolling my own. Over the years the implementations have varried from very narrowly focused systems that support a single subsystem to more generic mechanism that support a various paradigm. For example logging of the messages within an SOA environment.

  • Incoming Message
  • Outgoing message to the next service
  • Reponse from that service
  • Response to the original request.

When ever its time to implement these systems, our team would always debate if we could leverage the Enterprise Library logging block; however, we've found that it was always easier to just roll our own quick dirty logger.

Today I had to ask this question again, only this time I am a team of one so I only had myself to argue with. I want to store an audit trail when a user logs into my web application, and collect all the fun stats such as his browser. (I know I could look at the iis logs but that won't let me profile which browsers have access to certain features, without having to figure out which urls if any compose a specific feature...this can be a whole other post so I will leave it at this).

The enterprise library does support a datbase trace listener out of the bat; however, it is seriously flawed. The listener can only log to a very specifc table structure. There is no way to say, I want to add this piece of information such as the IP Address of the client to the table as a seperate field.

The table does a text field called FormattedMessage which will use an Formatter that is asociated with the listener to generate a message. So while it is possible to get my IP Address in there, it forces to fall back onto to a non-relational model such as XML. While I love this flexibility, I've seen many DBA pulling their hair out at 3:00 am when they need to pull and aggregate data from a XML blob. Since this is a text field and not the Xml Data Type, I can't really enforce a schema.

So what is a poor developer to do? Well I don't have a solution implemented yet...But I plan on creating my own database trace listener that will let you specify the parameters and how to pull the values from the LogEntry object dynamically.

If I make any progress I'll add a new post. If anyone is aware of any solutions out there let me know.

Thanks,

Josh

Saturday, September 6, 2008

Setting up a printer on Windows Home Server

I recently won an HP Home Server (I actually got it about a month ago and decided to break it out of the box today). Its amazing how much life has changed for me in 10 years with a wife and three kids. 10 years ago that server would have been unpacked and up and running within an hour of getting it.

Anyways so far I am fairly happy with how easy it has been to setup the media server. I've just finished adding my Lexmark 5495 to the server, it took me longer to find the right drivers then it did to actually install them.

My only issue with the process is that you have to use remote desktop and login to the server. The help documentation for Home Server actually advises that you do not do this. Sharing resources like a printer seems like such a common task that it would have been nice for them to find a way to integrate into the Home Server Console.

To install a printer you can simply hook the printer into the USB or Parralel port, and then one of two things will happen. Either the server will detect the printer and install the printer or you will have to do this manually. You can check if the printer was installed by going to the shared folders and you should see the printer, if you do not then you will need to do it manually.

To do this open up remote desktop and connect to the server. Then you will install the drivers just as you would on any other computer.

Once the printer is installed you can connect to it by going to \\YourServerName\Printers You will also see the printer at \\YourServerName\NameOfYourPrinter. When you right click on the printer you can select connect and it should install the drivers for you.

Enjoy
Josh