02 December 2009

Avoid Javascript if you can!

If you're developing a rich web control, then -- no two ways about it -- you are stuck using JavaScript. If you're developing a business web app, avoid JavaScript at (nearly) all costs.

Now before you assume that I'm an ignorant wretch who's never really used JavaScript, let me tell you this. I've done a bit of client-side in my time. Including: A Mac OS X widget written with no JS framework, a jQuery plugin for filtering search results (written for and included in jqGrid since 3.5), and lots of custom code for various projects using jQuery or Prototype.

This is what clinched it for me: When I was doing a refactor of a particular piece of my main work project, I decided to fully embrace jQuery and use its UI components, themes, plugins, etc. The long and the short of it is that I will never do this again.

I mean, it's cool that you can create a snazzy jQuery UI widget with a div and a line of javascript, but making that work with your server-side language requires javascript glue. Even with jQuery making things easier, I had to write an inane amount of JS code to glue everything together (check or toggle css classes, setup and handle events like clicking, make ajax requests). My application was a tad complicated, and this was not helped by the fact that I now had to debug both client and server code (and the server code being the much easier part). Sometimes it's fun to make your own glue, but it's a massive expense just to have a pretty client-side control.

The take-away from this is that as an app developer (not a control developer) it's best to avoid using client-side code (Javascript) when possible. I can get around this in ASP .NET for the most part by using pre-made AJAX server controls from the Ajax Control Toolkit, or by using normal server-side controls in UpdatePanels. UpdatePanels potentially add a good bit of overhead, but for internal projects (which is most of what I do) having slightly better performance is not nearly worth the time it takes to make high performance glue.

Well, that's my perspective anyway. ;)

Microsoft showers students with software

First I found out about DreamSpark. As long as you are enrolled in a participating college or high school and have an email address ending in .edu, you can download the following full versions of MS software (and Product Keys if they are needed) completely free:
  • Visual Studio 2008 Pro
  • Visual Studio 2005 Pro
  • Expression Studio 3
  • SQL Server 2008 Developer
  • Windows 2008 R2
  • Windows 2008
  • Windows 2003 R2
  • Robotics Developer Studio (Standard I assume?)
  • Other free software (Visual Web Dev Express, SQL Express, etc.)
No media is available. Most of these are for both 32 and 64 bit systems. 2008 R2 is only for 64 bit. (EDIT: The next bit is only if you try to install it on a Mac.) Burning the 2008 R2 IMG file requires special software beyond a standard ISO burner. On my first go, I got the 1. 2. prompt. I found this blog, which explain the why and how to fix it. It's a lengthy process, but I muddled through.

Then I found out about The Ultimate Steal where you can get more software from MS on the CHEAP. As long as you have a .edu email address, you can download ISOs, get CD keys, and get shipped the actual media for the following:
  • Media for all of these costs an additional $13.00 per product
    • The media is NOT the full retail package
    • The media takes a long time to arrive (a month perhaps)

  • Windows 7 Pro Upgrade (32 or 64 bit) - $29.99
    • You do NOT get both 32 and 64 bit versions. So know what you need beforehand. 32-bit should work for any machine that can run Windows 7. 64-bit is better if you have the hardware for it.

  • Office 2007 Ultimate - $59.95
  • Visio 2007 Pro - $55.95
  • Non-English language packs for Office/Visio - $9.95 each (no media available)
The agreement reads that if they discover you are not an enrolled student, they can charge you the difference between what you paid and full price. Just a fair warning.

The download process is more annoying than at DreamSpark. There are 2 things you can download. One of them is a downloader for the ISO, and the other is for the setup files themselves, presumably for an in-place upgrade. I couldn't finish the latter to find out since I was running XP (and 32-bit to boot). Regardless, at least get the ISO and burn yourself a DVD to keep, just in case!

After you download the small .exe file, you have to run it to start the download of the actual product. The Digital River downloader caused a lot of problems on my machine. I got out of buffer errors and the entire machine became completely unstable, lost network connection, wouldn't start programs, etc. Then when I shut down, it blue screened. I believe this was due to AVG. After I turned the real-time scanner off, the problems seemed to stop. After that fiasco, I uninstalled AVG and put Windows Security Essentials in its place. Just to test, I redownloaded the entire thing post-WSE with no problems.

AVG has generally been solid for me, but I guess it just couldn't handle the strain of scanning my 12 M/s download. Thumbs down to AVG.

Anyway, hope this helps all you students out there.

17 November 2009

Business and Data Layers

What follows in this entry is a way that works well for me to architect a Business Logic Layer (BLL) and Data Access Layer (DAL) into my projects. This may not be the best way, so any constructive feedback is welcome. I love to learn new and more efficient ways of doing things. (Read: I'm lazy, and if I can make it easier on myself, I will.)

Naming

DAL

I tend to like to organize my DAL in a structure similar to the database itself. The namespace ends up being something like:

com.project.DAL.DbName.TableName.Entity.Select.All()

com.project.DAL is the namespace for the DAL project
DbName.TableName is a folder structure
Entity is a class
Select is a nested class
All is a static method
(compare the latter two parts to doing a static method called SelectAll under the Entity class)

If there are specialized procedures that don't map to specific tables, I might make a "Procedures" class under the database folder to encapsulate those.

BLL

The BLL class, I tend to name based on functionality rather than database organization. Something like:

com.project.BLL.CRM.Customer.Get.All()

Interfaces

DAL

The DAL only returns DataTables or DataSets. I don't even bother with typed data sets because of the added hassle of regenerating the XML files each time I make a change.

Returning DataTables has a number of advantages.

For one thing, it's an abstraction over the database. I could even take DataTables pulled from completely different servers, put them in the same DataSet, and use DataRelations to query them in memory to get exactly what I need.

For another thing, if no extra business logic is needed (perhaps the table is only for display, not for editing), then I can skip creation of C# objects at the business layer and bind the DataTable from the DAL directly to web controls. Out of the box, DataTables already support all the niceties of web controls like paging and sorting.

BLL

The business layer objects are Plain Old CLR (C# or VB .NET) Objects (POCOs). They look something like this:
    public class Customer : IInitable  // my interface to make an Init method
{
public int customer_id { get; set; }
public string name { get; set; }
public CustomerTypeEnum type { get; set; }
public DateTime since { get; set; }
public List<History> HistoryEntries { get; private set; }

// note absence of constructor... CLR makes a default constructor this way

#region IInitable Members
public void Init()
{
... // set the HistoryEntries with something like this.HistoryEntries = History.Get.ByCustId(this.customer_id);
}
#endregion

public static class Get // nested class to handle fetch operations
{
public static List<Customer> All()
{
DataTable customer_table = DAL.DbName.Customers.Select.All();
List<Customer> ret_val = Converter.Fill<Customer>(customer_table);
return ret_val;
}
}
}
By convention, I use the same field names from the database as my object property names. That makes converting the data from the database to a C# object easy with reflection. I use a method similar to what I blogged about a while back that does this for me. Something like this:
    public static void Fill(object obj, DataRow row)
{
// get object's writable properties into dictionary by name
...
// loop through each column of the datarow
...
// if the prop name is the same as the column name
// check/handle: null values, enum types, type conversion
// assign obj to property
}
When I need to convert a whole DataTable into a list of objects, I wrote another convenience method:
    public static List<T> Fill(DataTable Table) where T: IInitable, new()
{ // where part means that T implements IInitable interface as has a default constructor
List<T> ret_val = new List<T>();
foreach (DataRow row in Table.Rows)
{
T obj = new T();
Fill(obj, row);
obj.Init(); // IInitable defines this method in case the object needs initialization after filling.
ret_val.Add(obj);
}
return ret_val;
}
IInitable is just a one-method interface that I wrote that makes the C# BLL class implement an Init method that returns void. Because I have to use empty constructors for uniformity, this Init method allows me to still initialize parts that would normally be initialized in the constructor, but after the data is filled in.

The CUD part of CRUD operations, at the BLL layer can take in an object and then use that object's properties for parameters of the DAL method.

However, to be really lazy, I am considering making an overload method for my DAL operations so that the BLL can just pass in the object directly and the overload method will use reflection to pull the properties out of the object and pass them into the method with typed parameters. I can probably encapsulate this with a separate static converter method so that the overload will essentially be a one-line call to the converter method (and therefore not much coding overhead), which invokes the real method. Once I get around to trying this, I'll post something about it.

Not that into NHibernate

I heard great things about Java's Hibernate, so I checked out NHibernate (Hibernate for .NET). After researching it a bit, I believe it will take more time and hassle to maintain than the current way I map data to objects. Here are my reasons.

Maintenance - 3 Sets of things to maintain
  1. C# Objects
  2. XML Mappings (something extra to learn)
  3. Database relations
Maintaining the XML doesn't have to be tedious, since there are tools that can generate it for you from the database schema. But most of the projects I work on are constantly evolving, and data changes are frequent. Assuming I remember to do it, that's a lot of regenerating XML (the same reason I stayed away from typed datasets) in addition to changing C# objects and database structures.

There are also things like NHibernate Mapping Attributes and ActiveRecord. These allow you to forego XML at first (although they ultimately recommend making XML files). But instead of learning XML, you have to learn the appropriate Attribute tags (although admittedly much less pain than the XML, especially ActiveRecord). Fluent NHibernate actually seems like the least pain, but you have to learn and use Fluent's coding conventions (which don't seem out of line) in order to get it to work properly.

Database Independence

This is probably the thing that NHibernate has going for it most. If you use the query language and the criteria language to get all your results, then you really could be connecting to just about any database.

The main issue I have with this is that NHibernate queries != stored procedure calls. For small projects that you don't expect to grow, stored procedures could be an overkill (but so could NHibernate!), but for projects of any complexity store procedures are the way to go. Stored procedure calls have a couple of major advantages, not the least of which being the object-oriented principle of encapsulation (or information hiding if you prefer). If you have a DBA, they can completely rewrite a procedure (for efficiency, say) and your program never knows the difference, which is a Good Thing. The other major advantage is performance as database procedures can be pseudo-compiled, depending on db platform, to make them run faster.

You can use NHibernate to call stored procedures, but the XML just specifies the native stored procedure call syntax ("exec proc_name" in MS SQL and "call proc_name()" in MySQL, for instance) -- there's no real database abstraction advantage there. Worse, if you need to run a procedure that doesn't return a result set, then you just have to make a native query call.

Conclusions

While I had high hopes for using NHibernate to make data access easier, it doesn't seem to accomplish that goal for me. It's an extra thing to learn and maintain, and has a number of drawbacks. Next article (who knows when that will be), I will show the way I architect data access. It might be a laughable thing to some of you (as if anyone reads my blog), but it's the easiest way I've found so far, while still having a lot of flexibility.

10 November 2009

ObjectDataSource TypeName and Nested Classes

When trying to use an ObjectDataSource with an method from my data access layer, I kept getting this error:



The type specified in the TypeName property of ObjectDataSource 'myODS' could not be found.



Long story short, the object I was using was actually a nested class. Even though you write you code like this:

MyNamespace.MyClass.NestedClass.Action();

The actual fully qualified type name for an ObjectDataSource TypeName will be "MyNamespace.MyClass+NestedClass" with a SelectMethod of "Action". If you visit this MSDN page and look down to where it talks about delimiters, you will see that nested classes use a plus as a delimiter for some reason instead of the usual dot.

05 October 2009

Crystal Reports 2008 V1 and MySQL

I have had the worst time with Crystal and MySQL. I tried MySQL's ODBC 5.1 drivers, but for me it didn't show any tables or views, even though I had all CR 2008 updates applied. Then I tried MySQL's ODBC 3.51 drivers, which showed tables, but when I didn't select any database, it would not show all databases... it would just show Add Command as the only option. That's not very useful since I have multiple databases. I'd have to create a connection for each one.



I finally ended up finding this page, and using the JDBC drivers. They aren't perfect, and they are a pain to install (editing config files), but they seem to work better than ODBC. I suppose Crystal just hates MySQL... which is ironic since that's the database they embed in their server product!



Getting Crystal Server 2008 setup... now that's a whole different (and painful) ball of wax. If I had this to do over again, I think I would have evaluated Crystal alongside a solution from Logi first. From the hassle I've had just to get a Crystal Reports Server 2008 solution working (and still not successful so far), I can only imagine it being better with Logi.

17 April 2009

Using Reflection to fill a Business Layer object

Getting tired of endlessly assigning properties for business objects, I decided to try to make life easier on myself. So I developed a handy class, which takes a DataRow and fills a Business Layer object with that data. So now every one of my business object constructors is as simple as this:




public MyItem(DataRow Row)
{
Converter.Fill(this, Row);
}


The class that does all the conversion is as follows:




using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;

namespace MyNamespace.Data
{
class Converter
{
public static void Fill(object LogicObject, DataRow Row)
{
Dictionary<string, PropertyInfo> props = new Dictionary<string,PropertyInfo>();
foreach (PropertyInfo p in LogicObject.GetType().GetProperties())
props.Add(p.Name, p);
foreach (DataColumn col in Row.Table.Columns)
{
string name = col.ColumnName;
if (Row[name] != DBNull.Value && props.ContainsKey(name))
{
object item = Row[name];
PropertyInfo p = props[name];
if (p.PropertyType != col.DataType)
item = Convert.ChangeType(item, p.PropertyType);
p.SetValue(LogicObject, item, null);
}
}

}
}
}


The full class for MyItem could look something like this:




using System;
using System.Data;

namespace MyNamespace.Data
{
public class MyItem
{
public int item_id { get; set; }
public string item_name { get; set; }
public DateTime item_expiry { get; set; }

public MyItem(DataRow Row)
{
Converter.Fill(this, Row);
}

public static MyItem Select(int item_id)
{
DataRow row;
// code to perform query and get row
...
return new MyItem(row);
}
}
}


In this implementation of Converter, your business object properties have to have the same names as the column names in the database. However, the Converter could be modified to also accept a Dictionary (or array of KeyValuePair) that provides a mapping from database column names to object property names.

26 March 2009

Why no one should ever again use Internet Explorer

The reason is simple: so the Internet can grow.

Web pages, which by my reckoning are the core use of the Internet, unfortunately don't magically spring up. A person has to create those pages. A person then has to maintain those pages. If a page feature is difficult to get working or is not supported in all browsers, then that feature gets abandoned. Thus, if a browser lags behind, and a lot of people are using that browser, then developers have to forsake features, or worse hack them in, to target the maximum audience.

This brings us to IE, which is by far the furthest behind the curve, both in terms of standards-compliance and features. Take a simple feature like rounded corners, which have been available in other browsers since IE was at version 6. For a developer to add rounded borders in FireFox, Chrome, and Safari is a couple of simple lines of styling. IE 7 and 8 releases passed by without this simple feature, and Microsoft's recommendations (put out in 2005, and sadly still relevant to IE8) on how to emulate this feature all complicate and bloat site design and maintenance.

Some developers will still choose to implement features like this. And as a result, they will spend a chunk of their time implementing and maintaining the hacked-in-for-IE feature rather than developing other features. The end result is that the users of the site lose out. All the time the developer wasted on adding in an IE-specific hack could have been put to a much more constructive use.

So far I've only mentioned absence of features. IE has a number of rendering problems that cause a developer to spend a lot of time trying to get a site to work in IE. All browsers have rendering issues, but IE is the very worst in my experience.

With the recent release of IE8, some of IE's hackiness will supposedly be eliminated, but it is still very far behind in terms of features.

So, from a developer standpoint, IE is severely limiting. Not only does it not have time-saving features that other popular browsers are supporting, but years will pass before they update the browser, and then they don't add much, or even get anywhere close to catching up to the state-of-the-art, from a developer perspective.

The best way for the web to move forward is for IE to be abandoned wholesale by users. Then developers can turn the time they would ordinarily waste hacking things into their code for IE into more time creating, innovating, and improving the web. If users dropped IE en mass, I believe that web developer productivity would increase by no small amount. Which in turn means that the end user has a better experience.

If you use a PC (IE is currently only available on PC), do yourself and the rest of the Internet a favor and use IE only once: to download some other browser. Firefox is probably the most popular. Safari is great if you like simple interfaces. Chrome is currently my favorite and is produced by Google.

27 January 2009

jGrowl

I started developing a web application that needs a sophisticated notification system. It turns out the actual notification message is the easy part since jGrowl does all the work for me.



Here's all you have to do to get jGrowl to work:



Download jGrowl.

Copy the 3 files into your web site directory:



jquery-1.2.6.js (or your newer or minimized version of jQuery)

jquery.jgrowl.js (or the minimized version)

jquery.jgrowl.css



(I put the 2 .js files in /scripts and the .css file in /styles.)



Link to the 3 files like so:



<link href="styles/jquery.jgrowl.css" rel="Stylesheet" type="text/css" />
<script src="scripts/jquery-1.2.6.js" type="text/javascript"></script>
<script src="scripts/jquery.jgrowl.js" type="text/javascript"></script>

Then within any Javascript file, you should be able to call jGrowl:



$.jGrowl("This is a jGrowl notification.");

Also, the notification text was blue for me, which didn't look to good on a black background. I fixed it by adding the following CSS to my main CSS file:



div.jGrowl div.message
{
color: #fff;
}