•  Tuesday, November 18, 2008

ASP.NET Adjust HTML Size, UX and AJAX

Problem Introduction

You come up with a great HTML layout for data-driven ASP.NET web page which makes perfect sense from business perspective and reveals key indicators while making possible to drilldown into details if needed. This page utilizes DHTML and AJAX calls to improve User eXperience (UX) and overall page looks great until… your database is not filled up and page load time is increasing due to generated HTML size, DHTML is slow due to big DOM tree and users starting to complain about it.

Possible Solutions

At that point there are several approaches that can be employed to help:

  1. Rework page layout so only part of data is loaded;
  2. Download data to the client in XML or JSON format and generate necessary HTML code on client
  3. Load only those portions that need to be immediately displayed and load others on demand (e.g. like paging)

Let’s explore these options:

Rework layout

Here is no technical challenge here – just business one: you have to sell new layout to existing users/customers and once they liked the initial one this can be really difficult. If you can do that – go ahead. There is nothing wrong with that approach.

Client-side bindings

Instead of downloading 10 MB HTML code to client’s browser you can generate pure JSON and having a template of HTML just to fill data in the HTML template with simple (or not-so-simple) loop. Good news here is that such feature is coming to ASP.NET AJAX in v4.0. It is called client templates and implements the idea. Here are some resources:

Partially Loading Page

This is also not new concept – load only  those parts that should be visible immediately to the user. Traditional paging explores that option although it is not that fancy. If you’re using Google Reader you probably noticed that it loads only visible part of the feed and as you scroll down it keeps loading from the RSS feed.

So here is the idea: having small piece of HTML loaded would ease the browser in rendering HTML DOM tree and you gain performance. As user keeps using the page you keep adding HTML to existing DOM tree by loading it dynamically using AJAX.

Tricky part could be rendering ASP.NET User Control within Web Service so pure HTML can be returned by AJAX. Here are some useful tips:

Summary

Utilizing these techniques could improve download time for your web apps and also speed them up because the browser doesn’t need to process whole DOM before give the control to the user. All these can be used together or in any combination as long as it makes sense from business perspective.

Hope this helps!


  •  Monday, November 17, 2008

Playing Poker - Planning Poker

Imaging the following situation: your team is having a meeting in which you have to decide which features, components to build for next product or service release. Or by having a specification from system analyst you have to decide how long it would take to build the desired product so appropriate offer can be made to the client.

Either way it is very important what you will decide because all future changes will be around this very first decision. You are setting the expectations and this is very important for the project outcome.

One very popular technique is asking the developers for their estimation. Once the team leader has this number it multiplies x2 and gives it to PM. PM also multiplies it x2.5 so there will be good buffer zones. This approach, funny or not, somewhere works. And there are happy customers and ISV.

Yesterday I attended Seattle code camp and  a very interesting session was “Agile Estimation Techniques” presented by David Starr.

crispdeck (image by old.crisp.se)
The idea is simple: everyone from the team has a deck of cards. Having once already solved problem/built feature by the team is set as base one at well-known cost (in man-days, man-hours, ). Then another upcoming tasks is put on the table and after initial discussion everyone of the team lay the card of choice representing their thinking of the cost.
If there are big difference if some team members’ choice let them discuss their choice and repeat until everyone pick closer costs.

Look interesting, doesn’t it?! Read more here:

Managing Humans: Biting and Humorous Tales of a Software Engineering Manager

ManagingHumans  "What you're holding in your hands in by far the most brilliant book about managing software teams you're ever going to find. If you’re in a bookstore, buy it immediately, take it home, and read it right now. If you’ve found this book on a friend bookshelf, steal it immediately. You don’t have time to get to a bookstore, and you can always make new friends later."
Joel Spolsky, cofounder and CEO of Fog Creek Software

Fortunately I didn’t have to spoil any friendship and I was able to borrow the book from corporate library. With a series of tales Michael Lopp introduces the reader into the world of the tech manager. Whether manager or just wannabe this book describes very interesting situations and cases which can benefit you in so many ways. If you haven’t been in such situation yet you can mentally prepare for it and have an idea of good action in the back of your head when one occurs.

Ability to be in someone’s shoes is so rare and combined with good writing skills creates a wonderful must-have book.

This book is really so interesting so I am going to read it in just two days…


  •  Wednesday, November 12, 2008

VS2008 Web Setup Project and Win2008

I had interesting experience today. I tried to create a web setup project for one of my recent projects. As you know it is pretty straightforward: From Visual Studio 2008 File –> Add –> New Project –> Select Setup wizard.

image  

and just add output from existing project.
Then press Ctrl+Shift+B (Build ) and you’re ready… But not in Windows Server 2008. When I decided to test this newly made installation package I hit the ground with single dialog showing this message:

"The installer was interrupted before ApplicationName could be installed. You need to restart the installer to try again.

Click "Close" to exit."

 

Being experienced installer package developer I knew what I had to do: run the installer with verbose logging.

by executing this line:

   1: msiexec /i Installer.msi /lv detail.log

And I had nice 57 KB  file to read in wonderful notepad. And the problem action quickly appeared:

   1: Action start 16:22:53: WEBCA_SetTARGETSITE.
   2: MSI (c) (B4:4C) [16:22:53:082]: Note: 1: 2235 2:  3: ExtendedType 4: SELECT `Action`,`Type`,`Source`,`Target`, NULL, `ExtendedType` FROM `CustomAction` WHERE `Action` = 'WEBCA_SetTARGETSITE' 
   3: MSI (c) (B4:48) [16:22:53:082]: Invoking remote custom action. DLL: C:\Users\ADMINI~1\AppData\Local\Temp\MSIFB61.tmp, Entrypoint: SetTARGETSITE
   4: INFO   : [11/11/2008 16:22:53:097] [SetTARGETSITE                           ]: Custom Action is starting...
   5: INFO   : [11/11/2008 16:22:53:097] [SetTARGETSITE                           ]: CoInitializeEx - COM initialization Apartment Threaded...
   6: ERROR  : [11/11/2008 16:22:53:097] [SetTARGETSITE                           ]: FAILED:    -2147221164
   7: ERROR  : [11/11/2008 16:22:53:097] [SetTARGETSITE                           ]: Custom Action failed with code: '340'
   8: INFO   : [11/11/2008 16:22:53:097] [SetTARGETSITE                           ]: Custom Action completed with return code: '340'
   9: Action ended 16:22:53: WEBCA_SetTARGETSITE. Return value 3.
  10: MSI (c) (B4:4C) [16:22:53:097]: Doing action: FatalErrorForm
  11: Action start 16:22:53: FatalErrorForm.

Seeing WEBCA_SetTARGETSITE means that the installer was trying to set the destination. Having a web setup this means IIS was asked for “Default Web Site” and this call must be the one that fails. But why!? I have Web Server Role installed on the machine:

image

I and remembered from my MS DevDays 2008 IIS talk  – you still can use old (pre v7) IIS management tools with IIS7 as long as you have “IIS 6 Metabase Compatibility” role service installed.

image

I installed this role and … Voila!!! it works!!!


  •  Monday, October 27, 2008

.NET Has A New Logo

Our favorite and very wide spread development framework has new logo. It’s cool and it is already used on Microsoft PDC 2008 slide decks

newdotnetlogo_2

(via Scott Hanselman’s blog)


  •  Friday, October 24, 2008

.NET External Configuration & Build Process

It is very good practice to have several environments when creating a software solution - typical environments are Development -> Integration -> Staging -> Production environments. Also having Automated Continuous Integration server like CruiseControl.NET can greatly improve teamwork and quality of developed solution. Of course having a solution build (by CruiseControl.NET) you might want to have the installation project also build... and you will need a deployment procedure so the steps would look like these:

  1. Check-in source code changes
  2. Trigger a build on CI build server for the solution
  3. Trigger a build on CI build server for the build package
  4. Execute publish script which will deploy binaries to certain environment/servers and will change app settings, connection strings etc.

Example project setup can be seen on Omar Al Zabir's blog post ASP.NET website Continuous Integration+Deployment using CruiseControl.NET, Subversion, MSBuild and Robocopy.

 

Of course having all changes for different environments in publish script would make it big and difficult to maintain. This is why it is better to keep all environment/servers specific settings outside of the project. One option is machine.config. And storing it in source control, of course.

 

Another option is having all those settings in separate folder. I wasn’t aware of this option of .NET Configuration API and I was disappointed  when I found that XInclude is not supported. Fortunately there configSource section attribute which allows to achieve same functionality. For some ( more here ) this might be well known but I found this recently and AFAIK it is not widely used.

 

So you can specify web.config/app.config file like this:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <connectionStrings configSource="ConnectionStrings.config" ></connectionStrings>
   4:   <appSettings configSource="settings.config"></appSettings>
   5: </configuration>

and then specify actual configuration in external files. Here are my examples for ConnectionStrings.config

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <connectionStrings>
   3:   <add name="cs1" connectionString="Data Source=myServerAddress;Failover Partner=myMirrorServer;Initial Catalog=myDataBase;Integrated Security=True;"/>
   4: </connectionStrings>

and Settings.config

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <appSettings>
   3:   <add key="s1" value="Some very important setting"/>
   4: </appSettings>

 

As you can see from example the only important thing is to have root element of external file named same as referenced section in the core config file.

and after that you can simply get the values as usual:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         Console.WriteLine("cs1: {0}", ConfigurationManager.ConnectionStrings["cs1"].ConnectionString);
   6:         Console.WriteLine("s1: {0}", ConfigurationManager.AppSettings["s1"]);
   7:         Console.ReadLine();
   8:     }
   9: }

 

This means you can refactor configuration of existing .NET applications without having to touch the code or even recompile. Just be careful :)

Happy XML/Config refactoring :) !


  •  Wednesday, October 15, 2008

MVP or Blue badge

Which one is better?! No doubt both are very excellent in the world of software development with Microsoft tools. So I feel very lucky because last month I touched both in one or another way:

  • In the beginning of September I got a request for more detailed contact information regarding a upcoming nomination for MVP Award. Although very pleased I couldn't accept such nomination as I already had accepted an offer from Microsoft for fulltime employment.
  • In the beginning of October 2008 I've started working for Microsoft as fulltime employee(or as a blue badge in local slang :) ). I am working as SDE in AdCenter and I will be creating solutions with favorite .NET Framework. No need to say that I am very excited and impatient to get into deep details in organization processes inside the biggest and most successful software company.
    I also relocated from Bulgaria to Seattle with my wife and we had the longest journey so far. The international relocation package is excellent and several teams work with us to facilitate the relocation and to make it as painless as possible. The friends here helped us a lot for getting acknowledged with the area and the lifestyle.
What does this mean for the blog?!

Well... I will keep it for sure!!! Microsoft supports such initiatives but definitely you won't read news here prior officially announced :) so Scott Guthrie will remain your preferred source of fresh news regarding development tools. There were some suggestions to move the blog on blogs.msdn.com but I think I will keep it on http://www.galcho.com/blog/.

 

The photo album is also updated with various photos from our area exploration tours in Seattle/Redmond/Bellevue .


  •  Saturday, September 13, 2008

Virtual Machine's Network Adapter Hangs

I recently moved Galcho.com (and this blog) on a new Virtual Machine kindly provided by my friend Nanio Nanev and his system administration company PrimaNet Consult LTD.

The VM has Win2003 Web edition SP2 and it is very fast ( as it is hosted on monster hosting server ) but there is one nasty issue we are fighting with: The network adapter that is connected to WAN - external network and has real static IP address - hangs once in a while.

How is possible Intel 21140-Based PCI Fast Ethernet Adapter (Generic) Network adapter on Virtual machine to hangs?!?!

I was able to connect using internal network adapter and after disable and re-enable WAN it was fine for another 3-4 hours.

I've found a way to this by script - by using DevCon - command-line utility functions as an alternative to Device Manager (direct download link).

Using it this simple script does the job:

C:\Install\devcon disable PCI\VEN_1011&DEV_0009&SUBSYS_21140A00&REV_20\3&267A616A&0&50
C:\Install\devcon enable PCI\VEN_1011&DEV_0009&SUBSYS_21140A00&REV_20\3&267A616A&0&50


Note that device class can differ so the question "How did you get these?" comes naturally. Here is how you can list all devices from setup class:

c:\install\devcon listclass net


And here is the result in my case:

image

So doing this reset on certain period helps now but this is not the smartest solution. Does anyone have another idea?

Maintain Database Indexes

It is wide known that creating index on table column can speed up queries that has this column in it's Where clause. Table indexes are binary trees in most cases and they are stored in pages similar to stored data itself. Over time data changes which cause index changes and it require some sort of maintenance to keep database optimized and running as fast as possible.

Over time these modifications can cause the information in the index to become scattered in the database (fragmented). Fragmentation exists when indexes have pages in which the logical ordering, based on the key value, does not match the physical ordering inside the data file. Heavily fragmented indexes can degrade query performance and cause your application to respond slowly.*

This can be fixed by either rebuilding index (by dropping existing and create new one) or reorganize it (or defrag it).

Rebuild indexes

Rebuilding indexes can be done by either one of these

I won't cover the details as you can look them up on MSDN. Just my favorite way is like following:

ALTER INDEX ALL ON Person.Address REBUILD WITH (ONLINE=ON, FILLFACTOR = 80, SORT_IN_TEMPDB = ON, STATISTICS_NORECOMPUTE = OFF);

 

The advantage is this operation is online - meaning you can query table during index rebuild.

Reorganizing indexes

Reorganizing an index defragments the leaf level of clustered and nonclustered indexes on tables and views by physically reordering the leaf-level pages to match the logical order (left to right) of the leaf nodes. Having the pages in order improves index-scanning performance. The index is reorganized within the existing pages allocated to it; no new pages are allocated. If an index spans more than one file, the files are reorganized one at a time. Pages do not migrate between files.

Reorganizing also compacts the index pages. Any empty pages created by this compaction are removed providing additional available disk space.*

 

There are two ways to perform index reorganization:

Again my preferable is this:

ALTER INDEX ALL ON Person.Address REORGANIZE;


This is also online operation.

Note: Although both operation stated above should be online I've applied it on big tables (above 140M records on ~60 GB in two tables) and of course it was pretty I/O intensive which caused some delays in performed queries. Having in mind that default CommandTimeout in .NET Class Library is 30 seconds and application writing at least once per minute creates very challenging DB to maintain. Possible solution would be using MS SQL Server 2008 Resource Governor. Unfortunately the server was MS SQL 2005...

How to detect fragmentation

In order to apply techniques described above fragmentation should be detected. For this comes a new DMV (Dynamic Management View) sys.dm_db_index_physical_stats - that gives us fragmentation in percent (avg_fragmentation_in_percent).  

These are the recommendations depending on returned value in avg_fragmentation_in_percent column:

avg_fragmentation_in_percent value

Corrective statement

> 5% and < = 30%

ALTER INDEX REORGANIZE

> 30%

ALTER INDEX REBUILD WITH (ONLINE = ON)*

 

Using sys.dm_db_index_physical_stats could not be very useful when used by itself so I prefer using it together with system tables sys.tables and sys.indexes:

---=== get index fragmentation
SELECT a.index_id, t.name as TableName, i.name as IndexName, avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(NULL,NULL,NULL,NULL,NULL) AS a
INNER JOIN sys.indexes AS i ON a.object_id = i.object_id AND a.index_id = i.index_id
join  sys.tables t on t.object_id=i.object_id
ORDER BY avg_fragmentation_in_percent DESC

 

The result is like this (executed in AdventureWorks):

image  

 sys.dm_db_index_physical_stats can take time to execute so if you want to view all indexes with the table name this can be used:

select t.object_id, t.name as TableName, i.name as IndexName, i.type_desc as IndexType  
from sys.indexes i 
join  sys.tables t on t.object_id=i.object_id
where i.object_id >1000 
order by t.create_date asc


Which return following result:

image

And it can be used to generate detailed T-SQL queries for reorganizing indexes one at time:

select  'ALTER INDEX ' + i.name + ' ON ' + t.name + ' REORGANIZE;'
from sys.indexes i 
join  sys.tables t on t.object_id=i.object_id
where i.object_id >100 
order by t.create_date desc


producing

image

Summary

So far we took a look at following

  • Detect index fragmentation
  • Rebuild indexes
  • Reorganize indexes
  • Use T-SQL to generate T-SQL to maintain indexes.

I hope this helps.

* quoted from MSDN article Reorganizing and Rebuilding Indexes.


  •  Friday, September 12, 2008

Using Microsoft ADO.NET Data Services

Mike Flasko (ADO.NET Data Services, Program Manager) at Microsoft Corp. wrote an extensive article about recently released ADO.NET Data Services called Using Microsoft ADO.NET Data Services. It is full of examples and it is exactly the type of articles developers prefer to read :) - although slightly long.

The examples included are:

  • Example 1: Basic data service in C#
  • Example 2: ADO.NET Data Service exposing an in-memory data source
  • Example 3: Response for the root of a data service
  • Example 4: Listing of the contents of an entity set, in Atom/APP format
  • Example 5: Response for a single-entity URL
  • Example 6: A single-entity response from the data service
  • Example 7: A response that contains multiple entities
  • Example 8: Response with nested related entities using the "expand" option
  • Example 9: Atom service document as returned from an ADO.NET Data Service
  • Example 10: JSON response from a data service for a single 'Customer' entity
  • Example 11: A hierarchical result containing a Customer and its related Sales Orders, in JSON format
  • Example 12: Payloads for creating a new Category entity using Atom and JSON
  • Example 13: Response from the data service after processing a POST request for creating a Category, in Atom and JSON formats
  • Example 14: Payload used to modify an existing Category entity through an HTTP PUT request, Atom and JSON formats
  • Example 15: Payload used to modify an existing Category entity through an HTTP PUT request, Atom and JSON formats
  • Example 16: Payload to create a new Territory entity that includes an association to a Region entity
  • Example 17: Payload to update a Territory so it is associated with a different Region entity
  • Example 18: Keys-only payload format used for inserting an association
  • Example 19: Inserting a graph of data in a single request
  • Example 20: Request and Response using CategoryName as a concurrency token
  • Example 21: Request to update the name of a Category
  • Example 22: A data service operation to retrieve filtered customers
  • Example 23: Setting Visibility of Service Operations
  • Example 24: Access an Astoria data service from a .NET application using the client library
  • Example 25: Retrieving all customers in the city of London, ordered by company name
  • Example 26: Delay-loading related entities using the Load() method
  • Example 27: Using "expand" to eagerly-load related entities
  • Example 28: Inserting a new entity instance using the client library
  • Example 29: Updating an existing entity using the client library
  • Example 30: Creating a product entity and associate it with a Category
  • Example 31: Sending queries as a batch request
  • Example 32: Using the asynchronous API in the client library
  • Example 33: Setting service-wide access policy
  • Example 34: Query interceptor method implementing a custom, per request access policy
  • Example 35: Update interceptor method that validates input Category entities before being persisted in the underlying store
  • Example 36: Assume a validation error occurred while processing a request which caused an ArgumentException to be thrown invoking the exception handler shown in the ‘service code’ section.

Pretty long list, isn't it?!

For more details and code samples read article Using Microsoft ADO.NET Data Services on MSDN.