ActiveRecord and NHibernate

Posted by Jack Altiere on July 1st, 2008

In my first article in this series, I touched on using NHibernate and performed a few simple examples.  In the second, I added some complexity and introduced relationships, inheritance, etc.  In this article I want to see how we can use Castle’s ActiveRecord to potentially simplify things.

After downloading the Castle installation .msi from the website, I decided to just jump right in to the Quickstart guide to get things going.  Once again, I’ll be working with the AdventureWorks database if you want to follow along.  After creating a class library, I added a reference to these .dll’s to get things started:

  • Castle.ActiveRecord.dll
  • Castle.Core.dll
  • Castle.Components.Validator.dll
  • Castle.DynamicProxy.dll
  • NHibernate.dll
  • Iesi.Collections.dll
  • log4net.dll

    Next, I want to create a sample class.  It looks like using ActiveRecord is going to eliminate the need for me to hand write the .xml mapping files that I had to create when I used NHibernate by itself, which is a plus.  I decided to stick with the same example I used in my first NHibernate article and I created a Contact class.

     

       1:  [ActiveRecord("Person.Contact")]
       2:  public class Contact : ActiveRecordBase<Contact>
       3:  {
       4:     private int _contactID;
       5:     private string _firstName;
       6:     private string _middleName;
       7:     private string _lastName;
       8:     private string _email;
       9:     private DateTime _lastModified;
      10:   
      11:     [PrimaryKey]
      12:     public int ContactID
      13:     {
      14:        get { return _contactID; }
      15:        set { _contactID = value; }
      16:     }
      17:   
      18:     [Property]
      19:     public string FirstName
      20:     {
      21:        get { return _firstName; }
      22:        set { _firstName = value; }
      23:     }
      24:   
      25:     [Property]
      26:     public string MiddleName
      27:     {
      28:        get { return _middleName; }
      29:        set { _middleName = value; }
      30:     }
      31:   
      32:     [Property]
      33:     public string LastName
      34:     {
      35:        get { return _lastName; }
      36:        set { _lastName = value; }
      37:     }
      38:   
      39:     [Property("EmailAddress")]
      40:     public string Email
      41:     {
      42:        get { return _email; }
      43:        set { _email = value; }
      44:     }
      45:   
      46:     [Property("ModifiedDate")]
      47:     public DateTime LastModified
      48:     {
      49:        get { return _lastModified; }
      50:        set { _lastModified = value; }
      51:     }
      52:   
      53:     public Contact()
      54:     {
      55:   
      56:     }
      57:  }

    I’m liking this a whole lot better than just using NHibernate by itself.  I’m not a fan of writing .xml files for each class to map it to a table, and the use of attributes feels a lot cleaner to me.   Notice on line 1 where  decorate my class with the ActiveRecord attribute, and I pass in the table name.  My class must also inherit from ActiveRecordBase.  In the class itself, I am able to specify my primary key with the [PrimaryKey] attribute, and I can specify all of the properties that map to database columns with the [Property] attribute.  On a few of my properties the table name does not match the database column name, so in these cases I just pass in the correct column name via the attribute.

    I couldn’t escape XML configuration completely however, I need to tell ActiveRecord how to connect to my database, and specify configuration options to pass through to NHibernate.  There are a few ways to do this, I chose to add it into my app.config file, making it look like this:

       1:  <?xml version="1.0" encoding="utf-8" ?>
       2:  <configuration>
       3:    
       4:    <configSections>
       5:      <section name="activerecord"
       6:        type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, 
       7:                         Castle.ActiveRecord" />
       8:    </configSections>
       9:   
      10:    <activerecord
      11:          isWeb="false"
      12:          isDebug="true">
      13:      <config>
      14:        <add key="hibernate.connection.driver_class" 
      15:          value="NHibernate.Driver.SqlClientDriver" />
      16:        <add key="hibernate.dialect"                 
      17:          value="NHibernate.Dialect.MsSql2005Dialect" />
      18:        <add key="hibernate.connection.provider"     
      19:          value="NHibernate.Connection.DriverConnectionProvider" />
      20:        <add key="hibernate.connection.connection_string" 
      21:          value="Server=localhost;initial catalog=AdventureWorks;Integrated Security=SSPI" />
      22:      </config>
      23:    </activerecord>
      24:      
      25:  </configuration>

    There are a lot of options available when setting up your configuration.  If you want to dig into all of the different configuration options, this is a great place to start.  There is one more step necessary before you can start using ActiveRecord.  We have already set up our class, and we also have set up the necessary configuration….the only thing that remains is to initialize ActiveRecord.  This is only done one time, and my code to handle initialization looks like this:

       1:  IConfigurationSource config = 
       2:            ActiveRecordSectionHandler.Instance;
       3:  ActiveRecordStarter.Initialize(config, typeof(Contact));

    That wasn’t so bad, was it?  You do need to tell ActiveRecord which types it needs to initialize.   The reason for this is because ActiveRecord handles all of the mapping between the database and NHibernate, so it needs to know which types need set up.  I passed in the one type I had set up, but there are several ways to handle this.  You can pass in a list of types, an assembly, or a list of assemblies.  In fact, since I don’t want to have to remember to come back to add in any new types I set up, I’ll change this to load my assembly instead….making it look like this:

       1:  IConfigurationSource config = 
       2:          ActiveRecordSectionHandler.Instance;
       3:  Assembly asm = Assembly.Load("ActiveRecordSample");
       4:  ActiveRecordStarter.Initialize(asm, config);

    Now that everything is configured and initialized, it’s time to try and use it.  The first thing I want to be able to do is load a contact based on their ContactID.   This turns out to be really simple.  Using NHibernate expressions, I can query the database however I want to.  Recall that my Contact class inherits from ActiveRecordBase, which gives me access to several static methods for the loading and saving of data.

    I could put the query directly in my console application, like this:

       1:  Contact ct = 
       2:        Contact.FindOne(Expression.Eq("ContactID", 24));
       3:  StringBuilder sb = new StringBuilder();
       4:  sb.AppendLine("First Name: " + ct.FirstName);
       5:  sb.AppendLine("Middle Name: " + ct.MiddleName);
       6:  sb.AppendLine("Last Name: " + ct.LastName);
       7:  sb.AppendLine("Email: " + ct.Email);
       8:  Console.WriteLine(sb.ToString());

    There are a few reasons I don’t like this.  First, I have to add a reference to the NHibernate.dll in my console application, which couples my UI and data layer together.  Second, I would have to rely on anyone using this class to know how to write HQL (Hibernate Query Language) expressions to load this data.  I don’t think that’s necessarily a fair assumption to make, so the better solution in my mind is to abstract this out.  If you were paying attention to the class diagram above, you’ll notice that I already created a method in my Contact class called LoadContactById which looks like this:

       1:  public static Contact LoadContactById(int contactID)
       2:  {
       3:     return FindOne(Expression.Eq("ContactID", contactID));
       4:  }

    I could then modify my console application to load the Contact like this:

       1:  Contact ct = Contact.LoadContactById(24);
       2:  StringBuilder sb = new StringBuilder();
       3:  sb.AppendLine("First Name: " + ct.FirstName);
       4:  sb.AppendLine("Middle Name: " + ct.MiddleName);
       5:  sb.AppendLine("Last Name: " + ct.LastName);
       6:  sb.AppendLine("Email: " + ct.Email);
       7:  Console.WriteLine(sb.ToString());

    This also allows me to remove the reference to the NHibernate .dll from my console application, which is better design to me anyway.  This gives us the expected result, and the data is loaded correctly:

    If you remember my last article, one thing we tried was to set up an inheritance structure and have NHibernate successfully load the data from multiple tables. ActiveRecord can also handle this scenario by using the JoinedBase attribute.  After I set up the Contact class to use the JoinedBase attribute, I just need to create my Employee class.  When I set this up, I have to set up a JoinedKey, which is the column that joins the two tables together. 

       1:  // This is the only change I made to the contact class.
       2:  [ActiveRecord("Person.Contact"), JoinedBase]
       3:   
       4:  [ActiveRecord("HumanResources.Employee")]
       5:  public class Employee : Contact
       6:  {
       7:     private int _contactID;
       8:     private int _employeeID;
       9:     private string _title;
      10:     private DateTime _hireDate;
      11:     private bool _salaryFlag;
      12:   
      13:     [JoinedKey("ContactID")]
      14:     public int CID
      15:     {
      16:        get { return _contactID; }
      17:        set { _contactID = value; }
      18:     }
      19:   
      20:     [Property]
      21:     public int EmployeeID
      22:     {
      23:        get { return _employeeID; }
      24:        set { _employeeID = value; }
      25:     }
      26:   
      27:     [Property]
      28:     public string Title
      29:     {
      30:        get { return _title; }
      31:        set { _title = value; }
      32:     }
      33:   
      34:     [Property]
      35:     public DateTime HireDate
      36:     {
      37:        get { return _hireDate; }
      38:        set { _hireDate = value; }
      39:     }
      40:   
      41:     [Property("SalariedFlag")]
      42:     public bool SalaryFlag
      43:     {
      44:        get { return _salaryFlag; }
      45:        set { _salaryFlag = value; }
      46:     }
      47:   
      48:     public Employee()
      49:        : base()
      50:     {
      51:   
      52:     }
      53:   
      54:     public static Employee LoadEmployeeByContactId(int contactID)
      55:     {
      56:        return (Employee)FindOne(Expression.Eq("ContactID", contactID));
      57:     }
      58:   
      59:     public static Employee LoadEmployeeByEmployeeId(int employeeID)
      60:     {
      61:        return (Employee)FindOne(Expression.Eq("EmployeeID", employeeID));
      62:     }
      63:  }

    Loading an employee works as expected, and I put together wrapper methods to load an employee by either the ContactID or the EmployeeID.  (since they are different in the AdventureWorks schema)

    Now it’s time for the fun part……relationships.  In my previous article I set up an object graph where I had an Order object, which contained a collection of OrderDetail objects, each of which encapsulated a Product object.  I want to duplicate that same idea using ActiveRecord.  This turns out to be incredibly intuitive, and the classes end up looking like this:

       1:  [ActiveRecord("Sales.SalesOrderHeader")]
       2:  public class Order : ActiveRecordBase<Order>
       3:  {
       4:     private int _orderID;
       5:     private DateTime _orderDate;
       6:     private decimal _orderTotal;
       7:     private IList _lineItems;
       8:   
       9:     [PrimaryKey("SalesOrderID")]
      10:     public int OrderID
      11:     {
      12:        get { return _orderID; }
      13:        set { _orderID = value; }
      14:     }
      15:   
      16:     [Property]
      17:     public DateTime OrderDate
      18:     {
      19:        get { return _orderDate; }
      20:        set { _orderDate = value; }
      21:     }
      22:   
      23:     [Property("TotalDue")]
      24:     public decimal OrderTotal
      25:     {
      26:        get { return _orderTotal; }
      27:        set { _orderTotal = value; }
      28:     }
      29:   
      30:     [HasMany(typeof(OrderDetail), Table="Sales.SalesOrderDetail", 
      31:            ColumnKey="SalesOrderID")]
      32:     public IList LineItems
      33:     {
      34:        get { return _lineItems; }
      35:        set { _lineItems = value; }
      36:     }
      37:   
      38:     public Order()
      39:     {
      40:   
      41:     }
      42:   
      43:     public static Order LoadOrderById(int orderID)
      44:     {
      45:        return FindOne(Expression.Eq("OrderID", orderID));
      46:     }
      47:  }
      48:   
      49:  [ActiveRecord("Sales.SalesOrderDetail")]
      50:  public class OrderDetail
      51:  {
      52:   
      53:     private int _orderDetailID;
      54:     private int _quantity;
      55:     private string _trackingNumber;
      56:     private decimal _price;
      57:     private Product _item;
      58:   
      59:     [PrimaryKey("SalesOrderDetailID")]
      60:     public int OrderDetailID
      61:     {
      62:        get { return _orderDetailID; }
      63:        set { _orderDetailID = value; }
      64:     }
      65:   
      66:     [Property("OrderQty")]
      67:     public int Quantity
      68:     {
      69:        get { return _quantity; }
      70:        set { _quantity = value; }
      71:     }
      72:   
      73:     [Property("CarrierTrackingNumber")]
      74:     public string TrackingNumber
      75:     {
      76:        get { return _trackingNumber; }
      77:        set { _trackingNumber = value; }
      78:     }
      79:   
      80:     [Property("UnitPrice")]
      81:     public decimal Price
      82:     {
      83:        get { return _price; }
      84:        set { _price = value; }
      85:     }
      86:   
      87:     [BelongsTo("ProductID")]
      88:     public Product Item
      89:     {
      90:        get { return _item; }
      91:        set { _item = value; }
      92:     }
      93:   
      94:     public OrderDetail()
      95:     {
      96:   
      97:     }
      98:  }
      99:   
     100:  [ActiveRecord("Production.Product")]
     101:  public class Product
     102:  {
     103:     private int _productID;
     104:     private string _name;
     105:     private string _productNumber;
     106:   
     107:     [PrimaryKey]
     108:     public int ProductID
     109:     {
     110:        get { return _productID; }
     111:        set { _productID = value; }
     112:     }
     113:   
     114:     [Property]
     115:     public string Name
     116:     {
     117:        get { return _name; }
     118:        set { _name = value; }
     119:     }
     120:   
     121:     [Property]
     122:     public string ProductNumber
     123:     {
     124:        get { return _productNumber; }
     125:        set { _productNumber = value; }
     126:     }
     127:   
     128:     public Product()
     129:     {
     130:   
     131:     }
     132:  }

    To encapsulate the Product inside the OrderDetail, I need to use the BelongsTo attribute.  To make the association, all I need to pass into this attribute is the name of the database column that ties the tables together.  In my case, the Sales.SalesOrderDetail table contains a clear foreign key to the Production.Product table, so I pass in “ProductID”.  The relationship mapped out in the Order class is even more intuitive.  I use the HasMany relationship to indicate that one order has many line items.  To complete this association, I pass in the table name of the order detail table and the column name of the field that serves as the foreign key, and ActiveRecord does the rest.  A complete list of ActiveRecord relationships and how they work can be found here.

    To load an order, I wrote a wrapper method in my Order class called LoadOrderById that takes care of it.  Calling this function will load the entire object graph.  This might not be the intended behavior, especially if you have a large and / or complex object graph.  For cases like this, lazy loading is possible with ActiveRecord.  I’m not going to get into the details here, but you can enable lazy loading on an ActiveRecord type or on a relation.  More details on how to do this can be found here.

    I can say that I’m very impressed with the implementation of ActiveRecord.  After giving it a try, I can say I’d never personally just use NHibernate again without it.  I love the fact that all of my mappings are taken care of with attributes rather than XML files, and relationships are very intuitive.  I’ve definitely only scratched the surface, but I think it’s safe to say that this product is definitely in my toolbox for good!

    kick it on DotNetKicks.com

  • NHibernate, Part II

    Posted by Jack Altiere on June 29th, 2008

    In my last article, I decided to give NHibernate a try to try and get my mind around what an ORM brings to the table.  I took a few hours and managed to get a simple example working, and was able to read and write data to the AdventureWorks database.   My example was simple though, now I would like to try and dig a little deeper into NHibernate.

    The first topic that I would like to delve into is relationships.  In my last article, I had a Contact business entity, but I was pulling all of the data for this object from a single database table.  This is often not the case in the real world, especially in situations where you don’t have absolute control over the database schema.  I would like to expand my example and add a new class called Employee that is derived from the Contact class.  I’ll just pick a few fields from the HumanResources.Employee table in AdventureWorks for this class, making my class diagram look like this:

    If I were writing a stored procedure this would be a walk in the park for me, I would just join the relevant tables in the database with an inner join and populate the Employee record with the results.  The SQL query would look something like this:

    select e.EmployeeID, e.Title, e.HireDate, e.SalariedFlag, c.FirstName, 
    c.MiddleName, c.LastName, c.EmailAddress, c.ModifiedDate
    from HumanResources.Employee e
    inner join Person.Contact c on e.ContactID = c.ContactID
    where e.ContactID = @ID

    Let’s figure out how to do the same task using NHibernate.  I’ll admit, it took me some trial and error to find a way to do this.  The learning curve to pick up all of the nuances for class mapping is a little steep.  I started by trying to create a mapping file for my Employee class, but I was hitting a wall because of how I set up the inheritance.  I’m fairly sure I could have gotten it to work that way if I had the Employee class encapsulate the Contact class rather than inheriting from it.  I ended up getting this to work by editing the mapping file for the Contact class, which was a little non-intuitive to me.  My new mapping file looks like this:

       1:  <?xml version="1.0" encoding="utf-8" ?>
       2:  <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
       3:    <class name="NHibernateSample.Contact, NHibernateSample" 
       4:                                                               table="Person.Contact">
       5:      <id name="ContactID" type="Int32">
       6:        <generator class="assigned" />
       7:      </id>
       8:      <property name="FirstName" type="String" length="50"/>
       9:      <property name="MiddleName" type="String" length="50"/>
      10:      <property name="LastName" type="String" length="50"/>
      11:      <property name="Email" column="EmailAddress" type="String" length="50"/>
      12:      <property name="LastModified" column="ModifiedDate" type="DateTime"/>
      13:   
      14:      <joined-subclass name="NHibernateSample.Employee,
      15:         NHibernateSample" table="HumanResources.Employee">
      16:        <key column="ContactID"/>
      17:        <property name="EmployeeID" type="Int32"/>
      18:        <property name="Title" type="String" length="50"/>
      19:        <property name="HireDate" type="DateTime"/>
      20:        <property name="SalaryFlag" column="SalariedFlag" type="Boolean"/>
      21:        <property name="LastModified" column="ModifiedDate" type="DateTime"/>
      22:      </joined-subclass>
      23:      
      24:    </class>
      25:  </hibernate-mapping>

    I added the joined-subclass section to the mapping file, which tells NHibernate how to load the information that is specific to the employee.  I loaded a random employee from the database (ContactID = 1001) and I was able to return the correct data.

    I was worried that this solution would break previous examples.  In the AdventureWorks schema, not all contacts are employees, which is consistent with our data model.  Since I modified the mapping file for the Contact class, I thought I might have broken things in the situation where I load a Contact record that wasn’t actually an employee.  NHibernate handled this situation perfectly, and I was able to load the contact record successfully.

    Next on my list of things to try was to get a one to many relationship to work.  The logical place for me to test this out is in the ordering system.  I want create an Order class, and in this class I want to store a list of order details that make up the order.  To add a little more complication, I made the OrderDetail class encapsulate a Product class, making my final class structure look like this:

       1:  public class Order
       2:  {
       3:     private int _orderID;
       4:     private DateTime _orderDate;
       5:     private decimal _orderTotal;
       6:     private IList _lineItems;
       7:   
       8:     public virtual int OrderID
       9:     {
      10:        get { return _orderID; }
      11:        set { _orderID = value; }
      12:     }
      13:     public virtual DateTime OrderDate
      14:     {
      15:        get { return _orderDate; }
      16:        set { _orderDate = value; }
      17:     }
      18:     public virtual decimal OrderTotal
      19:     {
      20:        get { return _orderTotal; }
      21:        set { _orderTotal = value; }
      22:     }
      23:     public virtual IList LineItems
      24:     {
      25:        get { return _lineItems; }
      26:        set { _lineItems = value; }
      27:     }
      28:   
      29:     public Order()
      30:     {
      31:   
      32:     }
      33:  }
      34:   
      35:  public class OrderDetail
      36:  {
      37:     private int _orderDetailID;
      38:     private int _quantity;
      39:     private string _trackingNumber;
      40:     private decimal _price;
      41:     private Product _item;
      42:   
      43:     public virtual int OrderDetailID
      44:     {
      45:        get { return _orderDetailID; }
      46:        set { _orderDetailID = value; }
      47:     }
      48:     public virtual int Quantity
      49:     {
      50:        get { return _quantity; }
      51:        set { _quantity = value; }
      52:     }
      53:     public virtual string TrackingNumber
      54:     {
      55:        get { return _trackingNumber; }
      56:        set { _trackingNumber = value; }
      57:     }
      58:     public virtual decimal Price
      59:     {
      60:        get { return _price; }
      61:        set { _price = value; }
      62:     }
      63:     public virtual Product Item
      64:     {
      65:        get { return _item; }
      66:        set { _item = value; }
      67:     }
      68:   
      69:     public OrderDetail()
      70:     {
      71:   
      72:     }
      73:  }
      74:   
      75:  public class Product
      76:  {
      77:     private int _productID;
      78:     private string _name;
      79:     private string _productNumber;
      80:   
      81:     public virtual int ProductID
      82:     {
      83:        get { return _productID; }
      84:        set { _productID = value; }
      85:     }
      86:     public virtual string Name
      87:     {
      88:        get { return _name; }
      89:        set { _name = value; }
      90:     }
      91:     public virtual string ProductNumber
      92:     {
      93:        get { return _productNumber; }
      94:        set { _productNumber = value; }
      95:     }
      96:   
      97:     public Product()
      98:     {
      99:   
     100:     }
     101:  }

    You’ll notice on lines 6 and 23 above that my collection of OrderDetail objects is stored as an IList.  Normally I would use a List<OrderDetail> in this situation, but I couldn’t get NHibernate to map this correctly, so I used an IList instead.  The next step is to come up with the NHibernate mapping files.  A little reminder, make sure you change your .hbm.xml files to be embedded resources in the build action in Visual Studio. I forgot to do this and NHIbernate throws an exception and will be unable to load the class.  I created a mapping file for each of my 3 new classes, and they look like this:

    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
      <class name="NHibernateSample.Order, NHibernateSample"
                                    table="Sales.SalesOrderHeader">
        <id name="OrderID" column="SalesOrderID" type="Int32">
          <generator class="assigned" />
        </id>
        <property name="OrderDate" type="DateTime"/>
        <property name="OrderTotal" column="TotalDue"
                                   type="Decimal"/>
    
        <bag name="LineItems" inverse="true">
          <key column="SalesOrderID"/>
          <one-to-many
               class="NHibernateSample.OrderDetail, NHibernateSample"/>
        </bag>
    
      </class>
    </hibernate-mapping>
    
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
      <class name="NHibernateSample.OrderDetail, NHibernateSample"
                                      table="Sales.SalesOrderDetail">
        <id name="OrderDetailID" column="SalesOrderDetailID"  type="Int32">
          <generator class="assigned" />
        </id>
        <property name="Quantity" column="OrderQty"  type="Int32"/>
        <property name="TrackingNumber" column="CarrierTrackingNumber"
                                             type="String" length="25"/>
        <property name="Price" column="UnitPrice" type="Decimal"/>
    
        <one-to-one name="Item"
                class="NHibernateSample.Product, NHibernateSample"/>
    
      </class>
    </hibernate-mapping>
    
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
      <class name="NHibernateSample.Product, NHibernateSample"
                          table="Production.Product">
        <id name="ProductID" type="Int32">
          <generator class="assigned" />
        </id>
        <property name="Name" type="String" length="50"/>
        <property name="ProductNumber" type="String" length="25"/>
      </class>
    </hibernate-mapping>

    There are several new additions to these mapping files. First, in the Order class, I have a bag construct, which specifies a one to many relationship to OrderDetail.  A bag means that I have a collection of objects, and is mapped to the IList in my class definition.  The one-to-many construct also makes sense, because 1 order has many line items.  In the OrderDetail class, we have a one-to-one relationship defined to the Product class.  This is also intuitive when you think about it, because a single line item represents 1 product.  The quantity purchased can be more than one, but we’re still talking about a single product.

    To test this out, I created a method in my data layer to load an order based on the order ID which looks like this:

       1:  public static Order LoadOrderById(int orderID)
       2:  {
       3:     Configuration cfg = LoadConfig();
       4:     ISessionFactory factory = cfg.BuildSessionFactory();
       5:     ISession session = factory.OpenSession();
       6:   
       7:     Order current = (Order)session.Load(typeof(Order), orderID);
       8:   
       9:     return current;
      10:  }

    I plugged in a valid order ID from the database, and everything loaded perfectly! I’m starting to see the power of NHIbernate, although mapping relationships can be a little tough to grasp at first.  When learning how to map classes, I found the NHibernate site to be an invaluable resource, so be sure to check it out.  Next time I will take ActiveRecord for a spin and see if it makes the process of mapping classes any easier.

    kick it on DotNetKicks.com

    Downloads

    Twitter Updates

      Top Commentators

      • No commentators.

      Copyright © 2007 Jack Altiere. All rights reserved.