Wednesday, May 13, 2009

NHibernate Siri: Asas - Part 1 of N

Artikel aku yang lepas tentang NHibernate hanya menyentuh sedikit tentang NHibernate secara umum. Aku fokuskan kepada bagaimana hendak mula develop simple app dengan NHibernate.

Kali ini aku nak masuk detail kepada asas NHibernate, tapi aku plan tak nak sentuh teori tentang ORM atau teori NHibernate itu sendiri. Aku nak start direct asas kepada NHibernate technical.

Secara dasarnya element penting dalam NHibernate ialah Domain object, Domain Mapping dan juga Session.

Untuk mula aku akan cadangkan project structure yang aku akan buat ialah seperti ini


- NS.Domain

- NS.Infrastructure

- NS.Service

- NS.Tests



Domain Object
Domain object ialah normal class yang ada property (set,get) dan method. Disini letaknya business behaviour dan terpulang pada kita nak design domain object itu bentuknya bagaimana. Ia boleh menjadi complex atau menjadi simple mengikut context yang didefine semasa process sesi modeling. Untuk tutorial ini, aku akan define simple business object iaitu Product. Product class akan di create di dalam NS.Domain.





using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NS.Domain
{
public class Product
{
public string Name { set; get; }

public string Description { set; get; }

public decimal Price { set; get; }
}
}


Selepas create domain class ini, next ialah mapping domain object ini untuk persistence ke dalam database. NHibernate akan gunakan mapping ini sebagai panduan untuk berinteraksi dengan persistence medium (Database).

Domain Mapping
Untuk mapping domain object ada beberapa cara untuk melakukannya, pada kebiasaan ialah dengan create xml file Product.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="FirstSolution" namespace="FirstSolution.Domain">
<class name="Product">
<id name="Id">
<generator class="guid" />
</id>
<property name="Name" />
<property name="Description" />
<property name="Price" />
</class>
</hibernate-mapping>


Ok, kali ini aku tidak akan menggunakan xml kerana agak susah untuk write markup sebaliknya aku akan guna fluent mapping dalam project FluentNHibernate. Sebelum itu ada satu lagi cara untuk mapping ialah menggunakan property attribute

    [Class]
public class AACatalog
{
[Property]
public virtual string Version { get; set; }

[ManyToOne(Name = "Batery", Lazy = Laziness.Proxy, Cascade = CascadeStyle.None, OuterJoin=OuterJoinStrategy.False)]
public virtual Batery Batery { set; get; }

[Bag(Name = "BateryCatalogs", Lazy = true, Generic = true, Inverse = true, Cascade = CascadeStyle.AllDeleteOrphan)]
[Key(1, Column = "AAId")]
[OneToMany(2, Class = "BateryCatalogs", ClassType = typeof(BateryCatalogs))]
public virtual IList<BateryCatalog> BateryCatalogs
{
set;
get;
}
}



Untuk domain class Product aku akan create ProductMap didalam NS.Domain dan aku letak didalam folder Mapping seperti ini. Pastikan juga letakkan reference FluentNhibernate.dll di reference project tree.Class ProductMap ialah seperti dibawah. Ada sedikit penambahan di class Product dimana aku tambah property untuk Id, secara umum unutk mapping pada persistence class yang simple kita banyak boleh gunakan default attribute pada nhibernate mapping, dimana nhibernate engine akan "infer" colum name, colum type etc. Sebagai conoth jika kita tak define nama colum nhibernate mapping akan ambil property name sebagai nama column

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentNHibernate.AutoMap;

namespace NS.Domain.Mapping
{
public class ProductMap : AutoMap<Product>
{
public ProductMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Name);
Map(x => x.Description);
Map(x => x.Price).CustomSqlTypeIs("decimal");
}
}
}


Ok selesai dengan mapping domain class, langkah seterusnya ialah untuk test business domain dengan run configuration dan table schema.Class dibawah ialah helper class yang akan digunakan didalam test.

    public class NHibernateInMemoryConfiguration
{
protected static string DbFile = "db";
protected static ISessionFactory sf = null;
protected static Configuration config = null;


public static void InitializeConfiguration(string assemblyName)
{
sf = Fluently.Configure()
.Database(SQLiteConfiguration.Standard
.UsingFile(DbFile)
//.ProxyFactoryFactory("NHibernate.ByteCode.Castle.ProxyFactoryFactory,NHibernate.ByteCode.Castle")
.ShowSql())
.Mappings(m => m
.FluentMappings
.AddFromAssembly(Assembly.Load(assemblyName)))
.ExposeConfiguration(SetConfiguration)
.BuildSessionFactory();
}

public static void SetConfiguration(Configuration cfg)
{
config = cfg;
}

public void CreateSchema()
{
if (File.Exists(DbFile))
File.Delete(DbFile);
new SchemaExport(config).Create(true, true);

}
}


Ok selesai dengan class ini, masa unutk run test.Aku akan buat 2 class satu ialah base class iaitu RepositoryBaseTest.cs dan ProductTest.cs

    public class RepositoryBaseTest : NHibernateInMemoryConfiguration
{
protected ISession session;



[TestFixtureSetUp]
public void ConfigurationInitialization()
{
InitializeConfiguration("NS.Domain");
}

[SetUp]
public void Setup()
{
this.CreateSchema();
}
}



    [TestFixture]
public class ProductTest:RepositoryBaseTest
{
[Test]
public void Should_save_Product()
{
using(var s = sf.OpenSession())
using(var tx = s.BeginTransaction())
{
Product p = new Product { Name = "A", Description = "B", Price = Convert.ToDecimal("10.0") };
s.Save(p);
tx.Commit();
}

}
}



0 comments: