about software programming techniques


JJ van Zon 2023

📀 ORM

back

An ORM aims to make it easier to focus on the logic around entity objects, while saving things to a database is pretty much done for you.

Contents

Introduction

This article covers specific anomalies you may encounter while using an ORM. It also offers suggestions, how to deal with it.

This article won’t go into the basics of using ORM. There’s other resources for that.

This information was gathered from experience, built up with NHibernate. It might be possible that other ORM's have similar issues, due to how ORM's work internally.

Binary Fields

You might not want to map binary and other serialized data fields using ORM, because it can harm performance quite a bit.

Retrieving some loose fields of an entity, would also retrieve a blob in that case. As well as saving a whole blob, when changing just a few fields. That data transmission can be quite a bottle-neck sometimes, especially in a multi-user environment.

Using separate SQL statements for retrieving blobs might be a better alternative.

Read-Write Order

It seems ORM's like it when you first read the data out, and then start writing to it. Not read, write some, read a little more, write some more. It may have to do with its way of querying the database, caching things and how it handles committed and uncommitted objects.

Bridge Entities

An bridge entity applies to n => n relationships and may require an additional table to make the link between the entities:

Using an ORM, the bridge entity might not be visible in the code, but can be managed as two collections inside the two main entities:

class Question
{
    IList<Category> Categories { get; set; }
}

class Category
{
    IList<Question> Questions { get; set; }
}

The ORM can do quite a bit of magic under the hood, to keep these collections in sync. Perhaps a little too much for its own good. You might expect quite a few Exceptions to go off, while ORM tries to guard the integrity of the relationship.

These problems almost all go away, if you map a bridge entity instead. This turns the n => n relationship into two 1 => n relationships which ORM can manage with less hardship. You can let both entities hold a list of bridge entities instead. In turn, the bridge entity would link back to the two main entities:

class QuestionCategory
{
    Question Question { get; set;}
    Category Category { get; set;}
}

class Question
{
    IList<QuestionCategory> QuestionCategories { get; set; }
}

class Category
{
    IList<QuestionCategory> CategoryQuestions { get; set; }
}

This also has the advantage, that the entity model would not need to be refactored, if you’d want to add properties to a combination of things.

It might be advised, that the bridge table not rely on a composite key of the two ID's. A single surrogate ID might do better:

This is because it gives 1 handle to the combination of 2 thing. This gives ORM less difficulty managing things under the hood, prevents passing around composite keys, lower quality hash codes, URLs that don’t look pretty, etc.

Inheritance

Particular surprises might emerge when using inheritance in your entity model at least while working with NHibernate. The main advice is to avoid inheritance at all in the entity models if you can.

Problem: Entity / Proxy Type Mismatch

When retrieving an entity through ORM, it will likely not return an instance of your entity type, but an instance of a type derived from your entity, a so called Proxy. This Proxy adds to your entity a sort of connectedness to the database.

Problem: Base Proxy / Derived Proxy Type Mismatch

When you retrieved an entity from NHibernate that has inheritance, using the base type it returns a Proxy of the base type instead of a Proxy of the derived type, which makes reference comparisons between base Proxies and derived class Proxies fail.

Problem: 2 Proxies / 1 Entity

But you can also get failing reference comparisons another way. If you Unproxied a derived type, and retrieve another Proxy of the derived type, reference comparison might also fail.

Problem: Query Performance

It can also harm performance of queries, getting a lot of left joins: one for each derived class’ table.

Alternative: Unproxy for Reference Comparison

You can then Unproxy both and it will return the underlying object, which is indeed of the derived class, upon which reference comparison succeeds.

Alternative: Unproxy for Type Evaluation

To evaluate the type, you are better of Unproxying as well. Otherwise it will compare Proxy types instead of your entity type. This can be confusing.

Alternative: ID Comparison

ID comparison could avoid this problem that surrounds entity equality checks.

Alternative: 1-to-1 Relationship

An alternative for inheritance might be, to use a 1-to-1 related object to represent the base of the entity. Although, NHibernate and other ORM's are not a fan of 1 => 1 relationships either. What may save the day, is to map the relationship one-way only and not bidirectionally, so the ORM gets less confused.

Alternative: Interfaces

Letting two entity types use a mutual interface might be an alternative too.

Alternative: No Inheritance

By now maybe it may be clear, that the main advice is not to use inheritance in the first place in your entity models, if at all possible.

Generic Interfaces

Data access in this architecture is favored behind generic interfaces from JJ.Framework.Data.

Uncommitted Objects

Here is something that happens in ORM sometimes:

Some methods of data retrieval work with uncommitted / non-flushed entities: so things that are newly created, and not yet committed to the data store. Other methods of data retrieval do the opposite: only returning committed / flushed entities. This asymmetry might be common in ORM's, since doing it another way might harm performance considerably:

Method Data Read
IContext.Query committed
IContext.Get 1st committed, then uncommitted
IContext.TryGet 1st committed, then uncommitted
Navigation properties /
following the object graph
1st committed, then uncommitted

It appears to have to do with, when the ORM goes to the database to query for objects.

Flush

Flushing in NHibernate would mean that all the pending SQL statements are executed onto the database, without committing the transaction yet.

A Flush can help get an auto-generated ID from the database. Also, sometimes when NHibernate is confused about the order in which to execute things, a Flush may help it execute things in the right order.

The trouble with Flush is, that it might be executed when things are not done yet, and incomplete data might go to the database, upon which database may give an error. So it is a thing to use sparsely only with a good reason, because you can expect some side-effects.

Flushes might also go off automatically. Sometimes NHibernate wants to get a data-store generated ID. This can happen calling Save on an entity. Unlike the documentation suggests, FlushMode.Never or FlushMode.Commit may not prevent these intermediate flushes.

Upon saving a parent object, child objects might be flushed too. Internally then NHibernate asked itself the question if the child object was Transient and while doing so, it apparently wanted to get its identity, by executing an insert statement onto the data store. This once caused a null Exception on the child object’s ParentID column.

It may also help to create entities in a specific order (e.g. parent object first, child objects second) or choose an identity generation scheme, that does not require flushing an entity pre-maturely (like a Database Sequence or Guids).

Entity Framework

Entity Framework is a framework for data access, a so called ORM (Object Relational Mapper). Entity Framework might be hidden behind abstractions using JJ.Framework.Data.EntityFramework and repository interfaces.

At one point we noticed a slow down in JJ.Framework.Data.EntityFramework. But it hadn’t even been modified. Probably caused by an upgrade to a newer version of Entity Framework. Unfortunately JJ.Framework.Data.EntityFramework was not upgraded since then. The reason was most apps used NHibernate instead.

When using Entity Framework, transactions might not work unless you enable MSDTC (Microsoft Distributed Transaction Coordinator). That is a Windows service belonging to the SQL Server installation.

NHibernate

NHibernate is a technology used for data access. A so called ORM (Object Relational Mapper). It is comparable to Entity Framework.

NHibernate is used in some projects, because an employer favored it, and other projects joined the club.

NHibernate might be hidden behind abstractions using JJ.Framework.Data.NHibernate and repository interfaces.

Conclusion

If all this makes you lose grip on reality and wonder whether ORM's are really worth it? Well, they can be. They allow you to program focusing on the meaning of things, rather than how to store it. Even though that is ambiguous because the story above suggests you’d still be better off knowing what it does and how it does it. You just don’t need to do it yourself anymore.

back