Home .NET Fluent NHibernateand Oracle

Fluent NHibernateand Oracle

by admin

In thisthread I would like to highlight the library Fluent NHibernate in conjunction with Oracle and give a small example.There are not many articles on thistopic right now (mentioning on the hubra ), even fewer descriptions of the interaction with Oracle.Naturally, most are not in Russian. However, the library is noteworthy.
The authors setthemselves the task of ridding NHibernate from XML (xmlless, so to speak). Therefore, it won’t be in our project. At all. Instead, a fluent ("fluid" or "flexible")interface is provided, usinglambda expressions and method chains to customize the database and entity mapping.

Props

We will need :

  • Oracle Server (11.2.0.1.0 in my case)
  • Oracle DAC (Data Access Components, in my case 11.2.0.2.1), Oracle Client, or any of your other Oracle database access providers.

To avoidproblems in the future, make sure that you can connect to the database, for example, usingthird-party utilities. The client settings are located at :
%Oracle DAC/Client%product11.2.0client_1NetworkAdmintnsnames.ora
In my case, the file has the form :
ORCL =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = orasrvxp)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcl)
)
)

Let’s create a project

Let’s create a newconsole project in Visual Studio 2010 and name it FluentExample. I use NuGet Package Manager to quickly find, getand install the necessary packages. You can do the installation of the library and manually
Right-click on the project file and click "Add Library Package Reference…". On the Online tab, enter "fluent" in the search field:
Fluent NHibernateand Oracle
In addition to the Fluent NHibernate itself, all dependencies will be loaded (Castle.Core, Iesi.Collections, NHibernate, NHibernate.Castle).
I will base this on the native example from the website library, it is simple and illustrates the capabilities of the library quite fully. The diagram looks like :
Fluent NHibernateand Oracle
Create two subfolders in the project: Entities and Mappings, which will contain the entities and therefore the mappings.

Describing entities

The first entity, the employee. The worker has a first name, last name and a place of work – store. Let’s add a newclassto the Entities folder with the following code :

public class Employee
{
public virtual int Id{ get ; private set ;}
public virtual string FirstName { get ; set ;}
public virtual string LastName { get ; set ;}
public virtual StoreStore { get ; set ;}
}
* This source code was highlighted with Source Code Highlighter

I draw your attention to two things:

  • Hereinafter the property Id and list properties (one-to-many) are declared with a privatesetter. Only NHibernate will setvalues for Id (via reflexion), and we initialize the lists in the constructor.
  • All properties are declared with the modifier virtual because NHibernate will create proxy classes for our entities, and we need to allow overloading of properties.

Next in line is a product that has a name, a price, and a list of stores that sell it:

public class Product
{
public virtual int Id { get ; private set ;}
public virtual string Name { get ; set ;}
public virtual double Price { get ; set ; }
public virtual IList<Store> StoresStockedIn { get ; private set ; }
public Product()
{
StoresStockedIn = new List <Store> ();
}
}
* This source code was highlighted with Source Code Highlighter

The list has a privatesetter and is initialized in the constructor as described above.
The classof the store that has the name as well as lists of products and staff :

public class Store
{
public virtual int Id { get ; private set ; }
public virtual string Name { get ; set ; }
public virtual IList<Product> Products { get ; private set ; }
public virtual IList<Employee> Staff { get ; private set ; }
public Store()
{
Products = new List <Product> ();
Staff = new List <Employee> ();
}
public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add( this );
Products.Add(product);
}
public virtual void AddEmployee(Employeeemployee)
{
employee.Store = this ;
Staff.Add(employee);
}
}
* This source code was highlighted with Source Code Highlighter

In this class we describe a bit of logic, since NHibernate requires both sides of the relationship to be set before saving the related entities.
This ends the description of our entities. The linking table to implement the many-to-many relationship between a product and a store will be discussed below.

Mappim essence

Next, with classic NHibernate, you would have to start writing xml mapping and database configuration files. But this is where Fluent NHibernate comes in.
All of the following mapping classes are located in the Mappings folder. Let’s start with the mapping class Employee

using FluentExample.Entities;
using FluentNHibernate.Mapping;
namespace FluentExample.Mappings
{
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
}
}
}
* This source code was highlighted with Source Code Highlighter

The display code itself will be placed in the constructor of the class inherited from ClassMap from the FluentNHibernate library.For Fluent NHibernate to be able to find mappings it is necessary to declare classes with the modifier public.Immediately consider full mapping :

public EmployeeMap()
{
Table( Employee" );
Id(e =>e.Id)
GeneratedBy.Sequence( "Employee_seq" );
Map(e =>e.FirstName);
Map(e =>e.LastName);
References(e =>e.Store);
}
* This source code was highlighted with Source Code Highlighter

First line, Table() is responsible for the nameof the table to which the mapping is performed.Below we see why we have to specify the name explicitly.
In the method Id() we specify which field identifies the object (NHibernate: <id> ). If we were usinga base with autoincrement, Fluent NHibernate would automatically recognize the type and assign an identity generator (NHibernate: <generator> ). But since autoincrement doesn't exist in Oracle, we will use sequence, which is shown further in the chain of methods.
The following two lines indicate the display of scalar properties (NHibernate: <property> ), where type and name are defined automatically. This is implemented with the property naming convention. The column name can also be specified manually, usingthe method Column() but I still recommend to use defaults or define your own. Read more about agreements
The next line shows that our object refers to the class Store in the singular, that is, it implements the "many" side with respect to many-to-one. The name of the foreign key, again following the conventions, is assumed to be Store_id. The foreign key column can be overridden by the method Column() Method References() is applied on the "many" side, on the other side of the relation ("one")the HasMany() (NHibernate: <bag> ), and the corresponding method for renaming the key field is KeyColumn()
Back to the method Table() The point here is that Fluent NHibernate by default surrounds table names with quotes when displaying them (the convention is this), which means our table would now have to be named "Employee", along with quotation marks. In Oracle, only names surrounded by quotes are case-sensitive. In our example, I didn't want to use quotation marks and leave the table names case insensitive and unquoted, so I specified them explicitly.
Let's move on to store mapping :

public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Table( "Store" );
Id(x =>x.Id)
GeneratedBy.Sequence( "Store_seq" );
Map(x => x.Name);
HasMany(x => x.Staff)
Inverse()
Cascade.All();
HasManyToMany(x => x.Products)
Cascade.All()
Table( "StoreProduct" );
}
}
* This source code was highlighted with Source Code Highlighter

We see here a symmetrical call to HasMany() for the class Employee which we discussed above. We also use the method HasManyToMany() which implements the many-to-many relation between the store and the product through an intermediate table, whose name is given in the chain by the Table()
Method Inverse() shows that the owner of the collection is the other end of the relation, i.e. class Employee , and it will be saved first. Cascade.All() Passes events to related elements. That is, ifwe, for example, delete a store, all links to products and all personnel of this store will also be deleted. See the NHibernate documentation for details.
Finally, let's display our product :

public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Table( "Product" );
Id(x => x.Id)
GeneratedBy.Sequence( "Product_seq" );
Map(x => x.Name);
Map(x => x.Price);
HasManyToMany(x => x.StoresStockedIn)
Cascade.All()
Inverse()
Table( "StoreProduct" );
}
}
* This source code was highlighted with Source Code Highlighter

I hope you don't find anything new here from previous mappings. More about fluent mappings
Now it is time to configure the connection to Oracle.

Connecting to Oracle

We are going to connect usingconnectionString. Let's adda new file to the project, App.config, and put our connection string there for the fluent user with the password nhibernate:

Copy Source | Copy HTML

    <? xml version ="1.0" encoding ="utf-8" ? >

  1. < configuration >
  2. < connectionStrings >
  3. < add name ="Oracle"
  4. connectionString ="DATA SOURCE=orcl;PASSWORD=nhibernate;PERSIST SECURITY INFO=True;USER ID=fluent"
  5. providerName ="Oracle.DataAccess.Client" />
  6. </ connectionStrings >
  7. </ configuration >
  8. Next, you need to add a link to the assembly Oracle.DataAccess supplied with the provider of your choice :
    Fluent NHibernateand Oracle
    The dialog of adding links is likely to be different for you - I use Productivity Power Tools
    Let's set the added link property Copy Local = True which tells us, that this library should be copied to the folder with output binary files of our project :
    Fluent NHibernateand Oracle
    For convenience, let's use a small helper to open a session in Fluent NHibernate:

    using FluentExample.Entities;
    using FluentNHibernate.Cfg;
    using FluentNHibernate.Cfg.Db;
    using NHibernate;
    using NHibernate.Driver;
    namespace FluentExample
    {
    public static class FluentNHibernateHelper
    {
    private static ISessionFactory _sessionFactory;
    public static ISessionFactory SessionFactory
    {
    get
    {
    if (_sessionFactory == null )
    {
    var dbConfig = OracleDataClientConfiguration.Oracle10
    ConnectionString(c => c.FromConnectionStringWithKey( "Oracle" ))
    Driver<OracleDataClientDriver> ()
    ShowSql();
    _sessionFactory = Fluently.Configure()
    Database(dbConfig)
    Mappings(m => m.FluentMappings.AddFromAssemblyOf<Employee> ())
    BuildSessionFactory();
    }
    return _sessionFactory;
    }
    }
    public static ISession OpenSession()
    {
    return SessionFactory.OpenSession();
    }
    }
    }
    * This source code was highlighted with Source Code Highlighter

    In the database configuration, we use the predefined class OracleDataClientConfiguration from the FluentNHibernate.Cfg.Db and configure it to use the connection string and NHibernate driver we created.You can also enable the option to show the generated SQL queries. More about database configuration
    Next we configure NHibernate. We set up our database configuration, specify the source of mappings and request the session factory. Read more about fluent configuration
    At this point receiving the session should be successful :

    class Program
    {
    static void Main()
    {
    using (FluentNHibernateHelper.OpenSession())
    {
    }
    }
    }
    * This source code was highlighted with Source Code Highlighter

    Let's test our base

    Now your project should have a structure like this :
    Fluent NHibernateand Oracle
    It's time to run the real test :

    private static void Main()
    {
    using ( var session = FluentNHibernateHelper.OpenSession())
    {
    using ( var transaction = session.BeginTransaction())
    {
    var barginBasin= new Store {Name = "BarginBasin" };
    var superMart = new Store {Name = "SuperMart" };
    var potatoes = new Product {Name = "Potatoes" , Price = 3.60 };
    var fish = new Product {Name = "Fish" , Price = 4.49 };
    var milk = new Product { Name = "Milk" , Price = 0.79 };
    var bread = new Product { Name = "Bread" , Price = 1.29 };
    var cheese = new Product { Name = "Cheese" , Price = 2.10 };
    var waffles = new Product { Name = Waffles" Price = 2.41 };
    var daisy = new Employee { FirstName = "Daisy" , LastName = "Harrison" };
    var jack = new Employee { FirstName = "Jack" LastName = "Torrance" };
    var sue = new Employee { FirstName = "Sue" LastName = "Walkters" };
    var bill = new Employee { FirstName = "Bill" LastName = "Taft" };
    var joan = new Employee { FirstName = "Joan" , LastName = "Pope" };
    //Add merchandise. Some products are repeated because the ratio
    // between the productand the storeis set as many-to-many.
    AddProductsToStore(barginBasin, potatoes, fish, milk, bread, cheese);
    AddProductsToStore(superMart, bread, cheese, waffles);
    // Each employeecan only work inone store
    AddEmployeesToStore(barginBasin, daisy, jack, sue);
    AddEmployeesToStore(superMart, bill, joan);
    // Save both stores and update everything else via cascading
    session.SaveOrUpdate(barginBasin);
    session.SaveOrUpdate(superMart);
    transaction.Commit();
    }
    //display all stores
    using (session.BeginTransaction())
    {
    var stores = session.CreateCriteria( typeof (Store)). List <Store> ();
    foreach ( var store in stores)
    WriteStorePretty(store);
    }
    Console ReadKey();
    }
    }
    public static void AddProductsToStore(Store store, params Product[] products)
    {
    foreach ( var product in products)
    store.AddProduct(product);
    }
    public static void AddEmployeesToStore(Store store, params Employee[] employees)
    {
    foreach ( var employee in employees)
    store.AddEmployee(employee);
    }
    private static void WriteStorePretty(Store store)
    {
    Console WriteLine(store.Name);
    Console WriteLine( " Products:" );
    foreach ( var product in store.Products)
    Console WriteLine( " " + product.Name);
    Console WriteLine( " Staff:" );
    foreach ( var employee in store.Staff)
    Console WriteLine( " " + employee.FirstName + " " + employee.LastName);
    Console WriteLine();
    }
    * This source code was highlighted with Source Code Highlighter

    That's it.
    I also bring in script for generating a schema in Oracle.

    What's next?

    Unrealized functionality

    The authors support a huge part of the features of the native library, but some features of NHibernate are not yet available, e.g, <sql-insert>

    Automapping

    Fluent NHibernate allows you to do mapping automatically, just follow the conventions entered or define your own.

    Testing mappings

    The library allows you to quickly test created mappings in the same style.

    //fluentnhibernate.org/api/index.htm "> Full API documentation

    You may also like