17 November 2009

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;
}

24 September 2008

More Navigation

I recently designed a navigation bar that meets the following criteria:

Cross-browser support (IE, Chrome, Firefox)
Scriptable (add/change links by only changing HTML, can create an ASP .NET or other server control with it)

The design I came up with looks like this:



The images are replaceable to match whatever theme. Sizes and so forth can be adjusted in the CSS.

Here's the HTML layout:

<div class="subNav">
  <div class="line"></div>
  <table class="links" cellpadding="0" cellspacing="0">
  <tr><td>
    <a href="#">
      <span class="left"></span>
      <span class="mid-wrapper">
        <span class="mid"></span>
        <span class="text">Text</span>
      </span>
      <span class="right-mid"></span>
    </a>
  </td><td>
    <span class="divider"><span>
  </td><td>
    <a href="#">
      <span class="left-mid"></span>
      <span class="mid-wrapper">
        <span class="mid"></span>
        <span class="text">Text</span>
      </span>
      <span class="right-mid"></span>
    </a>
  </td><td>
    <span class="divider"><span>
  </td><td>
    <a class="selected" href="#">
      <span class="left-mid"></span>
      <span class="mid-wrapper">
        <span class="mid"></span>
        <span class="text">And More Text</span>
      </span>
      <span class="right"></span>
    </a>
  </td></tr>
  </table>
</div>


It is a lot of HTML, compared to some other ways to do it, but it doesn't have any weird, oversized target areas, and it is very scriptable.

Here's the CSS (for transitional doctype):

.subNav
{
  position: relative ;
  width: 100% ;
  height: 32px ;
  text-align: center ; /* centers in IE */
}

.subNav .line
{
  position: absolute ;
  left: 0 ;
  top: 15px ;
  height: 1px ;
  border-top: 2px solid #aaa ;
  width: 100% ;
}

.subNav .links
{
  position: relative ;
  top: 0 ;
  margin: 0 auto ; /* centers in firefox and chrome */
  text-align: center ;
  vertical-align: bottom ;
}

.subNav .links a
{
  position: relative ;
  display: block ;
  text-decoration: none ;
  color: #000 ;
  height: 32px ;
  padding: 0 4px;
}

.subNav .links a:hover
{
  cursor: pointer ;
}

.subNav .links a .left
{
  position: absolute ;
  display: block ;
  left: -4px ;
  width: 8px ;
  height: 32px ;
  background: url(subNav-grey-left.png) no-repeat ;
}

.subNav .links .selected .left
{
  background: url(subNav-blue-left.png) no-repeat ;
}

.subNav .links a:active .left
{
  background: url(subNav-blue-left.png) no-repeat ;
}

.subNav .links a .right
{
  position: absolute ;
  display: block ;
  top: 0 ;
  right: -4px ;
  width: 8px ;
  height: 32px ;
  background: url(subNav-grey-right.png) no-repeat ;
}

.subNav .links .selected .right
{
  background: url(subNav-blue-right.png) no-repeat ;
}

.subNav .links a:active .right
{
  background: url(subNav-blue-right.png) no-repeat ;
}

.subNav .links a .left-mid
{
  position: absolute ;
  left: 0 ;
  top: 0 ;
  display: block ;
  width: 4px ;
  height: 32px ;
  background: url(subNav-grey-mid.png) repeat-x;
}

.subNav .links .selected .left-mid
{
  background: url(subNav-blue-mid.png) repeat-x;
}

.subNav .links a:active .left-mid
{
  background: url(subNav-blue-mid.png) repeat-x;
}

.subNav .links a .right-mid
{
  position: absolute ;
  right: 0 ;
  top: 0 ;
  display: block ;
  width: 4px ;
  height: 32px ;
  background: url(subNav-grey-mid.png) repeat-x;
}

.subNav .links .selected .right-mid
{
  background: url(subNav-blue-mid.png) repeat-x;
}

.subNav .links a:active .right-mid
{
  background: url(subNav-blue-mid.png) repeat-x;
}

.subNav .links a .mid-wrapper
{
  position: relative ;
  display: block ;
}

.subNav .links a .mid
{
  position: absolute ;
  display: block ;
  height: 32px ;
  width: 100% ;
  background: url(subNav-grey-mid.png) ;
}

.subNav .links .selected .mid
{
  background: url(subNav-blue-mid.png) ;
}

.subNav .links a:active .mid
{
  background: url(subNav-blue-mid.png) ;
}

.subNav .links a .text
{
  position: relative ;
  top: 8px ;
  font-size: 14px ;
  font-family: sans-serif ;
  line-height: 16px ;
  vertical-align: top ; /* makes it v-centered in IE */
  padding: 0 8px ;
}

.subNav .links .divider
{
  display: block ;
  height: 32px ;
  width: 1px ;
  background: url(subNav-divider.png) no-repeat ;
}


As you can see, everything to do with the layout is defined in the CSS, from sizes to images.