tag:blogger.com,1999:blog-124979812023-10-19T23:28:48.316+08:00Geeks don't wear tieAbout AJAX, Javascript, architecture, usability, configuration management, productivity improvement, and anything else that comes to my mindEnrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.comBlogger29125tag:blogger.com,1999:blog-12497981.post-65757569683418667202006-11-13T21:41:00.000+08:002006-11-13T23:12:38.253+08:00Passed 70-431This Saturday, I passed my first exam in the new certification series: TS: Microsoft SQL Server 2005 - Implementation and Maintenance (70-431).<br /><br />This is so far the hardest exam I took. Don't get me wrong. I have taken 5 Microsoft exams before and one of them is exam 70-229 using SQL Server 2000. However, since my background is more as an application developer rather than a database administrator, I found the exam very challenging. Unfortunate for me, it just cover a little bit of TSQL programming, the material that I am familar most. Most of the exam material focuses on the administrative/maintenance tasks such as backup/restore, indexing, and various high availability technologies.<br /><br />Lucky for me, I still managed to score 947 out of 1000.<br /><br />Here are few useful tips for the exam takers:<br /><br /><ul><li>If you're unfamiliar with the administrative/maintenance stuff, get a good book that explain the concepts rather than just giving you facts. By learning this way, you don't need squeeze your brain to understand all the new terminology. I highly recommend the MCTS Self-Paced Training Kit (the 'blue' book). Find out about the book <a href="http://www.amazon.com/o/ASIN/073562271X/ref=s9_asin_image_3/102-4729266-0912913">here</a>.<br />I also read the Exam Cram (the 'red' book), but it is not that helpful for me.<br /></li><li>Do some practice. Follow the practice/lab from the book (if any). I find that doing practice help me to grasp the concept quicker. I prefer to see things get executed than just believe what the author write in the book. Not convinced yet? Go to the next point<br /></li><li>Expect to see simulation questions... and a lot of them. So you really need to practice.<br /></li><li>Download the latest SQL Server Book Online and read additional material not covered in the book, but don't spend too much time studying this manual. Admit that you don't have time and energy to study everything. It's OK to miss one or two difficult questions, but don't miss the easy ones because you don't have time to study that chapter.<br /></li><li>Still related to the previous point... Don't try to remember all the switches, parameters, options, etc. from a SQL syntax. They are just too many... Only remember the important ones. How to identify the important ones? Read the book. If a very detail question appears on your exam and you really have no idea, use your intuitive or skip the question for later review. You may find the clue in the other questions.<br /></li><li>As always, Microsoft is proud of the new technologies/features, so put emphasizes on the new features, such as XML data type, database mirroring, and some enhancements in the TSQL syntax.<br /></li><li>Write on a piece of paper (OK you can use Notepad) all the important dynamic management views and functions. Read the note later when you are about to enter the exam room.</li></ul><p> </p><p>Hope this helps.<br /></p>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-89314817859426088632006-10-17T22:07:00.000+08:002006-10-17T22:40:41.741+08:00Scripting SQL Server database with ScriptTableData.cstRecently, I worked on SQL scripts for database deployment. I use VS.NET 2003's Database Project to maintain the Create Script and Change Script in the SourceSafe. Those scripts will be run automatically by the build tool (NAnt) to create the database from scratch at any time and update the existing database as required.<br /><br />The database project in VS.NET is an excellent tool to script database objects like tables, views, UDFs and stored procedures. You just need to drag and drop those objects from the Server pane to the Solution pane and automatically all the required SQL scripts will be generated and added to the solution. However, it fells short when I use the database project to script the data. The data is stored as binary file, so I lose control of it.<br /><br />Surprisingly, Codesmith 2.6 (the free edition) comes with a template called <strong>ScriptTableData.cst</strong> that will do exactly what I want: to script the data as SQL statements. It produces INSERT INTO... SELECT statements that can be run on the target database to populate the data.<br /><br />Unfortunately, the ScriptTableData template contains some annoying bugs. For example, the bit data type is scripted as "true" or "false" instead of "1" or "0", causing errors when you execute the produced SQL script. Moreover, it does not handle datetime and binary datatype very well.<br /><br />I decided to modify the script to help my work. The following is the modified version of ScriptTableData.cst. All good credit goes to the maker of this excellent code generator, Eric J. Smith. I only fix some bugs and add few more features.<br /><br />Bugs fixed:<br /><br /><ul><li>Boolean data type is handled properly</li><li>SET IDENTITY_INSERT appears only for the table with identity column</li></ul><p>Improvement:</p><ul><li>More precise representation for DateTime data type</li><li>Support binary data type</li><li>Support two style of scripting: Compact and Verbose. The verbose style will produce SQL script similar to the commercial tools.</li></ul><p>The template:</p><pre><br /><code><span style="FONT: 10pt Courier New"><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">CodeTemplate</span><span class="html1-space"> </span><span class="html1-identifier">Language=</span><span class="html1-value">"C#"</span><span class="html1-space"> </span><span class="html1-identifier">Debug=</span><span class="html1-value">"True"</span><span class="html1-space"> </span><span class="html1-identifier">TargetLanguage=</span><span class="html1-value">"T-SQL"</span><span class="html1-space"> </span><span class="html1-identifier">Description=</span><span class="html1-value">"Generates a script based on the data from a table."</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">Property</span><span class="html1-space"> </span><span class="html1-identifier">Name=</span><span class="html1-value">"SourceTable"</span><span class="html1-space"> </span><span class="html1-identifier">Type=</span><span class="html1-value">"SchemaExplorer.TableSchema"</span><span class="html1-space"> </span><span class="html1-identifier">Category=</span><span class="html1-value">"\tContext"</span><span class="html1-space"> </span><span class="html1-identifier">Description=</span><span class="html1-value">"Table to get the data from."</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">Property</span><span class="html1-space"> </span><span class="html1-identifier">Name=</span><span class="html1-value">"ScriptType"</span><span class="html1-space"> </span><span class="html1-identifier">Type=</span><span class="html1-value">"ScriptTypeEnum"</span><span class="html1-space"> </span><span class="html1-identifier">Category=</span><span class="html1-value">"Option"</span><span class="html1-space"> </span><span class="html1-identifier">Default=</span><span class="html1-value">"Compact"</span><span class="html1-space"> </span><span class="html1-identifier">Description=</span><span class="html1-value">"How the script is rendered"</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">Assembly</span><span class="html1-space"> </span><span class="html1-identifier">Name=</span><span class="html1-value">"SchemaExplorer"</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">Assembly</span><span class="html1-space"> </span><span class="html1-identifier">Name=</span><span class="html1-value">"CodeSmith.BaseTemplates"</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">Assembly</span><span class="html1-space"> </span><span class="html1-identifier">Name=</span><span class="html1-value">"System.Data"</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">Import</span><span class="html1-space"> </span><span class="html1-identifier">Namespace=</span><span class="html1-value">"SchemaExplorer"</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">Import</span><span class="html1-space"> </span><span class="html1-identifier">Namespace=</span><span class="html1-value">"System.Data"</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">Import</span><span class="html1-space"> </span><span class="html1-identifier">Namespace=</span><span class="html1-value">"System.Text"</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%@</span><span class="html1-space"> </span><span class="html1-identifier">Import</span><span class="html1-space"> </span><span class="html1-identifier">Namespace=</span><span class="html1-value">"System.Collections"</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%<br />bool</span><span class="html1-space"> </span><span class="html1-identifier">hasIdentity</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">HasIdentityColumn();<br /></span><span class="html1-identifier">string</span><span class="html1-space"> </span><span class="html1-identifier">tableName</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">string.Format("{0}.[{1}]",</span><span class="html1-space"> </span><span class="html1-identifier">GetTableOwner(),</span><span class="html1-space"> </span><span class="html1-identifier">SourceTable.Name);<br />string</span><span class="html1-space"> </span><span class="html1-identifier">columnList</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">GetColumnList();<br /></span><span class="html1-identifier">%><br /></span><span class="html1-text">USE </span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-space"> </span><span class="html1-value">SourceTable.Database.Name</span><span class="html1-space"> </span><span class="html1-identifier">%><br /></span><span class="html1-text">GO<br /><br />DELETE </span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-value">tableName%</span><span class="html1-symbol">><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%</span><span class="html1-space"> </span><span class="html1-identifier">if</span><span class="html1-space"> </span><span class="html1-identifier">(hasIdentity)</span><span class="html1-space"> </span><span class="html1-identifier">{%><br /></span><span class="html1-text">DBCC CHECKIDENT ('</span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-value">tableName%</span><span class="html1-symbol">></span><span class="html1-text">', RESEED, 1)<br />SET IDENTITY_INSERT </span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-value">tableName%</span><span class="html1-symbol">></span><span class="html1-text"> ON<br /></span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%}%><br /><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%</span><span class="html1-space"> </span><span class="html1-identifier">if</span><span class="html1-space"> </span><span class="html1-identifier">(ScriptType==</span><span class="html1-value">ScriptTypeEnum.Compact)</span><span class="html1-space"> </span><span class="html1-identifier">{</span><span class="html1-space"> </span><span class="html1-identifier">%><br /></span><span class="html1-text">INSERT INTO </span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-value">tableName%</span><span class="html1-symbol">></span><span class="html1-text"> (</span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-value">columnList%</span><span class="html1-symbol">></span><span class="html1-text">)<br /></span><span class="html1-space"> </span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%</span><span class="html1-space"> </span><span class="html1-identifier">for</span><span class="html1-space"> </span><span class="html1-identifier">(int</span><span class="html1-space"> </span><span class="html1-identifier">i</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">0;</span><span class="html1-space"> </span><span class="html1-identifier">i</span><span class="html1-space"> </span><span class="html1-symbol"><</span><span class="html1-space"> </span><span class="html1-unknownword">SourceTableData</span><span class="html1-identifier">.Rows.Count;</span><span class="html1-space"> </span><span class="html1-identifier">i++)</span><span class="html1-space"> </span><span class="html1-identifier">{</span><span class="html1-space"> </span><span class="html1-identifier">%><br /></span><span class="html1-space"> </span><span class="html1-text">SELECT </span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-space"> </span><span class="html1-value">GetTableRowValues(SourceTableData.Rows[i])</span><span class="html1-space"> </span><span class="html1-identifier">%><</span><span class="html1-unknownword"></span><span class="html1-identifier">%</span><span class="html1-space"> </span><span class="html1-identifier">if</span><span class="html1-space"> </span><span class="html1-identifier">(i</span><span class="html1-space"> </span><span class="html1-symbol"><</span><span class="html1-space"> </span><span class="html1-unknownword">SourceTableData</span><span class="html1-identifier">.Rows.Count</span><span class="html1-space"> </span><span class="html1-identifier">-</span><span class="html1-space"> </span><span class="html1-identifier">1)</span><span class="html1-space"> </span><span class="html1-identifier">{</span><span class="html1-space"> </span><span class="html1-identifier">%></span><span class="html1-text"> UNION</span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%</span><span class="html1-space"> </span><span class="html1-identifier">}</span><span class="html1-space"> </span><span class="html1-identifier">%><br /></span><span class="html1-space"> </span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%</span><span class="html1-space"> </span><span class="html1-identifier">}</span><span class="html1-space"> </span><span class="html1-identifier">%><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%}</span><span class="html1-space"> </span><span class="html1-identifier">else</span><span class="html1-space"> </span><span class="html1-identifier">{</span><span class="html1-space"> </span><span class="html1-identifier">for</span><span class="html1-space"> </span><span class="html1-identifier">(int</span><span class="html1-space"> </span><span class="html1-identifier">i</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">0;</span><span class="html1-space"> </span><span class="html1-identifier">i</span><span class="html1-space"> </span><span class="html1-symbol"><</span><span class="html1-space"> </span><span class="html1-unknownword">SourceTableData</span><span class="html1-identifier">.Rows.Count;</span><span class="html1-space"> </span><span class="html1-identifier">i++)</span><span class="html1-space"> </span><span class="html1-identifier">{</span><span class="html1-space"> </span><span class="html1-identifier">%><br /></span><span class="html1-text">INSERT INTO </span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-value">tableName%</span><span class="html1-symbol">></span><span class="html1-text"> (</span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-value">columnList%</span><span class="html1-symbol">></span><span class="html1-text">) VALUES (</span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-value">GetTableRowValues(SourceTableData.Rows[i])%</span><span class="html1-symbol">></span><span class="html1-text">)<br /></span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%}}%><br /><br /><</span><span class="html1-unknownword"></span><span class="html1-identifier">%</span><span class="html1-space"> </span><span class="html1-identifier">if</span><span class="html1-space"> </span><span class="html1-identifier">(hasIdentity)</span><span class="html1-space"> </span><span class="html1-identifier">{%><br /></span><span class="html1-text">SET IDENTITY_INSERT </span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%=</span><span class="html1-value">tableName%</span><span class="html1-symbol">></span><span class="html1-text"> OFF<br /></span><span class="html1-symbol"><</span><span class="html1-unknownword"></span><span class="html1-identifier">%}%><br /><</span><span class="html1-reservedword">script</span><span class="html1-space"> </span><span class="html1-identifier">runat=</span><span class="html1-value">"template"</span><span class="html1-symbol">><br /><br /></span><span class="html1-text">public enum ScriptTypeEnum<br />{<br /> Compact,<br /> Verbose<br />}<br /><br />private DataTable _sourceTableData;<br /><br />private DataTable SourceTableData<br />{<br /></span><span class="html1-space"> </span><span class="html1-text">get<br /></span><span class="html1-space"> </span><span class="html1-text">{<br /></span><span class="html1-space"> </span><span class="html1-text">if (_sourceTableData == null)<br /></span><span class="html1-space"> </span><span class="html1-text">{<br /></span><span class="html1-space"> </span><span class="html1-text">_sourceTableData = SourceTable.GetTableData();<br /></span><span class="html1-space"> </span><span class="html1-text">}<br /></span><span class="html1-space"> <br /> </span><span class="html1-text">return _sourceTableData;<br /></span><span class="html1-space"> </span><span class="html1-text">}<br />}<br /><br />public string GetColumnList()<br />{<br /></span><span class="html1-space"> </span><span class="html1-text">ArrayList columnList = new ArrayList(SourceTable.Columns.Count);<br /></span><span class="html1-space"> </span><span class="html1-text">foreach(ColumnSchema column in SourceTable.Columns)<br /></span><span class="html1-space"> </span><span class="html1-text">{<br /></span><span class="html1-space"> </span><span class="html1-text">columnList.Add(string.Format("[{0}]", column.Name));<br /></span><span class="html1-space"> </span><span class="html1-text">}<br /></span><span class="html1-space"> </span><span class="html1-text">return string.Join(", ", (string[]) columnList.ToArray(typeof(string)));<br />}<br /><br />public string GetTableRowValues(DataRow row)<br />{<br /></span><span class="html1-space"> </span><span class="html1-text">StringBuilder rowBuilder = new StringBuilder();<br /></span><span class="html1-space"> <br /> </span><span class="html1-text">int columnCount = SourceTable.Columns.Count;<br /></span><span class="html1-space"> </span><span class="html1-text">ArrayList valueList = new ArrayList(columnCount);<br /></span><span class="html1-space"> </span><span class="html1-text">for (int i=0; i</span><span class="html1-symbol"><</span><span class="html1-unknownword">columnCount</span><span class="html1-identifier">;</span><span class="html1-space"> </span><span class="html1-identifier">i++)<br /></span><span class="html1-space"> </span><span class="html1-identifier">{<br /></span><span class="html1-space"> </span><span class="html1-identifier">ColumnSchema</span><span class="html1-space"> </span><span class="html1-identifier">column</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">SourceTable.Columns[i];<br /></span><span class="html1-space"> </span><span class="html1-identifier">if</span><span class="html1-space"> </span><span class="html1-identifier">(row[i]</span><span class="html1-space"> </span><span class="html1-symbol">==</span><span class="html1-space"> </span><span class="html1-value">DBNull.Value)<br /></span><span class="html1-space"> </span><span class="html1-identifier">{<br /></span><span class="html1-space"> </span><span class="html1-identifier">valueList.Add("NULL");<br /></span><span class="html1-space"> </span><span class="html1-identifier">}<br /></span><span class="html1-space"> </span><span class="html1-identifier">else<br /></span><span class="html1-space"> </span><span class="html1-identifier">{<br /></span><span class="html1-space"> </span><span class="html1-identifier">switch</span><span class="html1-space"> </span><span class="html1-identifier">(column.NativeType.ToLower())<br /></span><span class="html1-space"> </span><span class="html1-identifier">{<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"bigint":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"decimal":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"float":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"int":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"money":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"numeric":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"real":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"smallint":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"smallmoney":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"tinyint":<br /></span><span class="html1-space"> </span><span class="html1-identifier">//</span><span class="html1-space"> </span><span class="html1-identifier">numeric</span><span class="html1-space"> </span><span class="html1-identifier">type<br /></span><span class="html1-space"> </span><span class="html1-identifier">valueList.Add(row[i].ToString());<br /></span><span class="html1-space"> </span><span class="html1-identifier">break;<br /></span><span class="html1-space"> <br /> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"bit":<br /></span><span class="html1-space"> </span><span class="html1-identifier">//</span><span class="html1-space"> </span><span class="html1-identifier">boolean</span><span class="html1-space"> </span><span class="html1-identifier">type<br /></span><span class="html1-space"> </span><span class="html1-identifier">string</span><span class="html1-space"> </span><span class="html1-identifier">val</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">((bool)</span><span class="html1-space"> </span><span class="html1-identifier">row[i])</span><span class="html1-space"> </span><span class="html1-identifier">?</span><span class="html1-space"> </span><span class="html1-identifier">"1"</span><span class="html1-space"> </span><span class="html1-identifier">:</span><span class="html1-space"> </span><span class="html1-identifier">"0";<br /></span><span class="html1-space"> </span><span class="html1-identifier">valueList.Add(val);<br /></span><span class="html1-space"> </span><span class="html1-identifier">break;<br /><br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"varbinary":</span><span class="html1-space"> <br /> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"binary":<br /></span><span class="html1-space"> </span><span class="html1-identifier">//</span><span class="html1-space"> </span><span class="html1-identifier">binary</span><span class="html1-space"> </span><span class="html1-identifier">type<br /></span><span class="html1-space"> </span><span class="html1-identifier">valueList.Add(GetHexStringFromBytes((byte[])row[i]));<br /></span><span class="html1-space"> </span><span class="html1-identifier">break;<br /></span><span class="html1-space"> <br /> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"datetime":<br /></span><span class="html1-space"> </span><span class="html1-identifier">case</span><span class="html1-space"> </span><span class="html1-identifier">"smalldatetime":<br /></span><span class="html1-space"> </span><span class="html1-identifier">//</span><span class="html1-space"> </span><span class="html1-identifier">datetime</span><span class="html1-space"> </span><span class="html1-identifier">type<br /></span><span class="html1-space"> </span><span class="html1-identifier">DateTime</span><span class="html1-space"> </span><span class="html1-identifier">dt</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">(DateTime)</span><span class="html1-space"> </span><span class="html1-identifier">row[i];<br /></span><span class="html1-space"> </span><span class="html1-identifier">valueList.Add(string.Format("'{0:yyyy-MM-dd</span><span class="html1-space"> </span><span class="html1-identifier">HH:mm:ss.fff}'",</span><span class="html1-space"> </span><span class="html1-identifier">dt));<br /></span><span class="html1-space"> </span><span class="html1-identifier">break;<br /><br /></span><span class="html1-space"> </span><span class="html1-identifier">default:<br /></span><span class="html1-space"> </span><span class="html1-identifier">//</span><span class="html1-space"> </span><span class="html1-identifier">other</span><span class="html1-space"> </span><span class="html1-identifier">type<br /></span><span class="html1-space"> </span><span class="html1-identifier">valueList.Add(string.Format("'{0}'",</span><span class="html1-space"> </span><span class="html1-identifier">PrepareValue(row[i].ToString())));<br /></span><span class="html1-space"> </span><span class="html1-identifier">break;<br /></span><span class="html1-space"> </span><span class="html1-identifier">}<br /></span><span class="html1-space"> </span><span class="html1-identifier">}<br /></span><span class="html1-space"> </span><span class="html1-identifier">}<br /></span><span class="html1-space"> </span><span class="html1-identifier">return</span><span class="html1-space"> </span><span class="html1-identifier">string.Join(",</span><span class="html1-space"> </span><span class="html1-identifier">",</span><span class="html1-space"> </span><span class="html1-identifier">(string[])</span><span class="html1-space"> </span><span class="html1-identifier">valueList.ToArray(typeof(string)));<br />}<br /><br />public</span><span class="html1-space"> </span><span class="html1-identifier">string</span><span class="html1-space"> </span><span class="html1-identifier">PrepareValue(string</span><span class="html1-space"> </span><span class="html1-identifier">value)<br />{<br /></span><span class="html1-space"> </span><span class="html1-identifier">return</span><span class="html1-space"> </span><span class="html1-identifier">value.Replace("'",</span><span class="html1-space"> </span><span class="html1-identifier">"''").Replace("\r\n",</span><span class="html1-space"> </span><span class="html1-identifier">"'</span><span class="html1-space"> </span><span class="html1-identifier">+</span><span class="html1-space"> </span><span class="html1-identifier">CHAR(13)</span><span class="html1-space"> </span><span class="html1-identifier">+</span><span class="html1-space"> </span><span class="html1-identifier">CHAR(10)</span><span class="html1-space"> </span><span class="html1-identifier">+</span><span class="html1-space"> </span><span class="html1-identifier">'").Replace("\n",</span><span class="html1-space"> </span><span class="html1-identifier">"'</span><span class="html1-space"> </span><span class="html1-identifier">+</span><span class="html1-space"> </span><span class="html1-identifier">CHAR(10)</span><span class="html1-space"> </span><span class="html1-identifier">+</span><span class="html1-space"> </span><span class="html1-identifier">'");<br />}<br /><br />public</span><span class="html1-space"> </span><span class="html1-identifier">string</span><span class="html1-space"> </span><span class="html1-identifier">GetTableOwner()<br />{<br /></span><span class="html1-space"> </span><span class="html1-identifier">string</span><span class="html1-space"> </span><span class="html1-identifier">owner</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">SourceTable.Owner;<br /></span><span class="html1-space"> </span><span class="html1-identifier">if</span><span class="html1-space"> </span><span class="html1-identifier">(!owner.Equals(string.Empty))<br /></span><span class="html1-space"> </span><span class="html1-identifier">{<br /></span><span class="html1-space"> </span><span class="html1-identifier">return</span><span class="html1-space"> </span><span class="html1-identifier">string.Format("[{0}]",</span><span class="html1-space"> </span><span class="html1-identifier">owner);<br /></span><span class="html1-space"> </span><span class="html1-identifier">}<br /></span><span class="html1-space"> </span><span class="html1-identifier">return</span><span class="html1-space"> </span><span class="html1-identifier">string.Empty;<br />}<br /><br />public</span><span class="html1-space"> </span><span class="html1-identifier">string</span><span class="html1-space"> </span><span class="html1-identifier">GetHexStringFromBytes(byte[]</span><span class="html1-space"> </span><span class="html1-identifier">bytes)<br />{<br /></span><span class="html1-space"> </span><span class="html1-identifier">if</span><span class="html1-space"> </span><span class="html1-identifier">(bytes</span><span class="html1-space"> </span><span class="html1-symbol">==</span><span class="html1-space"> </span><span class="html1-value">null)<br /></span><span class="html1-space"> </span><span class="html1-identifier">{<br /></span><span class="html1-space"> </span><span class="html1-identifier">return</span><span class="html1-space"> </span><span class="html1-identifier">string.Empty;<br /></span><span class="html1-space"> </span><span class="html1-identifier">}<br /></span><span class="html1-space"> <br /> </span><span class="html1-identifier">int</span><span class="html1-space"> </span><span class="html1-identifier">byteCount</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">bytes.Length;<br /></span><span class="html1-space"> </span><span class="html1-identifier">StringBuilder</span><span class="html1-space"> </span><span class="html1-identifier">sb</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">new</span><span class="html1-space"> </span><span class="html1-identifier">StringBuilder(byteCount</span><span class="html1-space"> </span><span class="html1-identifier">*</span><span class="html1-space"> </span><span class="html1-identifier">2</span><span class="html1-space"> </span><span class="html1-identifier">+</span><span class="html1-space"> </span><span class="html1-identifier">2);<br /></span><span class="html1-space"> </span><span class="html1-identifier">sb.Append("0x");<br /></span><span class="html1-space"> </span><span class="html1-identifier">for</span><span class="html1-space"> </span><span class="html1-identifier">(int</span><span class="html1-space"> </span><span class="html1-identifier">i</span><span class="html1-space"> </span><span class="html1-symbol">=</span><span class="html1-space"> </span><span class="html1-value">0;</span><span class="html1-space"> </span><span class="html1-identifier">i</span><span class="html1-space"> </span><span class="html1-symbol"><</span><span class="html1-space"> </span><span class="html1-unknownword">byteCount</span><span class="html1-identifier">;</span><span class="html1-space"> </span><span class="html1-identifier">i++)<br /></span><span class="html1-space"> </span><span class="html1-identifier">{<br /></span><span class="html1-space"> </span><span class="html1-identifier">sb.Append(bytes[i].ToString("X2"));<br /></span><span class="html1-space"> </span><span class="html1-identifier">}<br /></span><span class="html1-space"> </span><span class="html1-identifier">return</span><span class="html1-space"> </span><span class="html1-identifier">sb.ToString();<br />}<br /><br />public</span><span class="html1-space"> </span><span class="html1-identifier">bool</span><span class="html1-space"> </span><span class="html1-identifier">HasIdentityColumn()<br />{<br /></span><span class="html1-space"> </span><span class="html1-identifier">foreach(ColumnSchema</span><span class="html1-space"> </span><span class="html1-identifier">column</span><span class="html1-space"> </span><span class="html1-identifier">in</span><span class="html1-space"> </span><span class="html1-identifier">SourceTable.Columns)<br /></span><span class="html1-space"> </span><span class="html1-identifier">{<br /></span><span class="html1-space"> </span><span class="html1-identifier">if</span><span class="html1-space"> </span><span class="html1-identifier">(column.ExtendedProperties["CS_IsIdentity"].Value.ToString()==</span><span class="html1-value">"True"</span><span class="html1-identifier">)<br /></span><span class="html1-space"> </span><span class="html1-identifier">{<br /></span><span class="html1-space"> </span><span class="html1-identifier">return</span><span class="html1-space"> </span><span class="html1-identifier">true;<br /></span><span class="html1-space"> </span><span class="html1-identifier">}<br /></span><span class="html1-space"> </span><span class="html1-identifier">}<br /></span><span class="html1-space"> </span><span class="html1-identifier">return</span><span class="html1-space"> </span><span class="html1-identifier">false;<br />}<br /><br /><</span><span class="html1-reservedword">/script</span><span class="html1-symbol">><br /><br /></span></span><br /></code></pre>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-44835470963911324452006-10-06T17:21:00.000+08:002006-10-06T17:32:58.320+08:00Detecting user authentication expiration from an AJAX request<p><strong>Problem</strong>:<br />How to detect that an AJAX request has been redirected to a login page by the ASP.NET's Forms Authentication?<br /><br /><strong>Background</strong>:<br />Forms Authentication is a standard user authentication is ASP.NET. As a refresher, this authentication scheme utilizes cookie to track whether a user already login. A user requesting a secured page will be redirected to a login page if he/she is not authenticated yet. Once the user is authenticated, the server will redirect to the page that the user originally request.</p><p>The Forms Authentication works fine for normal ASPX pages. When the user is idle for a certain amount of time (the default is 30 minutes), the cookie is expired, and the user will be forced to relogin again.</p><p>In an AJAX application, the server also behaves the same way. When the cookie is expired, the server will automatically redirect to the login page. However, since the request is made through a XmlHttpRequest object, the browser does not automatically load the login page, instead the content of login page is retrieved by the XmlHttpRequest object.</p><p><br /><strong>Solution</strong>:<br />I use a HTTP custom header to differentiate a login page from other pages in the web application. By doing this way, Javascript can easily identify whether an AJAX request has been redirected to a login page (Well... it actually detects whether it receives the login page instead of the expected response).</p><p>In the code behind of the login page, add the following code:</p><blockquote><pre><span style="font-family:courier new;font-size:85%;color:#3366ff;"><span style="color:#33cc00;">// add a custom HTTP header to identify that this is a login page</span><br />Response.AppendHeader("IsLoginPage","1");</span></pre></blockquote><p>In the Javascript code, in the function that handles XmlHttpRequest's response, add the code in bold:</p><blockquote><pre><span style="font-family:courier new;font-size:85%;"><span style="color:#3366ff;">xmlHttp.onreadystatechange = function() {<br /> if (xmlHttp.readyState==4) {<br /> if (xmlHttp.status==200 && xmlHttp.responseText!=null) {<br /> </span><span style="color:#3366ff;"><strong>if (xmlHttp.getResponseHeader('IsLoginPage')=='1') {<br /> alert('Your session has expired. Please relogin again');</strong><br /> } else if (typeof(responseHandler)=='function') {<br /> responseHandler(xmlHttp.responseText);<br /> }<br /> }<br /> }<br />};</span></span></pre></blockquote><p><br />I decided to simply display an alert so that the user is aware of the situation. My expectation is upon receiving this message, the user will explicitly relogin to the application.<br /><br /><br /></p>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-23192135742496230212006-10-03T21:02:00.000+08:002006-10-03T21:24:33.599+08:00Data Encryption in SQL Server 2005One of our product stores sensitive information in the database. Our client wants the sensitive information to be encrypted so that nobody except the authorized person can access to it.<br /><br />We want to implement the data encryption at the database level so that it becomes transparent to the applications using the data. The developer should not need to take care about data encryption/decryption and equally important our reporting system which is based on Reporting Service should still works.<br /><br />SQL Server 2000 does not have built-in encryption capability. Data encryption can be done by using extended stored procedures that utilize external dll. This is definitely cumbersome.<br /><br />It turns out that data encryption is a native feature in the new SQL Server 2005. So without further ado, I spent some time investigating the data encryption feature.<br /><br />In SQL Server 2005, data can be encrypted using symmetric keys, asymmetric keys, certificates, or passphrases (plain text), being the last option as the least recommended. We can use a combination of several encryption mechanisms to create a hierarchy of encryption. For example, the data is first encrypted using a symmetric key, then the symmetric key is encrypted using an asymmetric key, and so on to make a stronger encryption.<br /><br />Encryption using certificates and asymmetric keys are slower but more secure than using symmetric keys. Microsoft recommends using symmetric key to encrypt large amount of data and then secure the symmetric key by asymmetric keys or certificates.<br /><br />The code following this blog entry is my first attempt to test the encryption in SQL Server 2005. I created a hypothetical Employee table that stores salary (as money data type), credit card number (varchar) and birth date (datetime). Since encrypted data is stored as varbinary, those columns are declared as varbinary instead of their original type.<br /><br />Following Microsoft's recommendation for large amount of data, I use a symmetric key to encrypt/decrypt the data. The symmetric key is then encrypted by a certificate created internally in SQL Server. Since I don't specify any further encryption mechanism to secure the certificate, by default, the certificate is encrypted by the database master key. In the encryption hierarchy, the database master key is further encrypted by the service master key, and the service master key is secured by DPAPI at the operating system level.<br /><br />I use EncryptByKey and DecryptByKey functions for the encryption/decryption. These functions only accept varchar, nvarchar, char, nchar, varbinary and binary data type. For other data type like datetime and money, we need to CAST/CONVERT it to varbinary. The symmetric key must also be opened/decrypted before we can use it for encryption/decryption.<br /><br />In the view, I use DecryptByKeyAutoCert that automatically opens the symmetric key and uses it to decrypt the cipher text.<br /><br /><br />The code:<br /><br /><div style='font-size: 8pt; font-family:"Courier New"'><br /><span style='color:green'>-- create database</span><br /><span style='color:blue'>CREATE</span> <span><span style='color:blue'>DATABASE</span> EncryptionTest</span><br /><span>GO</span><br /><span>Â </span><br /><span style='color:green'>-- use database</span><br /><span style='color:blue'>USE</span> <span>EncryptionTest</span><br /><span>GO</span><br /><span>Â </span><br /><span style='color:green'>-- create master key for the new database</span><br /><span style='color:blue'>CREATE</span> <span>MASTER <span style='color:blue'>KEY</span> ENCRYPTION <span style='color:blue'>BY</span> PASSWORD <span style='color:gray'>=</span> <span style='color:red'>'P@ssw0rd'</span></span><br /><span>GO</span><br /><span>Â </span><br /><span style='color:green'>-- Create table</span><br /><span style='color:blue'>CREATE</span> <span><span style='color:blue'>TABLE</span> [dbo]<span style='color:gray'>.</span>[Employee]<span style='color:gray'>(</span></span><br /><span>Â Â Â Â Â Â [EmployeeID] [int] <span style='color:blue'>IDENTITY</span><span style='color:gray'>(</span>1<span style='color:gray'>,</span>1<span style='color:gray'>)</span> <span style='color:gray'>NOT</span> <span style='color:gray'>NULL,</span></span><br /><span>Â Â Â Â Â Â [Name] [nvarchar]<span style='color:gray'>(</span>200<span style='color:gray'>)</span> <span style='color:blue'>COLLATE</span> SQL_Latin1_General_CP1_CI_AS <span style='color:gray'>NOT</span> <span style='color:gray'>NULL,</span></span><br /><span>Â Â Â Â Â Â [Position] [nvarchar]<span style='color:gray'>(</span>200<span style='color:gray'>)</span> <span style='color:blue'>COLLATE</span> SQL_Latin1_General_CP1_CI_AS <span style='color:gray'>NULL,</span></span><br /><span>Â Â Â Â Â Â [Salary] [varbinary]<span style='color:gray'>(</span>256<span style='color:gray'>)</span> <span style='color:gray'>NOT</span> <span style='color:gray'>NULL,</span></span><br /><span>Â Â Â Â Â Â [CreditCard] [varbinary]<span style='color:gray'>(</span>256<span style='color:gray'>)</span> <span style='color:gray'>NULL,</span></span><br /><span>Â Â Â Â Â Â [BirthDate] [varbinary]<span style='color:gray'>(</span>256<span style='color:gray'>)</span> <span style='color:gray'>NULL,</span></span><br /><span>Â <span style='color:blue'>CONSTRAINT</span> [PK_Employee] <span style='color:blue'>PRIMARY</span> <span style='color:blue'>KEY</span> <span style='color:blue'>CLUSTERED</span></span><br /><span style='color:gray'>(</span><br /><span>Â Â Â Â Â Â [EmployeeID] <span style='color:blue'>ASC</span></span><br /><span style='color:gray'>)</span><span style='color:blue'>WITH</span> <span><span style='color:gray'>(</span>IGNORE_DUP_KEY <span style='color:gray'>=</span> <span style='color:blue'>OFF</span><span style='color:gray'>)</span> <span style='color:blue'>ON</span> [PRIMARY]</span><br /><span style='color:gray'>)</span> <span><span style='color:blue'>ON</span> [PRIMARY]</span><br /><span>GO</span><br /><span>Â </span><br /><span style='color:green'>-- create certificate to encrypt the symmetric key</span><br /><span style='color:blue'>CREATE</span> <span><span style='color:blue'>CERTIFICATE</span> EmployeeCert</span><br /><span>Â Â Â Â Â Â <span style='color:blue'>WITH</span> SUBJECT <span style='color:gray'>=</span> <span style='color:red'>'Company Certificate'</span><span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â START_DATE <span style='color:gray'>=</span> <span style='color:red'>'1/1/2006'</span><span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â EXPIRY_DATE <span style='color:gray'>=</span> <span style='color:red'>'12/31/2006'</span><span style='color:gray'>;</span></span><br /><span>GO</span><br /><span>Â </span><br /><span style='color:green'>-- create symmetric key and encrypt it with the certificate</span><br /><span style='color:blue'>CREATE</span> <span><span style='color:blue'>SYMMETRIC</span> <span style='color:blue'>KEY</span> EmployeeKey</span><br /><span style='color:blue'>WITH</span> <span>ALGORITHM <span style='color:gray'>=</span> TRIPLE_DES</span><br /><span>ENCRYPTION <span style='color:blue'>BY</span> <span style='color:blue'>CERTIFICATE</span> EmployeeCert</span><br /><span>GO</span><br /><span>Â </span><br /><span style='color:green'>-- create a view to access Employee table</span><br /><span style='color:blue'>CREATE</span> <span><span style='color:blue'>VIEW</span> [dbo]<span style='color:gray'>.</span>[vw_Employee]</span><br /><span style='color:blue'>AS</span><br /><span style='color:blue'>SELECT</span><span>Â [Name]<span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â Â Â Â Â Â Â Â Position<span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â Â Â Â Â Â Â Â <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>MONEY</span><span style='color:gray'>,</span> DecryptByKeyAutoCert<span style='color:gray'>(</span>CERT_ID<span style='color:gray'>(</span><span style='color:red'>'EmployeeCert'</span><span style='color:gray'>),</span> <span style='color:gray'>NULL,</span> Salary<span style='color:gray'>))</span> <span style='color:blue'>AS</span> Salary<span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â Â Â Â Â Â Â Â <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>VARCHAR</span><span style='color:gray'>,</span> DecryptByKeyAutoCert<span style='color:gray'>(</span>CERT_ID<span style='color:gray'>(</span><span style='color:red'>'EmployeeCert'</span><span style='color:gray'>),</span> <span style='color:gray'>NULL,</span> CreditCard<span style='color:gray'>))</span> <span style='color:blue'>AS</span> CreditCard<span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â Â Â Â Â Â Â Â <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>DATETIME</span><span style='color:gray'>,</span> DecryptByKeyAutoCert<span style='color:gray'>(</span>CERT_ID<span style='color:gray'>(</span><span style='color:red'>'EmployeeCert'</span><span style='color:gray'>),</span> <span style='color:gray'>NULL,</span> BirthDate<span style='color:gray'>),</span> 112<span style='color:gray'>)</span> <span style='color:blue'>AS</span> BirthDate</span><br /><span style='color:blue'>FROM</span> <span>dbo<span style='color:gray'>.</span>Employee</span><br /><span>GO</span><br /><span>Â </span><br /><span style='color:green'>-- *** Batch: Encryption</span><br /><span style='color:green'>Â </span><br /><span style='color:green'>-- open symmetric key</span><br /><span style='color:blue'>OPEN</span> <span><span style='color:blue'>SYMMETRIC</span> <span style='color:blue'>KEY</span> EmployeeKey</span><br /><span>DECRYPTION <span style='color:blue'>BY</span> <span style='color:blue'>CERTIFICATE</span> EmployeeCert</span><br /><span>Â </span><br /><span style='color:green'>-- get symmetric key id to be used in the encryption</span><br /><span style='color:blue'>DECLARE</span> <span style='font-size: 8.0pt;'>@KeyGUID <span style='color:blue'>uniqueidentifier</span></span><br /><span style='color:blue'>SET</span> <span>@KeyGUID <span style='color:gray'>=</span> KEY_GUID<span style='color:gray'>(</span><span style='color:red'>'EmployeeKey'</span><span style='color:gray'>)</span></span><br /><span style='color:gray'>Â </span><br /><span style='color:green'>-- insert some records to Employee table</span><br /><span style='color:blue'>INSERT</span> <span><span style='color:blue'>INTO</span> dbo<span style='color:gray'>.</span>Employee<span style='color:gray'>(</span>[Name]<span style='color:gray'>,</span> Position<span style='color:gray'>,</span> Salary<span style='color:gray'>,</span> CreditCard<span style='color:gray'>,</span> BirthDate<span style='color:gray'>)</span></span><br /><span style='color:blue'>SELECT</span><br /><span>Â Â Â Â Â Â <span style='color:red'>'John Smith'</span><span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â <span style='color:red'>'CEO'</span><span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â EncryptByKey<span style='color:gray'>(</span>@KeyGUID<span style='color:gray'>,</span> <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>VARBINARY</span><span style='color:gray'>(</span>256<span style='color:gray'>),</span> $200000<span style='color:gray'>)),</span></span><br /><span>Â Â Â Â Â Â EncryptByKey<span style='color:gray'>(</span>@KeyGUID<span style='color:gray'>,</span> <span style='color:red'>'4444-3333-2222-1111'</span><span style='color:gray'>),</span></span><br /><span>Â Â Â Â Â Â EncryptByKey<span style='color:gray'>(</span>@KeyGUID<span style='color:gray'>,</span> <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>VARBINARY</span><span style='color:gray'>(</span>256<span style='color:gray'>),</span> <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>DATETIME</span><span style='color:gray'>,</span> <span style='color:red'>'19400502'</span><span style='color:gray'>,</span> 112<span style='color:gray'>)))</span></span><br /><span style='color:blue'>UNION</span><br /><span style='color:blue'>SELECT</span><br /><span>Â Â Â Â Â Â <span style='color:red'>'Garry Baker'</span><span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â <span style='color:red'>'General Manager'</span><span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â EncryptByKey<span style='color:gray'>(</span>@KeyGUID<span style='color:gray'>,</span> <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>VARBINARY</span><span style='color:gray'>(</span>256<span style='color:gray'>),</span> $150000<span style='color:gray'>)),</span></span><br /><span>Â Â Â Â Â Â EncryptByKey<span style='color:gray'>(</span>@KeyGUID<span style='color:gray'>,</span> <span style='color:red'>'4444-3333-2211-1144'</span><span style='color:gray'>),</span></span><br /><span>Â Â Â Â Â Â EncryptByKey<span style='color:gray'>(</span>@KeyGUID<span style='color:gray'>,</span> <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>VARBINARY</span><span style='color:gray'>(</span>256<span style='color:gray'>),</span> <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>DATETIME</span><span style='color:gray'>,</span> <span style='color:red'>'19450108'</span><span style='color:gray'>,</span> 112<span style='color:gray'>)))</span></span><br /><span style='color:blue'>UNION</span><br /><span style='color:blue'>SELECT</span><br /><span>Â Â Â Â Â Â <span style='color:red'>'Natasha Smith'</span><span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â <span style='color:red'>'Account Manager'</span><span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â EncryptByKey<span style='color:gray'>(</span>@KeyGUID<span style='color:gray'>,</span> <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>VARBINARY</span><span style='color:gray'>(</span>256<span style='color:gray'>),</span> $120000<span style='color:gray'>)),</span></span><br /><span>Â Â Â Â Â Â EncryptByKey<span style='color:gray'>(</span>@KeyGUID<span style='color:gray'>,</span> <span style='color:red'>'4444-1111-1111-1111'</span><span style='color:gray'>),</span></span><br /><span>Â Â Â Â Â Â EncryptByKey<span style='color:gray'>(</span>@KeyGUID<span style='color:gray'>,</span> <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>VARBINARY</span><span style='color:gray'>(</span>256<span style='color:gray'>),</span> <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>DATETIME</span><span style='color:gray'>,</span> <span style='color:red'>'19550501'</span><span style='color:gray'>,</span> 112<span style='color:gray'>)))</span></span><br /><span style='color:gray'>Â </span><br /><span style='color:green'>-- close key</span><br /><span style='color:blue'>CLOSE</span> <span><span style='color:blue'>SYMMETRIC</span> <span style='color:blue'>KEY</span> EmployeeKey</span><br /><span>GO</span><br /><span>Â </span><br /><span style='color:green'>-- *** Batch: Decryption</span><br /><span style='color:green'>Â </span><br /><span style='color:green'>-- open symmetric key</span><br /><span style='color:blue'>OPEN</span> <span><span style='color:blue'>SYMMETRIC</span> <span style='color:blue'>KEY</span> EmployeeKey</span><br /><span>DECRYPTION <span style='color:blue'>BY</span> <span style='color:blue'>CERTIFICATE</span> EmployeeCert</span><br /><span>Â </span><br /><span style='color:green'>-- select Employee</span><br /><span style='color:blue'>SELECT</span><span>Â [Name]<span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â Â Â Â Â Â Â Â Position<span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â Â Â Â Â Â Â Â <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>MONEY</span><span style='color:gray'>,</span> DecryptByKey<span style='color:gray'>(</span>Salary<span style='color:gray'>))</span> <span style='color:blue'>AS</span> Salary<span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â Â Â Â Â Â Â Â <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>VARCHAR</span><span style='color:gray'>,</span> DecryptByKey<span style='color:gray'>(</span>CreditCard<span style='color:gray'>))</span> <span style='color:blue'>AS</span> CreditCard<span style='color:gray'>,</span></span><br /><span>Â Â Â Â Â Â Â Â Â Â Â Â Â <span style='color:fuchsia'>CONVERT</span><span style='color:gray'>(</span><span style='color:blue'>DATETIME</span><span style='color:gray'>,</span> DecryptByKey<span style='color:gray'>(</span>BirthDate<span style='color:gray'>),</span> 112<span style='color:gray'>)</span> <span style='color:blue'>AS</span> BirthDate</span><br /><span style='color:blue'>FROM</span> <span>dbo<span style='color:gray'>.</span>Employee</span><br /><span>Â </span><br /><span style='color:green'>-- close key</span><br /><span style='color:blue'>CLOSE</span> <span><span style='color:blue'>SYMMETRIC</span> <span style='color:blue'>KEY</span> EmployeeKey</span><br /><span>GO</span><br /></div>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-17305463615701200562006-09-05T20:00:00.000+08:002006-09-05T20:32:00.277+08:00Javascript AutocastA typical day at work requires me to constantly switch between writing code in C# and Javascript. This effects the way I write code in both languages. Since C# is more strict that Javascript, my coding style leans towards C# and sometimes I find myself writing Javascript code in C# style, which can be viewed as good or a bad thing.<br /><br />Every language has its goodies that if we take advantage of can produce better quality code.<br /><br />Let's take a common task as example. We want to test a string variable for null and empty.<br /><br />In C#,I will do like:<br /><br /><br /><span style="font-size:85%;"><blockquote><span style="color:#000099;"><span style="font-size:85%;">string str;<br />...</span><br /></span><span style="font-size:85%;color:#000099;">if (str != null && !str.Equals(string.Empty)) { }<br /></span><br /></blockquote></span><br />Applying the same C# style to Javascript will result in the following code:<br /><br /><span style="font-size:85%;"><blockquote><span style="color:#000099;"><span style="font-size:85%;">var str;</span><br /></span><span style="font-size:85%;color:#000099;">...<br />if (str != null && str != '') { }<br /></span><br /></blockquote></span>The above code is not optimized. In Javascript, we can utilitize 'autocast' feature that will reduce the code into:<br /><br /><blockquote><span style="font-size:85%;color:#000099;">var str;<br />...<br />if (str) { }<br /></span></blockquote><br />A null or an empty string autocasts into false, and therefore the condition is evaluated as false.<br /><br />As you can see, the code is much shorter and cleaner. Size does matter in Javascript. Shorter code translates into faster download time.<br /><br />Autocast also applies to other types/conditions:<br /><br /><ul><li>Undefined variable is evaluated as false. A variable is undefined if it hasn't been assigned a value.<br />Example:<br /><br /><span style="color:#000099;"><span style="font-size:85%;">var myVar;<br />if (myVar) { } // false</span><br /></span><br /></li><li>Empty string is evaluated as false<br /><br /><span style="font-size:85%;"><span style="color:#000099;">var myStr = '';<br />if (myStr) { } // false<br /><br />var myStr2 = 'ABC';<br />if (myStr2) { } // true<br /></span><br /></span></li><li>Zero is evaluated as false<br /><br /><span style="font-size:85%;color:#000099;">var myNum = 0;<br />if (myNum) { } // false<br /><br />var myNum2 = 1;<br />if (myNum2) { } // true<br /></span><br /></li><li>Null is evaluated as false<br /><br /><span style="font-size:85%;"><span style="color:#000099;">var myObj = null;<br />if (myObj) { } // false<br /></span><br /></span></li><li>Object is evaluated as true<br /><br /><span style="font-size:85%;"><span style="color:#000099;">var myObj = {};<br />var myObj2 = new Object();<br />if (myObj) { } // true<br />if (myObj2) { } // also true</span><br /><br /></span></li><li>Empty array is evaluated as true<br />An array is essentially an object, therefore it is evaluated as true.<br /><br /><span style="font-size:85%;color:#000099;">var myArray = [];<br />var myArray2 = new Array();<br />if (myArray) { } // true<br />if (myArray2) { } // true</span></li></ul><p> </p><p> </p>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com2tag:blogger.com,1999:blog-12497981.post-39384052044925567882006-09-01T18:21:00.000+08:002006-09-01T18:24:34.917+08:00Javascript optimizationThe Internet Explorer Team (the team that brings us IE 7) has posted a nice article about Javascript optimization. This is the first part of the scheduled 3 parts article. In this article, the main drive behind the optimization is to reduce the number of symbolic lookups made by the Javascript engine to map the variable name to its real object.<br /><br />It is quite rare to find such optimization tips from the maker of the browser. I am glad finally IE team pays some attention to improve the Javascript development. We need more articles like this, as more and more Javascript code is written nowdays. In my current project which is using ASP.NET 1.1, about 75% of the UI code is written in Javascript, and the rest is the C# code behind.<br /><br />Please find the article <a href="http://blogs.msdn.com/ie/archive/2006/08/28/728654.aspx">here</a>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1156862878891649732006-08-29T22:43:00.000+08:002006-08-29T22:47:58.940+08:00Know your end users<p>On a Saturday morning, I went to a bank to do some over the counter transaction. I was attended by a customer service staff who used a Dell desktop computer with an LCD monitor.</p><p>My request takes many forms to be filled and a lot of data entries to be made to the system. I couldn't see the monitor, but I just imagine by looking at how busy the female banker was entering data using the keyboard. Surprisingly, she didn't use the mouse at all and rely entirely on the keyboard and its function keys (F1-F12). The mouse is connected to the computer, but she put it in front of the keyboard so I believe she just want to make some space by getting rid of it.</p><p>On another ocassion in a travel agency, I notice a diffent balance between keyboard and mouse usage. I was booking for an airline ticket and the staff attending me used both keyboard and mouse. However, he used the keyboard much more frequently to enter not-so-user-friendly commands on the terminal window and only few times used the mouse to click on the big toolbar located on top of the terminal window. Perhaps the toolbar is used to execute a simple command like 'Print Flight Itinerary'.</p><p>Drawing from the two short scenarios above, we can see that different users have different way to use the application. Naturally, the platform of an application defines its limitation, like in a terminal window where everything is text, keyboard is definitely the main input device. However, in most today's applications, desktop-based or web-based, the mouse and the keyboard are both acceptable input devices. But still many people choose to mainly use keyboard alone. They do have valid reasons, most probably because they are so familiar with the keyboard and therefore can operate faster compared to using the mouse.</p><p>It is paramount for us, the software developers, to know the behaviour of the end users who will actually use the application we build. Imagine if we develop a cool interactive web application, fully enriched with DHTML popups, animations, and drag and drops, only to realize later after the release that the users prefer to navigate using combinations of keyboard arrows and tabs rather than the mouse.</p><p>I highly recommend developers, who spent most of the time behind the stage, to come out from their cubicle and pay a visit to client office. Look at how your end users use the application that you build. I bet you will be surprised and it may change the way you design and develop your application. </p>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1156215401054362342006-08-22T10:36:00.000+08:002006-08-22T10:56:41.066+08:00Visual Studio 2003 SP1 is finally hereFinally, the long overdue, first and possibly the last, and much anticipated Service Pack 1 for Visual Studio 2003 is released. You can download the 156MB package from <a href="http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=69D2219F-CE82-46A5-8AEC-072BD4BB955E">the download page</a>. The service pack offers no new features, but it fixes many bugs listed in <a href="http://support.microsoft.com/?kbid=918007">the bug list</a>.<br /><br />One of the fix that looks promising is no. 832714: "Visual Studio cannot open a Web site if a duplicate Web site exists". This problem often happens when I opened a fresh new web project from the SourceSafe and I manually creates virtual directory for the web project. However, I haven't tested this fix yet.<br /><br />The installation of SP1 requires Visual Studio 2003 CD 1, so make sure you keep it handy. If your team has several developers, it's more efficient to copy the CD to a shared network drive and point the installer to look at the shared drive.Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1154619392827688122006-08-05T09:46:00.000+08:002006-08-09T13:37:52.163+08:00Check an HTML element is in view<span class="category">ajax</span>Most of the web page that I created contains a form to let the user enters data. Generally, I put a customized ASP.NET's validation summary control to validation errors in the unified way. Since this is a customized control, I also use the control to display the status of an AJAX operation whether successful or not.<br /><br />Whenever I display a message, I want to immediately grab user's attention. Initially, I call <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/scrollintoview.asp">object.scrollIntoView</a>. This is an IE specific method that scrolls the screen so that the corresponding object is in view. As the result, the element is either aligned at the top or bottom of the screen. This is OK, but what happen next is when the object is already in view, the screen jiggles and changes the position so that the object is position on the top. Quite an annoying experience for the user!<br /><br />So I write this Javascript function that will check whether an object is in view.<br /><br />The function accepts two parameters: the reference to the object (HTML element) and a boolean value bWhole. If you set bWhole to false, the function will only check the top-left corner of the element. It will return true if the top-left corner of the element is within the viewable area. However, if bWhole is to false, the function checks both top-left and bottom-right corners are within viewable area.<br /><br /><div class="code"><br /><span style="color:blue;">function </span><span style="color:black;">isInView(o,bWhole) {<br /></span><span style="color:blue;">if</span><span style="color:black;">(</span><span style="color:blue;">typeof</span><span style="color:black;">(o)</span><span style="color:blue;">==</span><span style="color:#808080;">'undefined' </span><span style="color:black;">!o) </span><span style="color:blue;">return false;<br />if</span><span style="color:black;">(</span><span style="color:blue;">typeof</span><span style="color:black;">(bWhole)</span><span style="color:blue;">==</span><span style="color:#808080;">'undefined'</span><span style="color:black;">) bWhole</span><span style="color:blue;">=false;<br />var </span><span style="color:black;">x1</span><span style="color:blue;">=</span><span style="color:black;">o.offsetLeft</span><span style="color:blue;">;<br />var </span><span style="color:black;">y1</span><span style="color:blue;">=</span><span style="color:black;">o.offsetTop</span><span style="color:blue;">;<br />var </span><span style="color:black;">p</span><span style="color:blue;">=</span><span style="color:black;">o</span><span style="color:blue;">;<br />while </span><span style="color:black;">(p.offsetParent) {<br />p</span><span style="color:blue;">=</span><span style="color:black;">p.offsetParent</span><span style="color:blue;">;<br /></span><span style="color:black;">x1+</span><span style="color:blue;">=</span><span style="color:black;">p.offsetLeft</span><span style="color:blue;">;<br /></span><span style="color:black;">y1+</span><span style="color:blue;">=</span><span style="color:black;">p.offsetTop</span><span style="color:blue;">;<br /></span><span style="color:black;">}<br /></span><span style="color:blue;">var </span><span style="color:black;">x2</span><span style="color:blue;">=</span><span style="color:black;">x1+o.offsetWidth-</span><span style="color:maroon;">1</span><span style="color:blue;">;<br />var </span><span style="color:black;">y2</span><span style="color:blue;">=</span><span style="color:black;">y1+o.offsetHeight-</span><span style="color:maroon;">1</span><span style="color:blue;">;<br />var </span><span style="color:black;">left</span><span style="color:blue;">=document</span><span style="color:black;">.body.scrollLeft</span><span style="color:blue;">;<br />var </span><span style="color:black;">right</span><span style="color:blue;">=</span><span style="color:black;">left+document.body.clientWidth-</span><span style="color:maroon;">1</span><span style="color:blue;">;<br />var </span><span style="color:black;">top</span><span style="color:blue;">=document</span><span style="color:black;">.body.scrollTop</span><span style="color:blue;">;<br />var </span><span style="color:black;">bottom</span><span style="color:blue;">=</span><span style="color:black;">top+document.body.offsetHeight-</span><span style="color:maroon;">1</span><span style="color:blue;">;<br />return </span><span style="color:black;">(bWhole)? (x1></span><span style="color:blue;">=</span><span style="color:black;">left && x2<</span><span style="color:blue;">=</span><span style="color:black;">right && y1></span><span style="color:blue;">=</span><span style="color:black;">top && y2<</span><span style="color:blue;">=</span><span style="color:black;">bottom) : (x1></span><span style="color:blue;">=</span><span style="color:black;">left && x1<</span><span style="color:blue;">=</span><span style="color:black;">right && y1></span><span style="color:blue;">=</span><span style="color:black;">top && y1<</span><span style="color:blue;">=</span><span style="color:black;">bottom) (x2></span><span style="color:blue;">=</span><span style="color:black;">left && x2<</span><span style="color:blue;">=</span><span style="color:black;">right && y2></span><span style="color:blue;">=</span><span style="color:black;">top && y2<</span><span style="color:blue;">=</span><span style="color:black;">bottom)</span><span style="color:blue;">;<br /></span><span style="color:black;">}</span> </div>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1153832853469302612006-07-31T21:32:00.000+08:002006-07-31T21:41:44.723+08:00UI Design Patterns: Hierarchical Master Detail<span style="font-size:130%;">Problem Summary</span><br /><br />The user needs to traverse through a hierarchical or tree-like structured data and do CRUD (Create, Read, Update, and Delete) operations.<br /><br /><br /><span style="font-size:130%;">Screenshot</span> (click to enlarge)<br /><a href="http://photos1.blogger.com/hello/289/6324/1024/MasterDetailPattern.jpg"><img style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #000000 1px solid; MARGIN: 2px; BORDER-LEFT: #000000 1px solid; BORDER-BOTTOM: #000000 1px solid" src="http://photos1.blogger.com/hello/289/6324/200/MasterDetailPattern.jpg" border="0" /></a><br /><p></p><span style="font-size:130%;">Use When </span><br /><p></p><ul><li>The data is structured in a hierarchical manner. Example: organization structure, web pages.</li><li>The amount of fields/columns in the data is sufficient enough to be displayed as a form in one screen-length.<br /></li></ul><p><span style="font-size:130%;">Don't Use When</span> </p><ul><li>The data is flat</li><li>The data consists of very few fields, e.g. only key and value, or consists of many fields that span across several screen length.</li></ul><p><span style="font-size:130%;">Solution </span></p><ul><li>Divide the screen into two columns.</li><li>The left column contains a tree view control to let the user navigates through the hierarchical data. On top of the tree view control is a toolbar (or collection of buttons) to work on the tree view. In the screenshot, it has "Add Child" to add a child node, "Add Root" to add root node, and "Remove" to remove a node.</li><li>The right column contains a form that the user can use to add new data and update existing data</li><li>The form on the right will only appear when there is selected node on the left.</li></ul><p><span style="font-size:130%;">Suggested Improvement</span> </p><ul><li><strong>Drag and drop</strong> among nodes in the tree view. Drag and drop is a sophisticated operation that moves a branch of hierarchical nodes from one parent to another parent.</li><li><strong>Clone node</strong> feature for faster creation of new data. Instead of always starting with blank form, the user is aided with a copy of data from another node.</li><li><strong>Load on demand </strong>treeview to handle large hierarchical data.</li><li><strong>Banding</strong> to improve the performance when there are too many children under one parent node</li><li><strong>Hierarchical delete</strong>. When the parent node is removed, all its direct and indirect children nodes are also deleted. If this is not possible, then the user should only be able to delete from the bottom-up.<br /></li></ul>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com1tag:blogger.com,1999:blog-12497981.post-1154352889301288472006-07-30T08:20:00.000+08:002006-07-31T21:34:49.323+08:00Enterprise UI Design Patterns anyone?As mentioned in my previous post, I have been closely following the UI design patterns published on the Internet. These patterns are really problem solver, however, I feel the majority of the patterns do not apply to the type of web application I am building.<br /><br />In my current company and previous ones, I build web application for business use. This type of application have their own distinct characteristics and problems:<br /><ul><li>Manipulates a lot of data in form layout</li><li>Majority of the operations are CRUD (Create Read Update Delete)</li><li>Data validations</li><li>Hierarchical and flat data structure</li><li>Deals with master-detail relationship</li><li>etc.</li></ul><p>I have been identifying some UI patterns that my team and I often use in the web application to tackle the same repeating problems. In the next posts, I will start documenting those UI patterns so that my reader can benefit from it. Feel free to comment on my patterns as I always need to continuously improve them.</p><p></p>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1153834630526095862006-07-25T21:17:00.000+08:002006-07-25T22:22:35.633+08:00UI Design Pattern GaloreNowadays, there has been a growing number of web sites that collect UI design patterns. The UI design patterns are common solution to recurring problems when designing user interfaces. Whenever I face a usability issue or make a judgement on screen design, I always recall to the collection of UI design patterns. Therefore, it is so useful to bookmark web sites that collects design patterns. My top three sites on the list are:<br /><br /><a href="http://developer.yahoo.com/ypatterns/">Yahoo! Design Pattern Library</a><br />It has a lot of interesting (read: advanced) patterns like drag and drop, animations, etc. It is good to discover what today's web applications can do. When you are ready to put the patterns into practice, try the companion <a href="http://developer.yahoo.com/yui/">Yahoo! UI library</a>.<br /><br /><a href="http://designinginterfaces.com/">Designing Interfaces</a><br />I read the book with the same title first before discovering this site. This is the book/site to discover more UI patterns beyond web page.<br /><br /><a href="http://www.welie.com/patterns/">Patterns in Interaction Design</a><br />Plenty of patterns and tons of screenshots make this site a good reference.Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1153303604032282312006-07-19T18:03:00.000+08:002006-07-19T18:06:44.046+08:00Microsoft hasn't abandon us!Better late than never. According to <a href="https://blogs.msdn.com/ddcpxblg/archive/2006/07/17/668502.aspx">this Microsoft blog</a>, Microsoft will release <strong>Service Pack 1 for VS.NET 2003</strong>. The beta has been out for some time and finally they decide to release it on 15 Aug 2006, if no futher delay introduced.<br /><br />This is certainly good news for me, who is still using VS.NET 2003. I have been struggling in the past few weeks because of the infamous "<em>Unexpected error creating debug information file ... The process cannot access the file because it is being used by another process</em>". In my case, <strong>aspnet_wp.exe</strong> process is locking the PDB file, so everytime I want to build the solution I need to kill the process manually. Furthermore, whether this is related or not, my VS.NET debugger is not working properly. When I mouseover a variable, the value does not come correctly. The value will appear much later after I traverse down several line of codes, so the QuickWatch and Watch become useless. This problem is not yet solved until now although I already took drastic measure to uninstall and install VS.NET.<br /><br />Finger crossed VS.NET 2003 SP1 will fix those problems, otherwise I have to live with it until the next upgrade to VS.NET 2005 :(Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1152766778588423592006-07-13T12:52:00.000+08:002006-07-14T13:58:04.763+08:00Web server and database server time differenceTime difference between web server and database server can cause hard-to-find bugs. This is what I experienced recently after deploying a web application to a live server. Unlike our development server where IIS and SQL Server reside in one machine, in the live server we have a SQL Server sitting in one machine and IIS sitting in another machine.<br /><br />For some reasons, time syncronization in both servers (Windows 2003) did not work and as a result there was significant time difference between the two servers.<br />Because of the time difference, application features that depend on comparison between current time and stored datetime value will not work properly. For example, when I save User's password expiry date, I call DateTime.Now from the application (using the web server's time) and save the value to the database. In the stored procedure, I check if a password is expired using statement like:<br /><div class='code'><br /><font color='darkgreen'>-- check if password is expired<br /></font><font color='blue'>IF </font><font color='black'>@PwdExpDate <</font><font color='blue'>= GETDATE</font><font color='black'>()<br /> </font><font color='darkgreen'>-- password is expired<br /> </font><font color='blue'>ELSE<br /> </font><font color='darkgreen'>-- password is still valid<br /><br /></font><br /></div><br />Since Password Expiry Date is set in the web server and then compared in the database server, this comparison does not work properly due to the time difference.<br /><br />Although fixing time difference between the two servers is easy, I started to think whether we can always safely assume that both the web server and database server has the same time. Or do we need to introduce a programming guideline here:<br /><br />"Datetime that is set in one machine can only be safely compared to the current time from the same machine"<br /><br />In my case, the above guideline implies to moving the comparison logic from stored procedure to the application layer, or the other way around, setting the expiry date at the stored procedure and perform the comparison in the stored procedure as well.Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1151641339819781802006-06-30T12:11:00.000+08:002006-07-14T13:52:31.656+08:00Ordering Category in CodeSmithWhen I worked on a CodeSmith template (*.cst), I came into a small problem to order categories in a property grid. The template I created has several categories and I want it to be displayed in a specific order, not ordered alphabetically as per default.<br /><br />I came across <a href="http://www.thescripts.com/forum/thread214456.html" target="_blank">this discussion</a> that gave me a nice trick to order the categories. We need to prefix the category name with a special character that does not get displayed by Property Grid. Aab character (\t) will do the trick. Multiple tab characters might also be used. So for example if I want to order my categories in the following order:<br /><br />Context<br />Persister<br />BusinessEntity<br />UnitTests<br /><br />Then in CodeSmith template I need to write like:<br /> <div class='code' style="height:125px"><br /><span style='background-color:#ffff99'><nobr><font color='blue'><</font><font color='maroon'>%@</font><font color='red'> Property Name</font><font color='blue'>="SourceTable"</font><font color='red'> Type</font><font color='blue'>="SchemaExplorer.TableSchema"</font><font color='red'> Category</font><font color='blue'>="\t\t\tContext"</font><font color='red'> %</font><font color='blue'>></font><font color='black'></font></nobr></span><br /><span style='background-color:#ffff99'><nobr><font color='black'></font><font color='blue'><</font><font color='maroon'>%@</font><font color='red'> Property Name</font><font color='blue'>="CreateBusinessEntity"</font><font color='red'> Type</font><font color='blue'>="System.Boolean"</font><font color='red'> Category</font><font color='blue'>="\t\tBusinessEntity"</font><font color='red'> Default</font><font color='blue'>="True"</font><font color='red'> %</font><font color='blue'>></font><font color='black'></font></nobr></span><br /><span style='background-color:#ffff99'><nobr><font color='black'></font><font color='blue'><</font><font color='maroon'>%@</font><font color='red'> Property Name</font><font color='blue'>="CreatePersister"</font><font color='red'> Type</font><font color='blue'>="System.Boolean"</font><font color='red'> Category</font><font color='blue'>="\tPersister"</font><font color='red'> Default</font><font color='blue'>="True"</font><font color='red'> %</font><font color='blue'>></font><font color='black'></font></nobr></span><br /><span style='background-color:#ffff99'><nobr><font color='black'></font><font color='blue'><</font><font color='maroon'>%@</font><font color='red'> Property Name</font><font color='blue'>="CreatePersisterUnitTest"</font><font color='red'> Type</font><font color='blue'>="System.Boolean"</font><font color='red'> Category</font><font color='blue'>="UnitTest"</font><font color='red'> Default</font><font color='blue'>="True"</font><font color='red'> %</font><font color='blue'>></font><font color='black'></font></nobr></span><br /> </div>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1150375136295722832006-06-15T20:37:00.000+08:002006-07-15T12:42:35.556+08:00Ajax and Auto SaveThe following article summaries our experience in working with auto save feature in the web application. New technology and new features always bring fresh challenges to developers and also good things for users, but they may also raise new issues that have never come up before. I hope by sharing this experience with you, you will be more aware with the issues while working on similar feature.<br /><hr /><br /><p>One of the recent challenge that my team had was to handle session timeout issue when our users spend too much time on a web form. As a background, a user session will time out when there is no communication (client request) with the server for a certain period. When time out happens, the user has to log in again and this action potentially destroys any unsaved changes the user has made on the web form.</p><br /><p>We don't favor to increase the session time out, since this solution imposes a greater risk to our application. What we need is a feature or two that can handle session time out gracefully.</p><br /><p>We come up with the idea of auto save after experiencing GMail for a while. The auto save feature has been in Microsoft Word as long as I can remember, so we often take it for granted, but it is a new and sexy feature for the web application.</p><br /><p>In case you haven't seen how auto save works in GMail, this feature runs silently in the background. It will detect changes in the email content and periodically send the content to the server for saving. A short, non obstructive message will appear to indicate that the auto save is done.</p><br /><p>So we made our mind to implement auto save in our web form. The web form is much more complex that Gmail's form. In the form, we have about 25 fields (textbox, dropdownlist, textarea) and a rich text box that can contains HTML.</p><br /><p>Briefly this is what we did: </p><br /><ol><li>A timer in set in Javascript that will invoke a function called saveData() every n-minutes.</li><br /><li>saveData() calls a home-grown Javascript form utility to extract the values of all fields into XML.</li><br /><li>We use AJAX to send the XML to the server.</li><br /><li>The server receives the XML and based on the status of the data does either one of the following: <ol type="a"><br /><li>If the user does not explicitly save the data, we save the XML into a table that is created for the sole purpose of saving data temporarily.</li><br /><li>If the user has explicitly save the data before (for example, by clicking on the save button), we deserialize the XML into business object, and use the data access layer (DAL) class to save the data into proper tables.</li><br /></ol></li><br /><li>Upon completing the operation, the server returns a return status and the javascript displays the auto save message.</li><br /></ol><br /><p>In my observation, it is important that the auto save message does not distract from the user's focus. We don't want the user to lose focus on the fields he/she is working only for the sake of auto save. The auto save feature should be made as transparent as possible.</p><br /><p>Point 1-5 above is what we did initially with the auto save. When we used this feature for a while, we noticed that the audit log was filling up fast. This is because we always save to the database regardless whether the data has been changed.</p><br /><p>We have two options to solve the problem. First option is to compare the values of the fields in the client side and send the XML only if we find differences. The second option is to compare the values in the server side. While the second option requires less effort, we find that this solution has potential issues. As the value comparison is done in the server side, the client needs to constantly send the XML data to the server side. This will increase the traffic and make the server busy unnecessarily. It will also make the user session never expires, opening the application for further exploitation. The first option is indeed more challenging but it will create a more efficient traffic and the user session still works properly.</p><br /><p>Let's see the beauty of auto save in the following mini case study:</p><br /><p><i>A user logs in to the application, enters data in the form, but he hasn't press the save button. After n-minutes, the auto save triggers and saves the data to the temporary table. Suddenly, there is a network disruption and the user loses his session.</i></p><br /><p><i>After the network disruption is over, the user logs in to the application again and revisit the same form. This time the application checks the temporary table and finds the auto save data that belongs to the user. The application pops up a message, informing the user whether he/she want to recover the data or continue with the blank form. If the user chooses to recover the data, the auto save data is loaded and populated to the form. However, if he chooses to start with blank form, the auto save data is deleted.</i></p><br /><p>Giving options to recover data or to start with a blank form is a nice to have feature, because in some cases the lost data is not worth to recover.</p><br /><p>Does auto save solve session timeout issues? I can say partially. The session will still time out if the user does nothing, but with auto save the user will not lose all data. I have been thinking of another feature that can warn the user when the session is about to time out. This feature will complement auto save to make a really user friend form.</p>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com2tag:blogger.com,1999:blog-12497981.post-1148632628112791362006-05-26T16:15:00.000+08:002006-05-26T16:47:54.490+08:00Sharing Visio DiagramIn my current company, I create a lot of database and class diagram using Visio for Enterprise Architects (the one that comes with VS.NET 2003 Enterprise Architect) and share it to other people. The fellow developers who have VS.NET 2003 Professional always have problem viewing the file, because they don't have Visio installed in their machine.<br /><br />Installing <strong>Visio Viewer 2003</strong> won't help much. You can open Visio file from inside Internet Explorer, but the result is often unpredictable: contents of the entity diagram tends to overflow the table boundary, the line thickness is not correct, etc. Basically, I am not satisfied. The only solution is to print on paper and distribute, until now.<br /><br />I just discovered (doh!) that we can publish Visio diagram as web pages. The result is not much different as the original diagram viewed inside Visio. I do notice some minor defects like dashed line is converted to solid line. But so far the defects are insignificant.<br /><br /><a href="http://photos1.blogger.com/blogger/4/992/1600/visio.jpg"><img style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://photos1.blogger.com/blogger/4/992/320/visio.jpg" border="0" /></a><br /><br />In case you haven't discovered, to export Visio diagram to web page, just choose <strong>File</strong> - <strong>Save As Web Page</strong>.<br /><br />Inside the dialog box, you can set some parameters. In my observation, some options don't really affect the performance. I can't make the <strong>Custom Properties</strong> working as well, so I opt out this option to remove the empty space reserved for this feature.<br /><br />Visio will create a lot of html and vml file and pack them into a folder. I move this folder to our development web server and instantly everyone in the team can access the Visio diagram. The diagram is drawn using VML (Vector Markup Language) so it still looks nice even when we zoom in/out.Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1153200997621974612006-03-05T13:36:00.000+08:002006-07-25T13:13:03.963+08:00Managing ASHX filesIn the web application that I am working on, a web page is composed of several user controls. The composition happens in the runtime and is driven by metadata stored in a database. As reconstructing a web page during postback is quite expensive, I opted to <a href="http://eesamuel.blogspot.com/2006/02/why-i-use-out-of-band-ajax-requests.html">out-of-band AJAX requests</a> for all our asyncronous requests.<br /><br />Consequently, there is a growing number of ASPX created solely to handle AJAX requests in our web project. Initially, I created a folder called "Handlers", containing all ASPXs that handle out-of-band AJAX request, separating from the normal ASPX files. I also suffix the file name with Handler, like 'LookupHandler.aspx' to further separate between ASPX handler and normal ASPX.<br /><br />I am aware about ASHX as an option to handle out-of-band AJAX request instead of using ASPX. A lot of people say that ASHX is simpler, therefore it should run faster. I need to see a benchmark to support this though.<br /><br />Although ASHX seems to give some performance benefit over ASPX, I was initially discouraged to use this in our projects or even recommend this approach to my fellow developers. ASHX is not supported by VS.NET 2003. There is no file template to begin with, and the worst is there is no Intellisense support. This thought revolved around me until after some time I got spare time to do more experiment with ASHX.<br /><br />Not to my surprise, ASHX supports code behind. But the code behind just does not work as seamlessly as ASPX. I can't make the code behind file a child of the ASHX file in the web projects like:<br /><br /><pre><br />ContactHandler.ashx<br />|<br />+- ContactHandler.ashx.cs<br /></pre><br /><br />This does not work.<br /><br />What I can do is to structure the ASHX and code behind at the same level. Not as nice as the way ASPX is structured, but still acceptable.<br /><br />ContactHandler.ashx<br />ContactHandler.ashx.cs<br /><br />To have the ASHX working properly with code behind, we have to add class attribute in the declaration:<br /><br />ContactHandler.ashx only contains 1 line:<br /><br /><div class="code" style="height:60px"><nobr><br /><span style="color:#ffff99;"><span style="color:blue;"><</span><span style="color:maroon;">%@</span><span style="color:red;"> Webhandler language</span><span style="color:blue;">="c#"</span><span style="color:red;"> class</span><span style="color:blue;">="JLTi.iClaims.Handler.LookupHandler"</span><span style="color:red;"> %</span><span style="color:blue;">></span><span style="color:black;"></span></span><span style="color:black;"></span></nobr><br /></div><br /><br /><br />ContactHandler.ashx.cs contains the actual code to handle out-of-band AJAX request:<br /><br /><div class="code"><span style="color:blue;">using </span><span style="color:black;">System</span><span style="color:blue;">;<br />using </span><span style="color:black;">System.Web</span><span style="color:blue;">;<br /><br />namespace </span><span style="color:black;">Experiment<br />{<br /></span><span style="color:blue;">public class </span><span style="color:black;">ContactHandler : System.Web.IHttpHandler<br />{<br /></span><span style="color:blue;">#region</span><span style="color:black;"> IHttpHandler Members<br /><br /></span><span style="color:blue;">public void </span><span style="color:black;">ProcessRequest(HttpContext context)<br />{<br />context.Response.Write(</span><span style="color:#808080;">"Hello World from ASHX"</span><span style="color:black;">)</span><span style="color:blue;">;<br /></span><span style="color:black;">context.Response.End()</span><span style="color:blue;">;<br /></span><span style="color:black;">}<br /><br /></span><span style="color:blue;">public bool </span><span style="color:black;">IsReusable<br />{<br /></span><span style="color:blue;">get<br /></span><span style="color:black;">{<br /></span><span style="color:blue;">return true;<br /></span><span style="color:black;">}<br />}<br /><br /></span><span style="color:blue;">#endregion<br /></span><span style="color:black;">}<br />}</span><br /></div><br /><br />A better way to manage ASHX is to put the code behind file in another project (a class library) like:<br /><br /><pre><br />Experiment.Handler<br />|<br />+-- ContactHandler.cs<br /><br />Experiment.Web<br />|<br />+-- Handlers<br /> |<br /> +-- ContactHandler.ashx<br /></pre><br /><br />In the above example, the ASHX file is put in the folder 'Handlers' inside the web project and the code behind file is put in a separate class library project. By structuring this way, we can easily version, share, and reuse the code in the code behind among several solutions.Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1140854471660290512006-02-25T16:00:00.000+08:002006-07-18T13:36:02.323+08:00Why I use out-of-band AJAX requestsIn current project, I use a lot of out-of-band AJAX requests. In an out-of-band request, individual request does not flow into its own standard ASP.NET page life-cycle but instead calls another page and follows the flow of the corresponding page.<br /><br />There has been a growing debate regarding the pros and cons of out-of-band request. In the cons side, the out-of-band request breaks ASP.NET model. Developers do not code in the usual manner they are used to do for years. Instead they have to create another page to server AJAX request. In my company, we call this page as 'handlers' or 'AJAX handlers', or simply 'AJAX servers' to less-technical people :) Whatever the name is, programming this handler is usually more raw and messy since we have to let go some nice ASP.NET features like ViewState that makes web programming easier and more intuitive (more like an event-driven programming).<br /><br />In the pros side, the out-of-band request is more efficient since it only carries data that the handler need. It does not need to carry hefty payload from ViewState. The server side processing is also more efficient since it does not to reconstruct the state of the whole control hierarchy. Moreover, the handler also promote reusability and clear separation of responsibility, since a handler's only responsibility is to provide correct response based on received request. It does not need to know how the UI is rendered. Thus a handler can be used by several UIs.<br /><br />So which kind of request to choose? It really depends on how you structure the content of the page. In a common ASP.NET project where one screen in the specification translates into one ASPX page, then you can safely avoid out-of-band request. Microsoft ATLAS does this. The programming model does not change dramatically.<br /><br />However, the moment you want to promote reusability, you will start using ASCX (user controls) inside the ASPX and later, to promote even further reusability, use custom controls. In this case, the out-of-band request is a better option (and perhaps the only way to implement AJAX request). Consider that it is too expensive to reconstruct the whole page (and reinstantiate all user controls/custom controls in the page) only to serve a single AJAX request.Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1140703505489036982006-02-23T21:53:00.000+08:002006-02-25T15:58:29.946+08:00Visual Studio 2005 LicensingThis afternoon, we went to Microsoft office to have a discussion with Rashish Pandey, Product Marketing Manager (Developer Tools) for Microsoft Singapore. The agenda of the meeting is to discuss various options to buy Visual Studio 2005 for our plan to migrate to Visual Studio 2005.<br /><br />Noted below are what I extracted from the discussion plus a few hours of surfing Microsoft site to seek further clarification. Bear in mind that those notes are my personal opinion and should not be taken as it is. If you are in the position of evaluating VS.NET 2005 licensing as well, I suggest to start from Microsoft site then seek more explanation from Microsoft representative.<br /><hr /><br />To begin with, Visual Studio 2005 licensing scheme is per user/developer basis, not per installation basis. Simply speaking, if there are 20 developers, then we need 20 licenses to adequately cover all the usage, regardless on how many instances of Visual Studio is installed.<br /><br />VS.NET 2005 comes in 4 editions: Express, Standard, Professional, and Team System. Visit <a href="http://msdn.microsoft.com/vstudio/products/compare/default.aspx">Visual Studio 2005 Product Feature Comparisons</a> for more comprehensive comparison among those editions. In my opinion, the Express and Standard editions are more suitable for hobbyist or individual developer working at home rather than for enterprise use. Both editions come bundled with SQL Express Edition, so we know how Microsoft positions these editions. Surprisingly, both Express and Standard editions support SQL Reporting Service, so theoretically they can be used to create and publish reports to SQL Reporting Service.<br /><br />Beginning from the Professional Edition and up, there are SQL Server 2005 integration and XML/XSLT editor, two major features which are missing in the first two editions. Microsoft positions the Professional edition for individual developers, but I believe in practice due to overwhelming features and high price tag of the Team System edition, most companies will stick to the Professional edition.<br /><br />In the high end side of the product line is Visual Studio Team System, which consists of 4 products offered in 5 different packaging. The products are Architect, Developer and Team Tester, each one targets specific role in the software development lifecycle, plus the Team Foundation Server to enable collaboration among those roles. You can buy the Team System Suite which consists of 3 Team System, one copy for each role, bundled together. The Team Foundation Server is sold separately and will be available in March 2006.<br /><br />Users connecting to the Team Foundation Server needs a license known as CAL (Client Access License). Every individual Visual Studio Team System edition comes with 1 CAL, which means the user automatically has license to access the Team Foundation Server. The Professional edition does not include CAL, so you need to buy a CAL if you want the developer using the Professional Edition to use the Team Foundation Server.<br /><br />Microsoft has a scheme called Software Assurance, which entitles you for free upgrade to the next version of the product as long as you has a valid subscription for the corresponding product. In VS.NET 2005, MSDN Subscription is a superset of Software Assurance. Other than offering free upgrade to the future release of Visual Studio, it also gives you phone-based support, newsgroup support, and a bundle of Microsoft operating systems, server products, betas, etc. licensed for development and testing only (Developer Edition). In my view, this is the most important benefit of MSDN Subscription. Developers can try various Microsoft products and run their applications in various environments without needing to buy licenses.<br /><br />The Team System edition with MSDN Subscription bundle comes with a 5-user-limited edition of the Team Foundation Server called 'Workgroup Foundation Server'. This product is functionally equivalent to the Team Foundation Server, but it is limited for 5 users only. IMPORTANT NOTE: You cannot buy extra CALs to increase beyond 5 users.<br /><br />Finally, there is a downgrade licensing scheme available for VS.NET. It means you can buy VS.NET 2005 to license your VS.NET 2003 installation. It sounds uncommon, but it might be useful in the situation where you still have projects in VS.NET 2003, not quite ready to jump to VS.NET 2005, and need more licenses to cover additional developers.Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1140098529910602752006-02-16T21:59:00.000+08:002006-02-22T13:14:09.703+08:00Separation of RolesAs a software developer, sometimes I come into situation when I have to code in all layers/tiers of the application. In the web development context using Microsoft technology (where most of my experience is in), it means writing code in Javascript, deal with divs and tables in HTML, write business logic in C# and all the way down to writing stored procedures. Working in this manner is nothing bad, since I can get the full picture of the process, from the moment the user input the data to the data being saved in the database. Quite surprisingly, this happens in most of the companies that I have worked for, regardless of the project size.<br /><br />The multi-responsibility role that a developer has to bear is quite common nowadays. Take a peak at the online job posts and you can easily notice that most developer job openings always look for the all-rounder candidate who can do from A-Z and has experience working in all tiers.<br /><br />I agree in a small development project, we don't have the luxury of proper design and planning. Thus work items are actually screens from a prototype, and often a single developer is assigned to code the screen from UI tier to database tier. However, when the project gets larger and more developers join in, I suggest developers are split into several distinct roles:<br /><br />1. UI/Front-end developers. These people are most experienced in event-driven nature of UI programming. In the web development projects, these are the type of developers who are fluent in client side scripting, prefer to hand code HTML code, fully understand the difference between a listbox and a dropdownlist, etc.<br /><br />2. Middle tier developers. These people deals with business object classes, web services, and data access layer classes.<br /><br />3. Database developers. They live in different side of the world than the other two types of developers. They speak only in TSQL. Their tools is Enterprise Manager and Query Analyzer instead of Visual Studio.<br /><br /><br />Those projects who have clear separation of roles enjoy the following benefits (which are derived from my experience working in such project):<br /><br />1. The right man on the right place. Almost like a cliche, but it is true. Most developers will say 'yes' they can work in every tier, but in fact they are more effective working in one tier compared to other tiers. A developer's past experience can tell much about this. Face this fact: developers who write well in object-oriented Javascript may not effectively write a business object classes in C#, or even write a sophisticated stored procedure in TSQL with proper error handling management.<br /><br />2. Promote separation of responsibility on each tier, an important concept in object orientation. Alhough only by a proper design a truly separation of responsibility can be achieved, having different developers on each tier will ensure there is no code that sit in the wrong place because they are written by different developers.<br /><br />3. Each tier can be planned and progressed independently. For example, after the database design is done, the database developers can start working on the stored procedures. Meanwhile, once the class diagram is done, the middle tier guys can start creating classes. Usually, the UI developers will start later and finish later as they have to work on the prototype and go through iterative release-feedback process with the client.<br /><br />4. Easier to implement programming standard and convention. Take this example: Naming convention for strongly-typed C# is different from loosely-typed Javascript. Syntax in TSQL are more effective in resultset, while C# developers are more used to loops.<br /><br />5. Promote communication. In my previous company, developers on the same role sit next to each other on the same corner, thus promoting communication and code reuse among them. In a 20-people team which everybody works on the stored procedures, not everybody know what other people have done.<br /><br /><br />As I mention earlier, the separation of roles may not be suitable for a small project where resource is limited. It is also not an all-good solution. The following are some disadvantages:<br /><br />1. Developers may know understand the whole picture as they only work in one tier instead of all tiers.<br /><br />2. Harder to track bugs and performance issues that run across tiers and other performance issues because of point no 1. If bug tracking is not manage properly, developers may start finger pointing on each other.Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1140007002320585032006-02-15T20:30:00.000+08:002006-02-19T21:59:13.900+08:00GPL vs LPGLThe company I am working on is heading for the annual audit process. Apart from the security and compliance issues that the internal auditor needs to find, the auditors will find whether we have unlicensed and underlicensed applications. Underlicense situation will rise if the current licenses we have do not adequately cover all the software installation or deployment.<br /><br />In our discussion, we come across several licensing schemes and surprisingly nobody has a clear understanding about them. Two most prominent licensing schemes: GPL (General Public License) and LGPL (Lesser GPL) seems to confuse many as they look similar but actually are very different.<br /><br />The GPL license allows free use and modification of the software, as long as we credited back to the original author and release the application (that utilizes the GPL'ed software) as open source. This is a major road block for commercial project to use GPL'ed software.<br /><br />On the other hand, the LGPL license does not require the application to be distributed as open source and thus can be used in a commercial application. This licensing is increasingly popular among software libraries. However, there is an extra complication: any modification or addition (and even wrapper) to the LGPL'ed library must also be released under LGPL/GPL scheme. Other parts of the software which do not use the library are not affected and therefore can be released under any licensing scheme. Therefore, in practice, if we create a wrapper class of the LGPL'ed library, or create a class that inherits from the library, then our library code must be released under LGPL scheme. In the .NET project, as a class library is compiled into an assembly, then the assembly and its source code must be released under LGPL. Not a pretty situation for a commercial project!<br /><br />In summary, whenever your software project needs to use a third-party library/application, make sure you understand its licensing very well and judge carefully whether it fits with the licensing of your software.Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com1tag:blogger.com,1999:blog-12497981.post-1139588728695870972006-02-10T23:33:00.000+08:002006-02-15T20:41:02.793+08:00A del.icio.us way to search<p>A simple idea turned into a great web application, that's what I like about <a href="http://del.icio.us/">del.icio.us</a></p><br /><p>Del.icio.us is an online bookmark repository. Instead of adding your favourite urls to the browser's bookmark, you can post them to Del.icio.us and access them from any computers on the web. Simple concept, but that's not all. You can also search what other people bookmark and see how popular those bookmarks are.</p><br /><p>Again, the power of virtual community is used here to leverage the usefulness of the site. The more people use Del.icio.us to post their bookmarks, the bigger the bookmark repository, and more useful search result can be returned. I believe Del.icio.us is waiting for that critical mass moment to happens, before they can truly reap the profit of running this service.</p><br /><p>Alas, Del.icio.us' infrastructure doesn't seem to cope well with the site growth. In the past few weeks, the site was down several times due to power failures in their colocation servers. These issues should be taken care seriously before people giving up and turning to alternative sites.</p><br /><p>Infrastructure issue aside, it is a great experience to use Del.icio.us on my daily surfing activity. I use the site to:<br /><ul><li>Manage my bookmarks. I surf at work and home, so having a single bookmark repository is really helpful. Assigning tags also helps me to categorize the bookmarks based on the keyword, which I find is easier to remember than file system-like structure in the classic bookmark system.</li><li>Share my technical bookmarks to my colleagues more easily. Just point them to my Del.icio.us url. No need to cut and paste the link in the email or IM. However, privacy is a big concern here because they can also get my personal bookmarks</li><li>Do search on the internet. See what other people bookmarking! Think Del.icio.us as an alternative to Google search.</li><li>Learn about the web interface... simple but powerful. More about this in the next post.</li></ul><p>Other that privacy issue, my other concern is if some people start to misuse the system by posting urls for advertising rather than their actual bookmarks. More and more people post the same url, the more popular the bookmark is. This will taint the usefulness of the site as an alternative way to search the Internet.</p>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1139582396116468802006-02-10T22:23:00.000+08:002006-02-11T00:28:31.966+08:00New Year, New Life, New ResolutionOne of my new year's resolution is to blog more actively. Looking back at my first post in April 2005, it was 10 months ago and there is only 5 messages so far. What a shame... I should do better this year!<br /><br />In 2006, I also start a new life, as a married man to a lovely wife Josepha. It has been about a month since we moved from Novena to an HDB flat in Braddell. Initially, the flat was pretty empty. Only basic furnitures like bed, L-shaped sofa, washing machines and fridges are left by the owner and previous tenant. But after a few trips to IKEA shop, we finally furnished our unit to a comfortable state.<br /><br />Now, I have a place to put the laptop and YES, we have a broadband Internet connection at home. So let's start blogging :)Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0tag:blogger.com,1999:blog-12497981.post-1119691665483430432005-06-25T17:27:00.000+08:002005-06-25T17:27:45.486+08:00Tech Ed 2005 WebcastsRecently, Microsoft released a series of pre-recorded webcast from Tech Ed 2005 in Orlando, Florida. The pre-recorded material is more suitable for people living in a very different time zone from United States, like me in Singapore. I don't need to wake up very early in the morning to join a live webcast broadcasted from US. The available webcasts are assorted selection of hands-on preview of the upcoming VS.NET 2005. Although we can discover ourself with Beta2 products, watching the experts can give us a more complete picture of what features are available and how they link together.<br /><br />I found watching webcast is a fun way to learn. It could not replace the experience of attending a live event by yourself where you have the opportunity to interact with the speakers, feel what other people are feeling (interesting, boring, sleepy, etc.) and of-course enjoy the snacks provided. :) With webcasts, you can skip the boring parts and just jump to the one you are interested. It is kind of attending a zipped-version of presentation.<br /><br />Link to <a href="http://www.microsoft.com/events/series/teched2005.mspx">Tech Ed 2005 Webcasts</a>Enrico Elizar Samuelhttp://www.blogger.com/profile/14655478052467148167noreply@blogger.com0