De vorige keer hebben we de structuur van de solution aangemaakt. We gaan dit keer verder met het bouwen van de BusinessObjects. Waarom eerst de Business Objects? De Business Objects komen overal in elke laag weer terug. Als ik nu eerst de DataLayer zou bouwen, zou ik de helft niet kunnen maken, omdat ik dan references nodig heb naar Business Objects. We kunnen niet zonder deze objecten.

In het OrderSystem hebben we te maken met 2 Business Objecten, namelijk Order en OrderDetail. We zullen ze toevoegen aan de class library OrderSystem.BusinessLayer.BusinessObjects. Klik met de rechtermuisknop op de class library, kies Add->Class… Geef als naam op: Order. Doe hetzelfde ook voor OrderDetail.

De Order object komt er als volgt uit te zien:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace OrderSystem.BusinessLayer.BusinessObjects
{
[Serializable]
public class Order
{
private int _id;
private DateTime _orderDatum;
private string _klantNaam;
private string _status;
private IList _orderDetails;
 
public Order() { }
 
public Order(int id, DateTime orderDatum, string klantNaam, string status)
{
_id = id;
_orderDatum = orderDatum;
_klantNaam = klantNaam;
_status = status;
}
 
public int ID
{
get { return _id; }
set { _id = value; }
}
public DateTime OrderDatum
{
get { return _orderDatum; }
set { _orderDatum = value; }
}
public string KlantNaam
{
get { return _klantNaam; }
set { _klantNaam = value; }
}
public string Status
{
get { return _status; }
set { _status = value; }
}
public IList OrderDetails
{
get { return _orderDetails; }
set { _orderDetails = value; }
}
}
}

Het OrderDetail object komt er als volgt uit te zien:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace OrderSystem.BusinessLayer.BusinessObjects
{
[Serializable]
public class OrderDetail
{
private int _id;
private decimal _aantal;
private string _eenheid;
private string _omschrijving;
private decimal _stukPrijs;
private decimal _bedrag;
 
public OrderDetail() { }
 
public OrderDetail(int id, decimal aantal, string eenheid, string omschrijving, decimal stukPrijs, decimal bedrag)
{
_id = id;
_aantal = aantal;
_eenheid = eenheid;
_omschrijving = omschrijving;
_stukPrijs = stukPrijs;
_bedrag = bedrag;
}
 
public int ID
{
get { return _id; }
set { _id = value; }
}
public decimal Aantal
{
get { return _aantal; }
set { _aantal = value; }
}
public string Eenheid
{
get { return _eenheid; }
set { _eenheid = value; }
}
public string Omschrijving
{
get { return _omschrijving; }
set { _omschrijving = value; }
}
public decimal StukPrijs
{
get { return _stukPrijs; }
set { _stukPrijs = value; }
}
public decimal Bedrag
{
get { return _bedrag; }
set { _bedrag = value; }
}
}

We zijn nu klaar met de OrderSystem.BusinessLayer.BusinessObjects class library. Het is nu simpel opgezet, maar voor een groter project zou je in ieder geval kunnen overwegen om een abstract BusinessObject base class te maken. Iedere concrete Business Object zou dan daar van moeten overerven. Je kunt dan standaard logica als IsDirty, IsValid, etc in die base class kunnen zetten. Het zelfde geldt ook voor Ilist. We gebruiken nu Ilist, maar het zou makkelijker zijn om een IbusinessObjectList te maken waarin de IsDirty en IsValid logica zit. Het gaat te ver om dat hier allemaal te implementeren en te bespreken. Misschien dat ik aan het eind het complete framework via een zip-bestand mee post.

Nu we de basis van het systeem afhebben, kunnen we beginnen met het implementeren van de DataLayer.

De class library OrderSystem.DataLayer is bedoeld als een abstractielaag om de concrete (OrderSystem.DataLayer.SqlServer) implementatie van de DataLayer heen. Om dit te realiseren zullen we hier de interfaces moeten definiëren. Het is de bedoeling dat in de Data Layer straks een implementatie bestaat van een DataAccessObject, ook wel DAO genoemd. Een DAO is verantwoordelijk voor de communicatie met de database en voor het mappen van de relationele data naar object georiënteerde data.

Omdat wij in het OrderSystem alleen maar te maken hebben met Order en OrderDetail objecten hebben wij maar te maken met 1 DAO, namelijk: OrderDao. Waarom 1 DAO? We kunnen de OrderDetail zien als een onderdeel van een Order. Een OrderDetail kan niet op zichzelf bestaan en heeft altijd een Order nodig. In UML wordt dit ook wel een aggregatie genoemd.

Om in de OrderSystem.DataLayer gebruik te kunnen maken van de Business Objects moeten we in OrderSystem.DataLayer eerst een reference toevoegen naar OrderSystem.BusinessLayer.BusinessObjects. Klik met de rechtermuisknop op de OrderSystem.DataLayer, kies Add reference -> Tabblad Projects aanklikken -> En selecteer het project OrderSystem.BusinessLayer.BusinessObjects.

Als eerste gaan we in de OrderSystem.DataLayer de interface IOrderDao toevoegen. Klik met de rechtermuisknop op de class library OrderSystem.DataLayer en kies Add->New Item -> Interface. Voeg de onderstaande code toe aan de interface.

using System;
using System.Collections.Generic;
using System.Text;
using OrderSystem.BusinessLayer.BusinessObjects;
namespace OrderSystem.DataLayer
{
public interface IOrderDao
{
Order GetOrder(int orderID);
IList GetOrders();
IList GetOrderDetails(Order order);
void VoegOrderToe(Order order);
void BestelOrder(Order order);
void MeldOrderBinnen(Order order);
void LeverOrderUit(Order order);
void WijzigOrder(Order order);
void VerwijderOrder(Order order);
}

De bovenstaande interface is bedoeld om aan de Business Layer een vaste interface te leveren. In een volgende stuk gaan we de interface implementeren voor Sql Server. We kunnen als we willen hetzelfde ook doen voor Oracle of bijvoorbeeld XML-bestanden.

Om in de Business Layer een eenduidige interface te geven gaan we een Abstract Factory Pattern toepassen. Dit pattern moet er voor zorgen dat de Business Layer door middel van 1 toegangspunt de functionaliteit in de Data Layer kan benaderen.

Voeg aan de class library de class DaoFactory toe. We maken er een abstracte class van, omdat we nog bezig zijn met het definieren van een standaard interface richting de Business Layer. De concrete implementatie van DaoFactory zal plaatsvinden in OrderSystem.DataLayer.SqlServer. De code ziet er als volgt uit:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace OrderSystem.DataLayer
{
public abstract class DaoFactory
{
public abstract IOrderDao OrderDao { get; }
// There will be a property for each DAO that can be
// created. The concrete factories will have to
// implement these properties.
}
}

Zoals al eerder is aangegeven moet willen we dat we zonder veel moeite kunnen wijzigen wat betreft de database of andere types opslag. Dit kunnen we nu vrij simpel bereiken, omdat we nu een generiek interface hebben voor de DataLayer, namelijk de class library OrderSystem.DataLayer. Om dit flexibiliteit te ondersteunen hebben we nog een andere design pattern nodig, namelijk de Factory Method. (Tussenhaakjes: design patterns worden vrij goed beschreven op <a href=”http://www.dofactory.com%29./”>http://www.dofactory.com).</a> Voeg een nieuwe class toe aan de OrderSystem.DataLayer class library en noem het DaoFactories. De inhoud ziet er als volgt uit:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Configuration;
using System.IO;
namespace OrderSystem.DataLayer
{
///
/// Factory of factories. This class is a factory class that creates
/// data-base specific factories which in turn create data acces objects.
///
/// GoF Design Patterns: Factory.
///
///
/// This is the abstract factory design pattern applied in a hierarchy
/// in which there is a factory of factories.
///
internal class DaoFactories
{
///
/// Gets a provider specific (i.e. database specific) factory
///
/// GoF Design Pattern: Factory
///
/// Data access object factory.
public static DaoFactory GetDaoFactory()
{
DaoFactory factory = null;
try
{
string dataLayerAssemblyName = @ConfigurationManager.AppSettings.Get("DataLayerAssemblyName");
Assembly assembly = Assembly.LoadFile(dataLayerAssemblyName);
string daoFactoryName = ConfigurationManager.AppSettings.Get("DaoFactoryName");
factory = (DaoFactory)assembly.CreateInstance(daoFactoryName);
}
catch (Exception ex)
{
throw new Exception("Cannot connect with the DataLayer", ex);
}
return factory;
}
}
}

Ik geef toe het ziet er in eerste instantie een beetje complex uit, maar als wat langer kijkt valt het reuze mee. De GetDaoFactory methode moet een Concrete instantie teruggeven van de eerder aangemaakt DaoFactory. Dit bereiken we door in de app.config de parameters DataLayerAssemblyName en DaoFactoryName te plaatsen. De eerste parameter moet de naam van de assembly bevatten waar een concrete implementatie van de DataLayer in zit. Dit bied je de mogelijkheid om de concrete implementatie in een aparte dll (class library) te plaatsen. Dit geeft weer de mogelijkheid om in productie te kunnen switchen wat betreft DataLayer zonder de applicatie opnieuw te hoeven compilen. Je hoeft dus alleen de assembly in de juiste directory te zetten en de beide parameters invullen in de app.config en voila: het werkt!

De tweede parameter, DaoFactoryName, geeft alleen aan wat de DaoFactory is van de concrete implementatie. In de interface heet de factory DaoFactory, maar in de concrete implementatie kunnen we hem SqlDaoFactory noemen, als we dat willen.

Wat wordt er nu gedaan in de GetDaoFactory methode? Heel simpel. De beide parameters worden uit de app.config van OrderSystem gehaald. Allereerst wordt de assembly geladen waar de implementatie van de DataLayer inzit. Dit gebeurd met reflection. Vervolgens is er een variabele factory gedeclareerd van het type DaoFactory (uit de interface). Nu wordt er een instantie aangemaakt van een class die de naam heeft, die gelijk is aan de parameter DoaFactoryName. Deze instantie is dan de concrete implementatie van de Data Layer en wordt toegekend aan de variabele factory.
Om het geheel te kunnen laten draaien moet er nog een reference worden toegevoegd aan OrderSystem.DataLayer, namelijk System.Configuration.

Om het “af” te maken: vanuit de Business Layer zou het mooi zijn als we bijvoorbeeld een order kunnen toevoegen aan de database met de volgende syntax:

DataAccess.OrderDao.AddOrder(Order order);

In plaats van:

DaoFactory daoFactory = DaoFactories.GetDaoFactory();
daoFactory.OrderDao.AddOrders(Order order);

Om dit te bereiken moeten we nog een class DataAccess toevoegen aan de class library OrderSystem.DataLayer. De code ziet er als volgt uit:

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
namespace OrderSystem.DataLayer
{
///
/// This class shields the client from the details of database specific
/// data-access objects. It returns the appropriate data-access objects
/// according to the configuration in web.config.
///
/// GoF Design Patterns: Factory, Singleton, Proxy.
///
///
/// This class makes extensive use of the Factory pattern in determining which
/// database specific DAOs (Data Access Objects) to return.
///
/// This class is like a Singleton -- it is a static class (Shared in VB) and
/// therefore only one ''instance'' ever will exist.
///
/// This class is a Proxy in that it ''stands in'' for the actual Data Access Object Factory.
///
public static class DataAccess
{
// The static field initializers below are thread safe.
// Furthermore, they are executed in the order in which they appear
// in the class declaration. Note: if a static constructor
// is present you want to initialize these in that constructor.
internal static readonly DaoFactory DaoFactory = DaoFactories.GetDaoFactory();
public static IOrderDao OrderDao
{
get { return DaoFactory.OrderDao; }
}
}
}

Deze class is nu de het toegangspunt van de Data Layer. Simpel te gebruiken, zonder al te veel code.
We hebben nu de interface van de DataLayer af. De volgende keer gaan we een concrete implementatie van de Data Layer maken voor Sql Server.

Stem of voeg toe aanUitleg over het gebruik van deze icons :Voeg dit artikel toe aan Del.icio.us Voeg toe aan je Google bladwijzers Plaats dit bericht op Twitter Geef dit als tip aan je Hyves-vrienden Voeg toe aan je Facebook-profiel Deel met je LinkedIn-contacten Abonneer je op de RSS-feed van deze site Verstuur deze pagina per e-mail via Feedburner Print deze pagina of genereer een PDF-bestand