Skip to main content

Fluent NHibernate .. Or as my manager calls it "Shibernate" (Tutorial 1)

So this is a first of a few posts I will be doing on a simple NHibernate application. This post is pretty basic but in my subsequent posts, I'll be adding more functionality and/or modifying components to include dependency injection using Microsoft Unity.

The base project is a minute modification of https://github.com/jagregory/fluent-nhibernate/wiki/Getting-started.

So here we go ...

1. Create a new Solution called "Project".

2. To it, add a class library called "Project.Domain". As the name suggests, this will represent your domain entities.


3. Create the Employee, Product and store Classes. I usually like to separate the Properties in partial classes.

 
public class Employee
{
public virtual int Id { get; protected set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}

public class Product
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual IList<Store> StoresStockedIn { get; protected set; }

public Product()
{
StoresStockedIn = new List<Store>();
}
}

public class Store
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; 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(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
}

4. So now, your project structure should look like ..



5. Add a new class library project "Project.Repository" to the solution like so ..



6. Its now time to add the relevant NuGet packages.. [ gotta love them :) ] ...




7. Its now time to create your mapping files. Start by adding a "Mappings" folder.

8. We now need to create a ClassMap for the Domain classes.
 
public class StoreMap: ClassMap<Store>
{
public StoreMap()
{
// Set the Id
Id(x => x.Id);

// Set Properties
Map(x => x.Name);

// Set References
HasMany(x => x.Staff)
.Inverse().Cascade.All();
HasManyToMany(x => x.Products)
.Cascade.All().Table("StoreProduct");
}
}

public class ProductMap: ClassMap<Product>
{
public ProductMap()
{
// Set Id
Id(x => x.Id);

// Set Properties
Map(x => x.Name);
Map(x => x.Price);

// Set References
HasManyToMany(x => x.StoresStockedIn)
.Cascade.All().Inverse().Table("StoreProduct");
}
}

public class StoreMap: ClassMap<Store>
{
public StoreMap()
{
// Set the Id
Id(x => x.Id);

// Set Properties
Map(x => x.Name);

// Set References
HasMany(x => x.Staff)
.Inverse().Cascade.All();
HasManyToMany(x => x.Products)
.Cascade.All().Table("StoreProduct");
}
}

9. Remember to add a project reference to the domain project.


10.  Now lets add a Console Application "Project.DatabaseSetup" that we will use to setup the database.

11. We'll start by adding a connection string to the console app's app.config.


12. Add references to the domain and mapping projects.

13. The code for the program is given here ...
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Tool.hbm2ddl;
using Project.Domain;
using Project.Repository.Mappings;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Project.DatabaseSetup
{
class Program
{
static void Main(string[] args)
{
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
// create a couple of Stores each with some Products and Employees
var barginBasin = new Store { Name = "Bargin Basin" };
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 products to the stores, there's some crossover in the products in each
// store, because the store-product relationship is many-to-many
AddProductsToStore(barginBasin, potatoes, fish, milk, bread, cheese);
AddProductsToStore(superMart, bread, cheese, waffles);

// add employees to the stores, this relationship is a one-to-many, so one
// employee can only work at one store at a time
AddEmployeesToStore(barginBasin, daisy, jack, sue);
AddEmployeesToStore(superMart, bill, joan);

// save both stores, this saves everything else via cascading
session.SaveOrUpdate(barginBasin);
session.SaveOrUpdate(superMart);

transaction.Commit();
}

// retreive all stores and display them
using (session.BeginTransaction())
{
var stores = session.CreateCriteria(typeof(Store))
.List<Store>();

foreach (var store in stores)
{
WriteStorePretty(store);
}
}

System.Console.ReadKey();
}
}

private static void WriteStorePretty(Store store)
{
System.Console.WriteLine("Store: " + store.Name);
}

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 ISessionFactory CreateSessionFactory()
{
var connectionString = ConfigurationManager
.ConnectionStrings["Connection"]
.ConnectionString;
return Fluently
.Configure()
.Database(MsSqlConfiguration
.MsSql2008
.ConnectionString(connectionString))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<EmployeeMap>();
m.FluentMappings.AddFromAssemblyOf<ProductMap>();
m.FluentMappings.AddFromAssemblyOf<StoreMap>();

})
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}

private static void BuildSchema(NHibernate.Cfg.Configuration config)
{
// this NHibernate tool takes a configuration (with mapping info in)
// and exports a database schema from it
new SchemaExport(config)
.Create(false, true);
}
}
}

14. Remember to add the relevant project references.

15. And now, you should be able to run the application. :)


The complete source is available here: http://sdrv.ms/151sjja


References:  https://github.com/jagregory/fluent-nhibernate/wiki/Getting-started.


Comments

Popular posts from this blog

Internet Information Services(IIS) reveals its real or internal IP Address

In the ever changing world of global data communications, inexpensive Internet connections, and fast-paced software development, security is becoming more and more of an issue. Security is now a basic requirement because global computing is inherently insecure.

Keeping that in mind, we recently ran our flagship product through a security audit. It was such a helpful exercise in tying-off any remaining lose ends in our application in terms of application security. 
Based on the security audit report, there was a relatively minor issue that appeared when accessing the /images directory of our application. Turns out that the Location response header of the 301 request returns an Internal IP address. The issue is detailed below.

Issue reportedInternet Information Services (IIS) may reveal its real or internal IP address in the Location header via a request to the /images directory. The value returned whilst pen testing is https://10.0.0.10/images.

The riskInformation regarding internal IP add…

C# Console app that displays twitter feed using Linq To Twitter (using Single User Authorization)

I recently had to add a twitter feed to my existing ASP.NET MVC 4 application. All I had to do was pull the last 10 tweets for a given user. It took me a while (shamefully, 3 hours) to get it working so I thought of writing a simple tutorial that explains how to pull a twitter feed for a console app using LINQ to Twitter.

LINQ to Twitter is an open source 3rd party LINQ Provider for the Twitter micro-blogging service. It uses standard LINQ syntax for queries and includes method calls for changes via the Twitter API
What took me long to figure out was the way twitter has implemented authentication using OAuth. Before you do anything, make sure you read the Learning to use OAuth document.
In my example, I used Single User Authorization. Single User Authorization is designed for scenarios where you'll only ever have one account accessing Twitter. i.e. if your Web site does periodic Twitter updates, regardless of user or you have a server that monitors general information. 
Before we be…

Unit Testing HttpContext.Current.Session in MVC3 .NET

We recently changed some functionality where during the "CREATE" process, we go through a wizard to save application data. This data is saved only to the session in the final step when the user clicks the final submit.

This was easy enough to implement but when I started writing unit tests for my static methods that Add, Update, Delete or Modify the contents of our application data in the session, I got the following error:
System.NullReferenceException: Object reference not set to an instance of an object.

Turns out I had forgotten to setup the HttpContext.
The following "TestInitialise" method fixed my problem :)

[TestInitialize]
public void TestSetup()
{
// We need to setup the Current HTTP Context as follows:

// Step 1: Setup the HTTP Request
var httpRequest = new HttpRequest("", "http://localhost/", "");

// Step 2: Setup the HTTP Response
var httpResponce = new HttpResponse(new StringWriter());

// Step 3: Se…