Inside HyperActive
9:29 PM Monday Dec 22, 2008 Comments: 0I'm finally getting around to documenting how to use this thing. HyperActive is a tool I wrote to help generate Castle ActiveRecord classes by inferring details from a database schema.
Hyperactive uses my SchemaProber to read a database schema, and the Dominator helps with generating the code.
Step 1: Download the Code
The latest release is 1.3.1.9 and can be downloaded here.
Step 2: Create the HyperActive Config File
Here is a config file for setting up HyperActive to run against the Northwind database running on your local system:
<hyperactive>
<config>
<add key="namespace" value="HyperActiveNorthwindSample.Data" />
<add key="outputpath" value="Data/Generated" />
<add key="basetypename" value="Castle.ActiveRecord.ActiveRecordBase"></add>
<components>
<component
service="HyperActive.Core.NameProvider, HyperActive" />
<component
service="HyperActive.SchemaProber.IDbHelper, HyperActive"
serviceimpl="HyperActive.SchemaProber.DbHelper, HyperActive">
<param name="connectionString"
value="integrated security=SSPI;server=(local);database=Northwind;" />
</component>
<component
service="HyperActive.SchemaProber.IDbProvider, HyperActive"
serviceimpl="HyperActive.SchemaProber.SqlServerProvider, HyperActive">
<param name="helper" type="System.String" value="$IDbHelper" />
</component>
<component
service="HyperActive.Core.Generators.ActiveRecordGenerator, HyperActive"
serviceimpl="HyperActive.Core.Generators.BasicActiveRecordGenerator, HyperActive" />
</components>
</config>
</hyperactive>
| |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
Things you need to provide. namespace, all of your generated classes will be contained in this namespace.
outputpath is where the generated files will be saved; it can be relative to your project or absolute, in the config above I've chosen to go with a directory relative to my project's root. I like putting generated files into their own directory. Most of the time I generate partial classes and into the Data/Generated directory and then if i choose to extend any partial classes, I'll put those in the Data directory.
basetypename indicates what generic class the generated classes will subclass. There are several ways of handling this, most of the time I use ActiveRecordBase
Components...
I ended up rolling my DI Framework. Mostly it was because I needed to minimize dependencies on other frameworks but also because I wanted to see what it takes to make a my own dependency injection tool. It was fun, brings up a good point though, how do you do dependency injection without having to also include Ninject, Windsor, StructureMap, or whatever the cool framework of the day is.
The different components that can be swapped out, are the NameProvider, SchemaProvider, and the ActiveRecordGenerator implementation. There are some included in the HyperActive assembly or you can roll your own.
Step 3: Setup Visual Studio External Tools
I have directory located at C:\lib where I put common libraries that I reference alot. Assuming that the HyperActive and hactive assemblies are located in that directory you can set up an external tool in Visual Studio with the following configuration:
Step 5: Got Database?
I know this works with the Northwind database. You can download my script from me here or grab the installer from codeplex or use your own database.
Step 6: Set Up Your Solution and Generate!
I'm going with a picture is worth a thousand words. Here's what the solution looks like before code gen:
After code generation, we'll have this:
The generated Products class looks like this:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.3053
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HyperActiveNorthwindSample.Data {
[Castle.ActiveRecord.ActiveRecordAttribute("Products")]
public partial class Products : Castle.ActiveRecord.ActiveRecordBase<HyperActiveNorthwindSample.Data.Products> {
private int _productID;
private string _productName;
private string _quantityPerUnit;
private System.Nullable<decimal> _unitPrice;
private System.Nullable<int> _unitsInStock;
private System.Nullable<int> _unitsOnOrder;
private System.Nullable<int> _reorderLevel;
private bool _discontinued;
[Castle.ActiveRecord.PrimaryKeyAttribute(Castle.ActiveRecord.PrimaryKeyType.Identity, "ProductID")]
public virtual int Id {
get {
return _productID;
}
set {
_productID = value;
}
}
[Castle.ActiveRecord.PropertyAttribute("ProductName")]
public virtual string ProductName {
get {
return _productName;
}
set {
_productName = value;
}
}
[Castle.ActiveRecord.PropertyAttribute("QuantityPerUnit")]
public virtual string QuantityPerUnit {
get {
return _quantityPerUnit;
}
set {
_quantityPerUnit = value;
}
}
[Castle.ActiveRecord.PropertyAttribute("UnitPrice")]
public virtual System.Nullable<decimal> UnitPrice {
get {
return _unitPrice;
}
set {
_unitPrice = value;
}
}
[Castle.ActiveRecord.PropertyAttribute("UnitsInStock")]
public virtual System.Nullable<int> UnitsInStock {
get {
return _unitsInStock;
}
set {
_unitsInStock = value;
}
}
[Castle.ActiveRecord.PropertyAttribute("UnitsOnOrder")]
public virtual System.Nullable<int> UnitsOnOrder {
get {
return _unitsOnOrder;
}
set {
_unitsOnOrder = value;
}
}
[Castle.ActiveRecord.PropertyAttribute("ReorderLevel")]
public virtual System.Nullable<int> ReorderLevel {
get {
return _reorderLevel;
}
set {
_reorderLevel = value;
}
}
[Castle.ActiveRecord.PropertyAttribute("Discontinued")]
public virtual bool Discontinued {
get {
return _discontinued;
}
set {
_discontinued = value;
}
}
}
}
| |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
Step 7: Take It For A Spin
So to test out our generated code, I added a nunit test fixture that just does a simple query for a specific product. That code looks like this:
using System;
using System.Configuration;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
using HyperActiveNorthwindSample.Data;
using NHibernate.Criterion;
using NUnit.Framework;
namespace HyperActiveNorthwindSample
{
[TestFixture]
public class SmokeTests
{
[TestFixtureSetUp]
public void TextFixtureSetup()
{
IConfigurationSource source =
ConfigurationManager.GetSection("activerecord") as IConfigurationSource;
ActiveRecordStarter.Initialize(typeof(Products).Assembly, source);
}
[Test]
public void CanGetOneProduct()
{
Products product = Products.FindOne(Expression.Eq("ProductName", "Chai"));
Assert.AreEqual("Chai", product.ProductName);
}
}
}
| |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
Thoughts and Caution
OK. So the caution part of this has to do with how I do databases. HyperActive, in it's current form, supports how I and my co-workers slash colleagues currently develop databases. This may differ drastically from the way you the reader develops against a database. I almost always use sql server 2005+ and identity columns. HyperActive will most likely puke on schemas that have tables with non-identiy column primary keys.
HyperActive is not as flexible as it needs to be. Frameworks should be easy to extend if you are not the orginal developer. HyperActive is easy to extend if you are me or really bored and feel like working through the framework's source, so to that end I need to refactor and simplify.
So anyway, there it is, I may have some more posts on this, but I have better ideas on how to improve it, so I might spend more time doing that.
The Code
Get it all right here http://panteravb.com/downloads/HyperActiveNorthwindSample.zip.