April 30, 2008 by
ryan
As mentioned in my last post, I've been starting to use NHibernate in some of my more recent projects. I checked it out years ago and I completely hated it (maybe becuase I was a newer developer -- not totally sure). More recently, however, I've realized some of the benefits of Domain Driven Design and thought its about time to give Hibernate another shot. I'm admittedly pretty new to Hibernate so any feedback would be appreciated!
Classes
public class ProductGroup
{
public virtual string ProductGroupID { get; set; }
public virtual string Title{ get; set;}
public virtual IList<SimpleProduct> Products { get; set; }
}
public class SimpleProduct
{
private IList<SimpleProduct> _relatedProducts;
public virtual string ID { get; set; }
public virtual string Title { get; set; }
public virtual string ImagePath { get; set; }
public virtual string Description { get; set; }
public virtual IList<SimpleProduct> RelatedProducts
{
get{ return _relatedProducts; }
private set { _relatedProducts = value; }
}
public virtual void AddRelatedProduct(SimpleProduct product)
{
if (_relatedProducts == null)
_relatedProducts = new List<SimpleProduct>();
_relatedProducts.Add(product);
}
}
We're going to start out with some very contrived classes (normally would start with tests but this is just for the hibernate concepts)... These classes should be pretty straight forward; they consist only of some basic properties and list methods.
Data Tables
Next, we need to create our tables to hold the data coming from the Classes -- I've created a product table / product group table and a lookup table for both (there are many-to-many references in both SimpleProduct and ProductGroup).
CREATE TABLE [dbo].[SimpleProducts](
[ProductID] [char](32) NOT NULL,
[Title] [nvarchar](50) NOT NULL,
[ImagePath] [nvarchar](300) NULL,
[Description] [nvarchar](500) NULL
)
CREATE TABLE [dbo].[RelatedProductsLookup](
[ProductID] [char](32) NOT NULL,
[RelatedProductID] [char](32) NOT NULL
)
CREATE TABLE [dbo].[ProductsProductGroupsLookup](
[ProductGroupID] [char](32) NULL,
[ProductID] [char](32) NULL
)
CREATE TABLE [dbo].[ProductGroups](
[ProductGroupID] [char](32) NOT NULL,
[Title] [nvarchar](50) NULL
)
You will want to set the Primary Key of the SimpleProduct and Product Groups table to be the ID.
Hibernate Mappings
Next we're on to the Hibernate mapping files. These files link the columns in the database to the fields/properties in the domain classes. Please check the project documentation for more detailed information on the Hibernate Schemas.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="ProductModel" assembly="ProductModel" default-lazy="false">
<class name="ProductGroup" table="ProductGroups">
<id name="ProductGroupID">
<column name="ProductGroupID" sql-type="char(32)" not-null="true" />
<generator class="uuid.hex" />
</id>
<property name="Title">
<column name="Title" length="50" not-null="true" />
</property>
<bag name="Products" table="ProductsProductGroupsLookup" lazy="false">
<key column="ProductGroupID" />
<many-to-many class="SimpleProduct" column="ProductID" />
</bag>
</class>
</hibernate-mapping>
Inside the hibernate mapping tag, we have an element called class where we're defining the relationship between the class and the table in the database. From there we're defining the properties -- Most of this should be pretty straight forward but there are a couple things I would like to focus on.
<id name="ProductGroupID">
<column name="ProductGroupID" sql-type="char(32)" not-null="true" />
<generator class="uuid.hex" />
</id>
This node is defining the unique ID for the class -- the ID is being created for each item and looks a little something like this: 46abbefc08d14b49a5d15c8a4dd69ff2
<bag name="Products" table="ProductsProductGroupsLookup" lazy="false">
<key column="ProductGroupID" />
<many-to-many class="SimpleProduct" column="ProductID" />
</bag>
</class>
Here we're defining a many-to-many relationship with Products -- this is bumping up against the lookup table we defined earlier to get all SimpleProducts associated with this class. currently the bag is defining a many-to-many relationship. We're going to assume that a product can exist under any number of product groups -- for instance if we have a video game system it could be under both the Home Entertainment and Electronics product group. See the project documentation for other types of relationships (one-to-many, etc).
SimpleProduct Mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="ProductModel" assembly="ProductModel" default-lazy="false">
<class name="SimpleProduct" table="SimpleProducts">
<id name="ID">
<column name="ProductID" sql-type="char(32)" not-null="true" />
<generator class="uuid.hex" />
</id>
<property name="Title">
<column name="Title" length="50" not-null="true" />
</property>
<property name="ImagePath">
<column name="ImagePath" length="300" not-null="false" />
</property>
<property name="Description">
<column name="Description" length="500" not-null="false" />
</property>
<bag name="RelatedProducts" table="RelatedProductsLookup" lazy="false">
<key column="ProductID" />
<many-to-many class="SimpleProduct" column="RelatedProductID" />
</bag>
</class>
</hibernate-mapping>
Accessing The Data
Now that we've defined all of our mappings its time to create some methods to access our data.
public class ProductGroupRepository
{
private ISession _session;
public ProductGroupRepository(ISession session)
{
_session = session;
}
public ProductGroup GetByTitle(string Title)
{
return _session.CreateCriteria(typeof(ProductGroup))
.Add(Expression.Eq("Title", Title))
.UniqueResult<ProductGroup>();
}
public IList<ProductGroup> List()
{
return _session.CreateCriteria(typeof(ProductGroup))
.List<ProductGroup>();
}
}
The ISession is the Hibernate object to use when accessing the data -- from what it looks like, these should pretty much line up with a unit of work (More on that here).
In the other methods of the class, we first need to define what type of object we're looking for. In case it's not obvious, we're specifying that in the CreateCriteria section. In the List() method, we're returning a list (of ProductGroups) -- there are no filters or other criteria defined for this operation. In the GetByTitle() method, however, we're stating that the Title for the product must match the Parameter 'Title'.
Demo Application
I've created a quick demo application using MVC and NHibernate. As I said, I'm still very new to hibernate so there's a lot I'm trying to learn (for instance where the ISession should initially be created and closed in an MVC application). Please let me know of any way that this could be better -- generally I'm writing this to help solidfy my thoughts on the technology, help others (hopefully) and also improve from others feedback.
If you have tortoise or some other svn client, you can grab the code from http://svn2.assembla.com/svn/NHibernateTest/ -- You will need to change the database connection to whatever your db is. Again I would like to reiterate, it's demo code -- not even close to anything that I would ever use in production but worked out okay for testing.
Disclaimer: Use this code at your own risk.
April 29, 2008 by
ryan
I've recently become interested in using NHibernate for some of my data access. For the past year or
so I have been using SubSonic for that, however, I'm trying to take a bit
more of a domain driven approach on the project I'm currently messing around on. Although I've only been looking at it for a couple of days, one thing I'm having a little bit of trouble figuring out is where my ISession should be opened / closed in an ASP.NET MVC application. Currently, I'm doing this in the Controller but I'm not totally sure that is the right way to go...
public ActionResult ViewProduct(string ID)
{
using (ISession session = SessionManager.GetCurrentSession())
{
SimpleProductRepository repo = new SimpleProductRepository(session);
return RenderView("ViewProduct",
new SimpleProductRepository(session).GetByTitle(ID));
}
}
What is the best way to go about this? Also, should I be looking more at using a
Unit of Work pattern to achieve this?
April 14, 2008 by
ryan
If you have not seen one of the other 5 million postings on this, there is now a
security patch available for
BlogEngine.NET. This weekend an issue was reported where an attacker could obtain the username and password of the site. Thanks
BlogEngine.NET team for the quick fix on this!
April 12, 2008 by
ryan
Last time I posted on the latest browser from Microsoft vs. Mozilla's latest I had compared page download times between the two browsers. This time, I would like to take a brief look at how they stack up in regard to JavaScript.
John Resig (of jQuery / Mozilla fame) announced the Dromaeo: Javascript Performance Testing suite yesterday. The suite runs some of the same tests as the WebKit team's SunSpider, however, there are some improved methodolgies (see the suite's wiki for more info on this). I don't know how often I feel like doing DNA Sequence Alignment in JavaScript but I think it would be a good way to quickly get some metrics of how both FireFox and IE8 handle the code.
The Comparison:
I ran the tests in groups because there were certain ones that were not finishing under IE8. To be quite honest, I'm not 100% sure if this is an Internet Explorer issue, or an issue with the tests. Both are a work in progress so I would rather not make assumptions at this point. Anyways, the results of the tests are listed below:
Test Name
|
Firefox 3.0 beta 5 |
Internet Explorer 8 |
| Fannkuch v122 |
802.80ms |
1032.20ms |
| Base 64 Encoding and Decoding v122: |
2942.00ms |
Would not finish |
| DNA Sequence Alignment v116: |
420.00ms |
Would not finish |
| N-Body Rotation and Gravity v122: |
270ms |
529.40ms |
| Prime Number Computation (2) v122: |
305.80ms |
518.20ms |
| Recursive Number Calculation v122: |
184.40ms |
417.80ms |
| Traversing Binary Trees v122: |
156.80ms |
422.20ms |
The Results
The initial results look like Firefox is performing the more advanced Javascript operations faster than Internet Explorer. That being said, Dromaeo, IE8 and Firefox 3.0b5 are still being developed so a lot could change before the release. For more metrics view a comparison on the Dromaeo site of Safari 3.1, Firefox 3.0b5, Opera 9.5 and IE8b1. Let me know what you think or if you've encountered different results.
April 10, 2008 by
ryan
Gravatar recently made some
big changes when migrating the site from Ruby on Rails to PHP. While most of these changes are a real benefit, there is one big feature that has been removed --
PNG files are no longer supported! Initially, I was sure it had to be a fluke -- why would someone want to remove PNG files for something like this? However, upon contacting support, my fears were confirmed (email pasted below):
"Gravatar.com has been redesigned to use JPEG images only. There are
certain (still in widespread use) web browsers which do not support PNG
files (which would allow for transparent backgrounds.) I would suggest
Adding a background color to your image, and re uploading it."
First off I want to
emphasize I really appreciate this service that
Gravatar is providing; it has always worked well and come on, it's free! That
being said, it is somewhat concerning that the support for PNG files has been dropped for
legacy browsers.
While PNG files may not look pretty on IE6, its not an issue that will prevent
IE6 (or other legacy browser) users from viewing the site. Moving web
standards forward is important! As web developers, I believe its our
responsibility to let the end-user know that their overall experience
may be better with a newer (standards-compliant) browser. Furthermore,
removing the ability to use PNG
files for legacy browser support is somewhat harmful! Imagine if iTunes
stopped selling MP3s because people on Walkman's couldn't listen to
them -- no real technological advancement would be made for fear of
alienating some users.
Ultimately, the decision to support PNG files is up to
Automattic (the creators of
Gravatar). My hope, is that they would allow the
Gravatar users to pick the image format of choice while educating their users that PNG files may not show up as expected for some people.
What do you think should be done? Should PNG files be supported by Gravatar? Please leave a comment below -- or continue this discussion on your site... this is an important issue for Web Standards.
April 2, 2008 by
ryan
After reading Steve Souders' article on IE8 speed enhancements, I became curious about how page download times in IE8 stacked up against the latest beta of Firefox (3.0b5). I realize that both of these browsers are in beta so the release product may be signficantly different. Also, please keep in mind these results are not very scientific (obviously). To obtain download times, I used Fiddler for Internet Explorer and Firebug for Firefox.
First off, I loaded a several higher volume websites to compare page download times. Each page was loaded three times (for extreme statistical accuracy :P) and the average load time was calculated.
Google News
http://news.google.com
0 Javascript Requests - 0 CSS Requests - 32 Images
| Firefox: |
1.08 Seconds |
| IE8: |
2.43 Seconds |
Amazon
http://www.amazon.com
12 Javascript Requests - 2 CSS Requests - 96 Images
| Firefox: |
5.59 Seconds |
| IE8: |
4.86 Seconds |
Mac.com
http://www.mac.com
15 Javascript Requests - 6 CSS Requests - 44 Images
| Firefox: |
5.74 Seconds |
| IE8: |
4.52 Seconds |
Finally, I ran Steve Souders' Parellel downloads test; the results were almost identical.
| Firefox |
 |
| Internet Explorer |
 |
It was really odd to me that Google News loaded almost two times as fast on Firefox while all the other sites loaded close to the same speed or slightly faster on Internet Explorer. The only difference I notice off the bat is the number of JS/CSS files; maybe its something to do with that? It would be very interesting to see how the browsers compare when rendering pages. Have you found results similar to this or know of a good way to test render time? Let me know what you think.