Click here to Skip to main content
15,892,643 members
Articles / Hosted Services / Azure

DataObjects.Net - ORM Framework for RAD - Introduction

Rate me:
Please Sign up or sign in to vote.
4.85/5 (15 votes)
7 Sep 2010CPOL11 min read 50K   1.1K   32  
Introduction into the ORM Framework DataObjects.Net from x-tensive
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Article Source</title>
<link rel="stylesheet" type="text/css" href="http://s.codeproject.com/App_Themes/Std/CSS/CodeProject.css?dt=2.3.0013" />
</head>
<body>
<hr class="Divider subdue" />
<div>
<!-- Start Article -->

    <h2>Introduction</h2>
    <p>
        Persistent data has come a long way from plain files, data with indexes like Berkley DB or DBase, SQL servers with transactions, stored procedures and referential integrity.
        Last but not least pure object oriented databases have been developed.
        XML files became very popular to store small peaces of information or do information interchange.
        In fact there is no swiss army knife or general purpose solution for persistent data.
        But for so called business applications SQL and relational databases are still the first choice.
    </p>
    <p>
        From the developers point of view pure object oriented databases are eye candy. 
        The code looks great and is not cluttered with database stuff. 
        But for the stakeholders there are many disadvantages using OO databases. 
        It begins by the (often) extortionate licensing costs and ends with the IT department who hasn't any idea how to maintain (backup, convert, etc) such a database.
        They object that mature and stable database systems are available.
        And some of them like PostgreSQL are free and open source.
        Some of them don't need hardware or maintainance like the Microsoft Azure cloud service.
    </p>
    <p>
        In this area of conflict the object relational mapping (ORM) frameworks came on the plan.
        ORM frameworks make relational databases look like object oriented (or object relational) ones. 
        However the data is stored in a relational (or even simpler) database.   
    </p>

    <h3>Why should we use a ORM Framework in general?</h3>
    <p>
        First of all, the ideal project for a relational database (and therefore for ORM) is a complex business application with a lot of logic (so called business logic) behind them.
        Typical business application domains are accounting and enterprise resource planning (ERP).
        Content management systems (CMS) for the web, organizers and business planners are such application domains too.
        Anything with complex relations and logic between the data (so called entities) fits in this concept. 
        Maybe it is not the best idea to use relational databases or ORM if you need to 
        store data that aren't structurated or related to each other very much (like log files).</p>
    <p>
        If you are familiar with SQL programming, you know that you always do the work twice while you develop such applications.
        Firstly you define your model on the database by creating a lot of tables and setting foreign keys between them.
        Then you write the code to access these tables, place queries and change values. 
        Your logical entities are spread over several tables, you have to join them manually and maintain consistency (almost) manually.
        This have been the building block of the so called client/server applications since the middle of the 90s.
        It is implemented in the Microsoft .NET Framework as ADO.NET and deals with classes like DataSet, DataTable, DataRow and DataView.
    </p>
    <p>
        Object Relational Mapping (ORM) came into play to access the database with objects. 
        It acts as a layer between the programming language or environment one one hand and the underlying database on the other hand. 
        As a result of this abstraction the programming of business applications is much faster and the code is more maintainable.
        ORM Frameworks implement several ways to speed up the development process and increase maintainability.
        In general they allow to access the database with objects that reflect entities in a (almost) natural way.
        Many of them allow to put LINQ queries against the database to get entities.
        And some of them decouple the business logic completely from the underlying database. 
    </p>
    <p>
        Two of the best known ORM frameworks are NHibernate (.NET) and the ADO.NET Entity Framework.
        Both of them have only a partial LINQ implementation and they are not known to be the fastest on the racetrack.
        In this article i want to introduce DataObjects.Net from x-tensive. DataObjects.net is a ORM with a very high abstraction, 
        a almost complete LINQ implementation and a really good performance.
    </p>
    
    <h3>Why is DataObjects.Net worth to give it a try?</h3>
    <p>
        First of all I gave it a try because of the automatic schema generation and maintenance.
        I've been programming SQL for years, and I'm completely fed up with this table, view and stored procedures declarations. 
    </p>
    <p>
        While working with it, I realized that this framework has solved almost all problems ORM comes along with.
        I can work with sessions and transactions in a transparent way and my entities look like real C#/VB objects.
        X-tensive have done a lot to reduce the chattiness (round-trips to the SQL server) to a minimum. 
        For complex business applications DataObjects.Net is even faster than working with the SQL server directly or using any lightweight ORM framework due to the optimizations 
        they have done.</p> 
    <p>
        Feature and performance comparison with other ORM frameworks can be found on: <a href="http://ormbattle.net/" target="_blank">
            ormbattle.net</a>
            </p>
    <h3>How does DataObjects.Net work?</h3>
    <p>
        DataObjects.Net uses 
        aspect oriented programming (AOP) to inject the persistence and business logic in 
        normal C#/VB classes. Aspects can be seen as some kind of function or 
        delegate called at the beginning or the end of other functions. They don't need 
        to be called in source code, they are injected after compiling the assembly in 
        so call Intermediate Language (IL). DataObjects.Net uses the well known
        <a href="http://www.sharpcrafters.com/" target="_blank">PostSharp</a> 
        framework (a framework for aspect oriented programming) to inject the mapping code. 
        The model is declared in form of simple classes with some attributes as shown in the following samples. It makes DataObjects.NET
        very elegant to use and it keeps all mapping stuff away from the user.</p>


    <h2>
        Download and Installation of DataObjects.Net</h2>
    <p>
        Before you can test drive the framework you have to download and install it from the x-tensive
        homepage:</p>
    <p>
        <a href="http://www.x-tensive.com/Downloads/Default.aspx?Path=DataObjects.Net">DataObjects.Net
            Download</a> In this article I use Version 4.3 for Visual Studio 2010.</p>

    <h3>
        Behind the Scenes: DataObjects.Net and Visual Studio
    </h3>
    <p>
        You may ask yourself how PostSharp and DataObjects.Net is integrated into a Visual
        Studio solution or C# project. Due to the fact that PostSharp has to manipulate
        the compiled and linked assemblies there must be a special build step after compilation.
        If you open your <code>.csproj</code> file in your Visual Studio solution you find
        an additional &quot;import project&quot; line:
    </p>
    <pre lang="xml">&lt;Import Project=&quot;$(MSBuildToolsPath)\Microsoft.CSharp.targets&quot; /&gt;
&lt;Import Project=&quot;$(DataObjectsDotNetPath)\Common\DataObjects.Net.targets&quot; /&gt;&nbsp;&nbsp;</pre>
    <p>
        This include does the magic behind the scenes. BTW: If you click right on your
        project in the Project Explorer window, you find &quot;Unload Project&quot;. After
        you have unloaded your project you find &quot;Edit Project&quot; to edit your project
        file. After editing you can load your project again.</p>
    <p>
        You find further information in the Manual under &quot;Using DataObjects.Net in
        your projects and solutions&quot;</p>
    <h2>
        Building Blocks</h2>
    <p>
        Every ORM framework maps some classes or objects against some data in a database. 
        However every ORM framework has its own way to accomplish this.
        In the following sections I will explain the building blocks of a 
        DataObjects.Net application and put a glance at the ideas behind.
    </p>

    <h3>Domains and Data Storage</h3>
    <p>
        DataObjects.Net needs access to some kind of data storage, commonly an SQL 
        server. The glue between the Database and the C# objects is the so called Domain. 
        The term domain has nothing to do with internet domains, but comes from the so 
        called Domain Driven Design (DDD). In technical terms the Domain is the 
        connector to the database. 
        A Domain needs two things: First of all a connection string to a database 
        or data storage in general. All data will be stored there. 
        Second, assemblies and namespaces where your persistent classes are defined. 
        These classes are the so called 'Model'. The needed tables, indices, 
        primary and foreign keys are created from this 'Model'. A DataObjects.Net Domain can be 
        configured in the App.config file or in code. Here is a sample how this is done in 
        code:</p>
    <pre lang="cs">var config = new DomainConfiguration(&quot;sqlserver://localhost\\SQLExpress/DO40-Tests&quot;);
config.UpgradeMode = DomainUpgradeMode.Recreate; // Drops all tables and recreates them. Database is empty after that
config.Types.Register(typeof(SimpleEntity).Assembly, typeof(SimpleEntity).Namespace);
var domain = Domain.Build(config);    
</pre>
    We have a connection string to a Microsoft SQL Server Express database named 
    &quot;DO40-Tests&quot;. Keep in mind that DataObejcts.Net <strong>can generate tables and so on</strong>, 
    but <strong>it can't generate any database itself</strong>. You must create the database 
    &quot;DO40-Tests&quot; and ensure that DataObjects.Net can access it under the given 
    connection string. After that DataObejcts.Net will do the rest.
    
    <h3>Model</h3>
    <p>Persistent data is declared and accessed in form of C# classes. 
    Persistent types must derive from the class Entity or some subclass of Entity. Persistent Fields are declared as properties with the [Field] attribute. 
    In an inheritance hierarchy the first or root class must be marked with the [HierarchyRoot] Attribute and at least one [Field] property must be declared with the [Key] attribute to becaome primary key.
    Here we have a simple entity with an int key and a string data (payload) field:</p>
    <pre lang="cs">[Serializable]
[HierarchyRoot]
public class SimpleEntity : Entity
{
    [Field, Key]
    public int Id { get; private set; }

    [Field(Length = 100)]
    public string Data { get; set; }
}   
</pre>
    These SimpleEntity objects aren't related in any way. The HierarchyRoot 
    attribute denote this class as a base for a hierarchy. Hierarchy in this context 
    means base class for inheritance in the object oriented meaning. 
    Next example shows how classes are derived from this base class:&nbsp;&nbsp;
    <pre lang="cs">[Serializable]
[HierarchyRoot]
public class BaseEntity : Entity
{
    [Field, Key]
    public int Id { get; private set; }

    [Field(Length = 100)]
    public string BaseEntityData { get; set; }
}

[Serializable]
public class DerivedEntity : BaseEntity
{
    [Field(Length = 100)]
    public string DerivedEntityData { get; set; }
}
    </pre>
    <p>
        As shown, the derived class inherits the normal C# way from the base class. It 
        inherits all base properties and the primary key from the base class. Your 
        can make an educated guess that all the casting stuff base &lt;-&gt; derived will work the normal C# 
        way. Working with this persistent objects is almost the same as working with normal objects. 
        In the next section a typical parent/child or sometimes called master/detail relation is modeled:
    </p>
    <pre lang="cs">[Serializable]
[HierarchyRoot]
public class ParentEntity : Entity
{
    [Field, Key]
    public int Id { get; private set; }

    [Field]
    public EntitySet&lt;ChildEntity&gt; Childs { get; private set; }
}

[Serializable]
[HierarchyRoot]
public class ChildEntity : Entity
{
    public ChildEntity( ParentEntity parent )
    {
        Parent = parent;
    }

    [Field, Key]
    public long Id { get; private set; }

    [Field]
    [Association(PairTo = &quot;Childs&quot;, OnTargetRemove = OnRemoveAction.Cascade)]
    public ParentEntity Parent { get; private set; }
}
    </pre>
    <p>
        The ParentEntity class is 
        almost straight forward. In the ChildEntity we see a ParentEntity 'Parent' 
        field, where a reference to the parent is stored. And we see some business logic 
        in form of an Association attribute. In this case the association pairs the 
        field 'Parent' and the EntitiySet 'Childs'. Every ChildEntity which has 
        a particular parent is automatically in in the Childs EntitySet and vice versa. 
        With the 'OnTargetRemove = OnRemoveAction.Cascade' action, childs are 
        automatically deleted when a parent is deleted. To explain all the business 
        rule features of DataObjects.Nent goes far behind the scope of this 
        introduction. But keep in mind that those things are possible with 
        DataObjects.Net. Both classes are marked with the HierarchyRoot attribute 
        because both classes are base classes (roots of an inheritenace hierarchy).</p>
        <p>
            in order to increase performance on queries it is also possible to specify indices on some fields. 
            They are declared with the Index attribute as shown in the next sample.</p>
<pre lang="cs">
[Serializable]
[HierarchyRoot]
[Index("Data")]
public class IndexedDataEntity : Entity
{
    [Field]
    [Key]
    public int Id { get; private set; }

    [Field]
    public int Data { get; set; }
}
</pre>
    <h2>
        Basic Operations</h2>
    <p>
        After setting up a Domain and declaring a Model it is time to run some 
        operations. Although DataObjects.Net allows a very natural way to access data, 
        it fully supports sessions and transactions.
        </p>

    <h3>
        Sessions and Transactions,
        Creation and Destruction of Persistent Objects&nbsp;</h3>
    <p>
        The next sample shows how sessions and transactions are used programmatically. 
        Especcially for transactions this can also be done in a declarative way using 
        attributes.</p>
        <pre lang="cs">using (Session.Open(domain))
{
    using (var transactionScope = Transaction.Open())
    {
        new SimpleEntity { Data = &quot;SimpleEntity 1&quot; };
        new SimpleEntity { Data = &quot;SimpleEntity 2&quot; };
        transactionScope.Complete();
    }
}        </pre>
        <p>
The use of the IDisposable pattern (using) guarantees that the session and transaction is closed in case of an exception. The SimpleEntity objects are really persisted in this sample 
            without providing any transaction or session to them.
            To achieve this the framework uses a hidden (thread static) current session and current transaction. 
            the current session and current transaction are automatically set by the Open() 
            methods. 
            In the next sample a ParentEntity and ChildEntity objects will be created. Due 
            to the constructor of the ChildEntity class it is impossible to create 
            ChildEntity objects without a parent. DataObjects.Net supports the 
            constructor semantic of normal C# classes.</p>
    <pre lang="cs">
using (Session.Open(domain))
{
    using (TransactionScope transactionScope = Transaction.Open())
    {
        var parent = new ParentEntity();
        new ChildEntity(parent);
        new ChildEntity(parent);
        new ChildEntity(parent);
        transactionScope.Complete();
    }
}
    </pre>
    <p>
    As shown, it is very easy to create persistent objects with DataObjects.Net. It is also no problem to remove or delete such an object.
    It is enough to call the Remove() member function on the object. 
    We should bear in mind, that these newly created objects will last forever or at least till the Remove() method is called.
    But when our local variables ran out of scope or our application is terminated, how can we get our objects again? 
    The answer is simple: We use LINQ (language integrated query) to get our objects from the database. This is shown in the next section.
    </p>
    <h3>
        Querying Persistent Objects with LINQ&nbsp; &nbsp; &nbsp;
    </h3>
    <p>
        The starting point of every DataObjects.Net query is Query.All&lt;&gt;. The next query 
        returns all DerivedEntity objects in an arbitrary order.&nbsp;
    </p>
    <pre lang="cs">
using (Session.Open(domain))
{
    var linqQuery = from entity in Query.All&lt;DerivedEntity&gt;() select entity;
    foreach (var entity in linqQuery)
    {
        // Do some work with the objects
    }
}   </pre>
    <p>

        Once we have an object, we can use the references and collections in the normal 
        C# way. In the next example we iterate over the Child collection in ParentEntity 
        objects.</p>
    <pre lang="cs">
using (Session.Open(domain))
{
    var linqQuery = from entity in Query.All&lt;ParentEntity&gt;() select entity;
    foreach (var parentEntity in linqQuery)
    {
        foreach( var childEntity in parentEntity.Childs )
        {
            // Do some work with the childs and parents
        }
    }
}   </pre>

    <p>
        DataObjects.Net has an almost complete LINQ support. The query in the next 
        sample is more complex. It put a condition (where) on the query and sorts the 
        result (orderby).</p>
    <pre lang="cs">
using (Session.Open(domain))
{
    var linqQuery = from entity in Query.All&lt;IndexedDataEntity&gt;() 
        where entity.Data < 1000 
        orderby entity.Data 
        select entity;
    foreach (var entity in linqQuery)
    {
        // Do some work with the selected objects
    }
}
    </pre>
    <p>
    In fact LINQ queries can be much more complex. But that goes far behind this introduction.
    </p>
<h2>Conclusion</h2>
<p>
We use DataObjects.Net 4.0 and PostgreSQl in a new project with about 60 persistent classes and currently 10 million entities.
At the beginning we had some problems with the performance of the PostgreSql server. 
But after configuring adequate cache size (the defaults are very low) and some minor opimizations it is really fast and and is comparable to the Microsoft SQL server in almost all cases.
We are working with PostgreSQl 9.0 Beta4 64Bit. I am quite impressed how fast and easy database application developement can be.
To put it in a nutshell: DataObjects.net can be a good choice to speed up the developement process especially for new projects.
</p>
<!-- End Article -->


</div> 
</body>

</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO
Germany Germany
I'm a Senior Software Consultant
Thomas Maierhofer Consulting

Comments and Discussions