I Need A Dip

Friday, April 07, 2006

JavaScript StringBuilder

The big web project that I have been working on lately utilizes a ton AJAX. Specificaly AjaxPro.NET.

As a result I have been doing a ton more JavaScript developement and client side rendering of tables and what not (string concatenation).
I noticed that creating HTML on the client wasn't as fast as I would like when constructing large tables to be displayed so I did a couple of tests.

First I just looped through 5,000 times creating a very large string like:
  html +
'Lorem ipsum dolor sit amet, consectetuer bla bla <br />';
= document.getElementById("results");
s.innerHTML html;
This took about 2.8 seconds in IE. (on average)

Next I created the following string builder class:
function StringBuilder(value){
this.strings = new Array("");
= function (value){
if (value)this.strings.push(value);
= function (){this.strings.length 1;}
= function (){return this.strings.join("");}
I ran the test again using my new class like:
var html = new StringBuilder("");
'Lorem ipsum dolor sit amet, consectetuer bla bla <br />');
= document.getElementById("results");
s.innerHTML html.toString();
The average results using the string builder was about 62 milliseconds.
While the results weren't as drastic in FireFox, the StringBuilder class was 2 to 3 times faster.

Give it a try...

Tuesday, January 31, 2006

Sorting Generics with IComparer<T>

I'm a huge fan of Generics.

I've replaced all of my code generated custom colletion classes with List<T>. But along with my simple implementation of CollectionBase I built in some custom sorting for my objects.

I know the correct way would be to implement IComparer with all my objects and sort that way, but the truth is I very rarely sort anything (only when displaying data t the user). I like just running my CodeSmith template against all 25 of my tables to generate some nice objects that take care of all the C.R.U.D. functions, along with the stored procs to make them work.

Meanwhile, back at the ranch, I need to sort my List<T>'s by a particular property value. Here was my solution:
   1:  public enum GenericComparerSortDirection { Asc, Desc } 
   3:  public class GenericComparer<T> : IComparer<T> 
   4:  {    
   5:      private string propertyName; 
   6:      private GenericComparerSortDirection theDirection; 
   8:      public GenericComparer(string propertyName, GenericComparerSortDirection eSortDirection) 
   9:      { 
  10:          this.propertyName = propertyName; 
  11:          this.theDirection = eSortDirection; 
  12:      } 
  14:      public int Compare(T x, T y) 
  15:      { 
  16:          // gets the value of the x property 
  17:          PropertyInfo property = x.GetType().GetProperty(propertyName); 
  18:          object valueOfX = property.GetValue(x, null); 
  20:          // gets the value of the y property 
  21:          property = y.GetType().GetProperty(propertyName); 
  22:          object valueOfY = property.GetValue(y, null); 
  24:          // now make the comparsion 
  25:          if (this.theDirection == GenericComparerSortDirection.Asc) 
  26:              return ((IComparable)valueOfX).CompareTo(valueOfY); 
  27:          else 
  28:              return ((IComparable)valueOfY).CompareTo(valueOfX); 
  29:      }
  30:  }

Now you can sort a generic list of custom objects like this:
   1:  List<MyClass> lstSorted = getABunchOfObjects(); 
   2:  lstSorted.Sort(new GenericComparer<MyClass>("PropertyName", GenericComparerSortDirection.Asc));

I understand that this isn't the most efficient way of sorting (using reflection) but like I said, I don't use this method often, and it works perfectly for my situation.

NOTE: Notice line 17 and 21 use GetType() rather than typeof(T) in case the properties aren't provided by T itself, but by a class derived from T.
Thanks to a suggestion from Jon Skeet

This implementation was inspired by my problem and the following post: http://www.dotnetjunkies.com/Tutorial/5091F698-EF8B-436B-A345-AFBEF18CE229.dcik

Sunday, January 29, 2006

Singleton pattern on the web

I would love some feed back so please don't hold back.

I have a webservice that is responsible for formatting tons of information that is then queried by other applications (agents) that utilize it (the info packets).

All this "formatting" and logic I am talking about needs to be done in a centralized place and accessed by the agents. That is why we chose to go with a web service. This formatting and logic also needs to be done and held in memory so the agents don't have to wait for it. So we decided to implement a sort of singleton pattern. What do you guys think?

   1:  public class PacketManager 
   2:  { 
   3:      private static object lockObject = new Object(); 
   4:      private const string CACHE_MANAGER = "Mngr:PacketManager"; 
   5:      private List<InfoPacket> _preparedPackets; 
   7:      private PacketManager() { initFunction(); } 
   9:      public static InfoPacket GetInfoPacket() { return Instance.getInfoPacket(); } 
  11:      public InfoPacket getInfoPacket() { ......some code ..... return this._preparedPackets[0]; } 
  13:      private static PacketManager Instance 
  14:      { 
  15:          get {
  16:              lock(lockObject){
  18:                  PacketManager oManager;
  20:                  object o = System.Web.HttpContext.Current.Application.Get(CACHE_MANAGER);
  21:                  if(o==null){
  22:                      oManager = new PacketManager();
  23:                      System.Web.HttpContext.Current.Application.Add(CACHE_MANAGER,oManager);
  24:                  } else oManager = (PacketManager)o;
  26:                  return oManager;                
  27:              }
  29:          }
  30:      }
  31:  } 

Here is more information on the Singleton Pattern: http://www.yoda.arachsys.com/csharp/singleton.html

Friday, January 27, 2006

sp_xml_preparedocument - Date problem

I'm no word-smith, so here was my problem:

My dataset looked like the following when it's serialized.
   1:  <NewDataSet>
   2:    <xs:schema id="NewDataSet" xmlns=""
   3:  xmlns:xs="http://www.w3.org/2001/XMLSchema"
   4:  xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
   5:      <xs:element name="NewDataSet" msdata:IsDataSet="true"
   6:  msdata:UseCurrentLocale="true">
   7:        <xs:complexType>
   8:          <xs:choice minOccurs="0" maxOccurs="unbounded">
   9:            <xs:element name="List_101">
  10:              <xs:complexType>
  11:                <xs:attribute name="InvoiceID" type="xs:int" />
  12:                <xs:attribute name="Email" type="xs:string" />
  13:                <xs:attribute name="DomainID" type="xs:int" />
  14:                <xs:attribute name="SourceID" type="xs:int" />
  15:                <xs:attribute name="TypeEnum" type="xs:int" />
  16:                <xs:attribute name="ImportID" type="xs:int" />
  17:                <xs:attribute name="DateStamp" type="xs:dateTime" />
  18:              </xs:complexType>
  19:            </xs:element>
  20:          </xs:choice>
  21:        </xs:complexType>
  22:      </xs:element>
  23:    </xs:schema>
  24:    <List_101 InvoiceID="55" Email="joeshmoe@joe.com" DomainID="2421"
  25:  SourceID="12" TypeEnum="2" ImportID="20"
  26:  DateStamp="2005-09-18T17:39:00-07:00" />
  27:  </NewDataSet>

My stored procedure looked like the following:
   1:  CREATE Proc xml_List_101
   2:  @listdata nText
   3:  AS
   4:  Declare @hDoc int
   5:  exec sp_xml_preparedocument @hDoc OUTPUT, @listdata
   6:  Insert Into List_101
   7:  Select 
   8:   [InvoiceID], [Email], [DomainID], [SourceID], [TypeEnum], [ImportID], [DateStamp]
   9:  From 
  10:  OPENXML(@hDoc, '/NewDataSet/List_101')
  11:  WITH (
  12:  [InvoiceID] Integer,[Email] varchar(150),[DomainID] Integer,[SourceID] Integer,[TypeEnum] Integer,[ImportID] Integer,[DateStamp] DateTime)
  13:  Exec sp_xml_removedocument @hDoc

First off, my dataset was a lot larger then the one I am showing you. There were many more "rows".

I was trying to insert the XML data into a table called List_101, and that table has a 'DateStamp' column that is a datetime type. I kept getting the "Syntax error converting datetime from character string." because of the DateTime format from DataSet.WriteXML(). (because of the -07:00 offset)

So I ended up using the CAST(left(DateStamp,10) AS datetime) so it was in the correct format and changed the WITH to DateStamp varchar(10). The sp_xml_preparedocument doesn't blow up anymore and the data gets successfuly inserted into List_101 with the proper DateStamp (which is a datetime in the table) because it converts the varchar(10) on its own.

Final working stored proc:
   1:  CREATE Proc xml_List_101
   2:  @listdata nText
   3:  AS
   5:  Declare @hDoc int
   6:  exec sp_xml_preparedocument @hDoc OUTPUT, @listdata
   8:  Insert Into List_101
   9:  Select 
  10:   [EmailID], [Email], [DomainID], [SourceID], [TypeEnum], [ImportID], CAST(LEFT(DateStamp],10) AS datetime)From 
  11:  OPENXML(@hDoc, '/NewDataSet/List_101')
  12:  WITH (
  13:  [EmailID] Integer,[Email] varchar(150),[DomainID] Integer,[SourceID] Integer,[TypeEnum] Integer,[ImportID] Integer,[DateStamp] varchar(10)) 
  14:  Exec sp_xml_removedocument @hDoc

Hopefully this helps the next person.