The Essentials of ORM Framework in Your Software Development

Michael Pendon
18 min readJul 20, 2020

--

The extent of this post is generally speaking the whole ORM family running from different technology stacks. However, in this article, I will limit the explanation and samples only to C# programming language.

Photo by Lucas Bellator on Unplash

After reading this article, you will be familiarized with the reasons why an ORM is an important piece on your Software Development. All the intentions will be supported by relevant technical details together-with the explanation of the benefits of why use an ORM.

This article is divided into 3 parts. First, I will explain the basic benefits of an ORM. Secondly, I will explain the important factors for you to understand the edge advantages of using an ORM. Lastly, I will explain the lower-level technicalities of how the ORM works behind the scene. This article is targeting everyone, but the latter sections are quite important if you are a Developer and/or Software Architect.

Disclaimer: I am the author of RepoDb, therefore some of the samples on this article is specific to this ORM, but again, the rationale of this post is beyond the technology stack and library you are prefer to work with. Think of all the written samples as the code for your favorite ORM.

Let us Start with the Basics. What is an ORM?

It has 2 meaning, one is for “mapper” as a framework, and the other one is for “mapping” as a concept.

In the case of “mapper”, the ORM abbreviates the Object/Relational Mapper. It is a framework that you can embed within your application to manage the mapping of the database resultsets and the application class objects (vice-versa). The conversion applies even with or without the entity relationships.

In the case of “mapping”, the ORM abbreviates the Object/Relational Mapping. It is the concepts that explains of how the ORM should work & behave, and how it is being implemented in overall. It also explains the high-level perspective of what ORM “as a mapper” is really about.

Image below shows the high-level diagram of an Application Landscape with ORM.

Let us say, you made a query from the Customer table with the code below.

SELECT Id, Name, Address FROM Customer WHERE Name = ‘John Doe’;

An ORM will then convert the result of the above query into a Customer class within your application. The selected columns Id, Name and Address will be mapped to the class properties named Id, Name and Address respectively. On the other hand, if you push a data towards the database, an ORM will map the properties of the class into the columns of the target table. The value will then be saved or updated accordingly based on these mappings.

As I am only explaining the basics, I would recommend to visit this article if you would like to understand further what is an ORM in both areas.

# Part 1 — The Benefits ✔️

In this part, the sections covered are all about the benefits of ORM. The information written is in purpose to persuade you to consider using an ORM on your Software Development. Let us begin!

ORM does…

1. Improves your Development Agility

Most ORM are packed with the necessary features in order for us to do the basic things. These features comes with different capabilities, addressing the use-cases from the different areas and/or domains. Same goes to your end, when you develop a Software Project, you are implementing its features and capabilities to address the use-cases requested by your customers.

As ORM is designed to address the problem in the Data Access Layers, it then as well by-default addressing your own problem on that area by being a consumer of this framework. Imagine inheriting the certain numbers of features that does the job for you without you even writing a code for it, and that you are required to implement these features without an ORM.

A Data Access Layer is commonly known as Repository, the object that mediates the mapping layer of your application and your database

To be specific, querying a record from the database requires certain number of code, like below.

And most of the code will be eliminated if with ORM, like below.

No just that, by using an ORM, it also helps you write simple unit of code and with uniformity. All the methods and functions are contextually pre-given with the names equivalent to its purpose. Those making you more faster to implement the important methods needed for your business cases.

I also would like to highlight the importance of ORM in the Microservice Architecture, commonly known as Microservices.

Microservice Architecture

Image above is taken from this link

As you know, the Microservice Architecture is known to be the most popular Software Architectural Design nowadays. Majority of the companies tend to develop a micro component in relation to this architecture. It is because, by doing such thing, it would sustain the fast-changing demands of the customers. This does not limit whether the application is running “On-premise or Cloud”, “Windows or Linux” and/or etc.

Micro component — it is a single running service within the Microservice Architecture. It has its own state and is independent from the other components.

An ORM is predominantly important on this area. It is an edge-case where it is beneficial to you “the most” as it improves the agility of your development within each micro-component. By having it as a tool during your development, you will eliminate the effort of writing the fundamentals of your Data Access Layer. It also important to take note that an ORM can be utilized to all other components of your Microservice Architecture without even concerning about the versions and/or etc.

Without using an ORM, all the fundamentals of your repository must first be implemented in every micro-component, and with this, your development is much slower. On top of this, the code-base is much complex and larger than it is with ORM. Not to mention, your code will most likely be duplicated if being used to the other use-cases and/or micro-components, which is against the DRY principle.

2. Eliminates your Code Complexity

Image above is taken from this link

In your application, you need a foundation of your Data Access Layers. By having a lower-level implementation of this area is an additional layer of complexity. Why not just utilize and leverage the ORM?

Imagine writing the full sets of C.R.U.D. operations with an average of 25 lines of code per operation. Whereas, writing it with ORM only takes 2 lines of code whilst accomplishing the same job. The difference is huge in relation to your effort.

Below are the drawbacks (in specific area) you will be facing if you are not using an ORM in your Software Development.

  • Maintainability— your application is becoming more difficult to maintain as you are to implement the fundamentals of your repository. The lines of code is most likely double-in-size than the number of code you can write if with ORM. Always bare in mind, the more code you write, the harder the maintenance is.
  • Code Duplication — there is a higher chance that the code you write may be duplicated to accommodate other data models for different use-cases and/or different micro components.
  • Re-usability — your code will most likely be not reusable from the other components of the Microservices. Other team may prefer a different style or standard of doing the “things” than you (or your team).
  • Extensibility — the code that you will write will mostly be closed for extensibility. It is because, without an ORM, you are also close to writing a code in relation to your own use-cases “without” thinking of the other use-cases. Though, “I am not generalizing this”.

Most of the drawbacks mentioned above are related to the SOLID principle, a very important principle when writing a software application with Object-Oriented Programming paradigm. It is just an overkill to implement everything from scratch, even for just a simple Insert/Update operation. Try to eliminate the mentioned drawbacks during your Software Development by simply using an ORM.

3. Helps You Write Lesser Code and Do More Features

Though this could be an extension of the first item above, but it explain the things in a more detailed.

An ORM, by default, mostly provides the initial features that accommodate the basic operations out-of-the-box. By using it, you are automatically inheriting all the those features. You also automatically skipped the tedious part of your Software Development pertaining to the Data Relational Management. Again, without using an ORM, you are responsible of building the fundamentals of your repository, which is quiet tedious in most cases.

The time and effort you spent building the foundation of your Data Access Repository could somehow be utilized implementing the actual business requirements. Enabling you to instantly work with the actual use-cases that gives more value to the customers.

To be specific, if you are to implement your own way, then below is the code for inserting a new Customer record via ADO.NET.

While below are the code when using an ORM.

The code above only refers to Insert operation, a basic operation that saves a data into the database. Imagine the effort you are going to spent if you are to implement the following operations just to build the foundation of your repository.

  • Read, Update, Delete operations— just to complete the C.R.U.D.
  • Batch and Bulk operations
  • Caching, Join operations
  • Many more here…

In addition to that, what if your requirement is different in every entity model? Not to mention the requirements in every micro-component.

The Importance of ORM in the Repository

As repository is an integral part of your application, therefore it is important that this area is consistent and free-of-error. It is also best to make this area clean and simple (i.e.: lesser lines of code, simple unit of methods, etc).

In repository, you introduce all the necessary operations needed by your application (already mentioned above). So it is common to this area to move fast, as the new operations will be introduced in response to the new requirements requested. However, it is a big problem if you are to implement everything from scratch specially if you have the requirements that limits you make this area “generic” and “dynamic”.

Imagine, you are working with clustered environment and you introduce some operations on your repository to cater this use-case, specifically for the tables where the Primary Keys are Identities. Few weeks later, a new requirement is needed where you need to connect to a non-clustered environment and/or maybe creating a support to a non-identity table to the existing clustered environment. Then you introduce specialized operations to cater it. There you wrote multiple sets of code to cater multiple requirements. It is just too much for the preliminary work as it drags you completely to slowness.

By having an ORM, you will skip all these preliminary work. You just have to leverage the built-in features like BaseRepository/DbContext and everything will just work like a charm, accomplishing the same job as your intention.

That being said, using an ORM has a huge advantage when being compared to “not using an ORM at all”.

4. Covers your Maintenance Responsibility

One important factor of why choose an ORM over implementing your own is the advantage of “responsibility” in the maintenance.

Image above is taken from this link

Do you prefer to fix the problems yourself? If yes, can you afford fixing the problems? If not, then use an ORM. Time is money, with ORM they do it for you for free.

As you know, ORM maintainers are there to dedicate their time fixing the problems pertaining to the Data Access Layers. This in short explain that you itself are leveraging their time fixing the problem for you, without asking for return. By having this privilege, you can just work directly on your business problems without worrying the other side of the coin.

# Part 2 — The Factors ✔️

In this part, the covered sections are more about the important factors to consider for you to use an ORM. These factors will give you an edge advantages when an ORM is used on your Software Development. Let us begin!

ORM is…

1. More Intelligent

This factor is related to the internal-logic implied within the ORM over the logic that you can write when writing yours.

You might didn’t know, most ORM understand the schema of your database. When you call any of the built-in operation, an ORM will most likely speak to your database to retrieve the necessary information pertaining to the schema of the target table. This is only happening once. The information is then saved in the memory of your computer, where the application runs.

By having such schema information, the ORM will use it to create an optimized compiled-function for your operation, giving you the most-performant and efficient method for future executions.

Disclaimer: I am not generalizing the above concepts to all ORM, but most ORM does it.

This means that, without you knowing behind the scene, an ORM is executing a special logic implemented internally to give you the desired compiled-operations. These special logic are:

  • Ahead-of -Time (AOT) Compilation
  • Skips the Database NULL-Checks
  • Column/Property Projection
  • Type Extraction/Conversion
  • Cache Re-usability

In the case of not using an ORM, you are most likely leaving the mentioned logic above behind, it is because you are only concentrating delivering your own business cases. Though, it is completely fine for as long you can afford implementing it, money and time wise. But it is highly recommended to utilize an ORM for you to leverage and inherit this package by default.

2. More Performant

This factor is related to the speed of the execution of an ORM, over the speed of the execution of your own implementation.

I often heard that if you would like to make it more-efficient and fast, then use the “raw” implementation. This statement is not truthful. There is no correct answer until you write the “perfect” code yourself.

Image above is taken from this link

Referring to performance, the main reason to this is the number of execution paths (aka branching points). In order for the application to be more performant, the number of execution path must be lessen. If you write your own fundamental implementation, you are most likely writing more lines of code. The more code you write, the more execution paths to execute, resulting to a slower application.

As you know, ORM is built to address the real-world problem pertaining to RDBMS. As “performance” is a basic problem in this space, you can guarantee that this use-case is by default addressed by ORM in many ways. Mentioned in the previous section, ORM gives you the best ahead-of-time (AOT) compilation, NULL-checks and/or etc. With these techniques, the number of execution paths were most likely reduced to its lowest number possible.

Note: A compiled code is difficult to read and maintain, therefore, the compilation is mainly used for library and framework only, like an ORM. You as a developer should not create a compiled-code during the Software Development to avoid the code-complexity.

Unless you are considering the mentioned techniques above while writing your own lower-level implementation, there is no way you can beat the ORM in terms of performance. Though, I will not generalize this to all good developers that could create the best code.

You can read the detailed explanations in the latter part of this article.

3. More Efficient

This factor is related to the efficiency of the code written in ORM when compared to the code that you can write.

Image above is taken from this link

One of the most important factor to sustain the stability of your application is when it is efficient. Efficiency is referring to the “how” the computer memory is being utilized. The lower the consumption, the better.

In general, if we accidentally have written a leaky code, it would result to a higher memory consumption in the application servers. Same goes to your end. The effect, your application may stop while in the middle of the operations.

Do you matter how the code is written in your end? I suppose not, so do to me. Though, I am not generalizing this.

To be specific, in C#, do you use string interpolation or the legacy low-level method like string.Contact()? Using a string interpolation allocates more memory than using the lower level implementation, so do for the other non-lower level implementations. It is a same question to the developers who are using the other programming language.

An ORM is always more efficient due to the information it got from your database, and also, the compilation techniques it utilizes when building the compiled functions. Not just that, a series of test is being performed to measure the memory usage before the actual production delivery.

To support the claims, an ORM is most-likely a well-tested framework (end-to-end) with the tools provided by the community itself. This tool often called an ORM Bencher and is used to measure both the performance and efficiency of various ORM that is being used by the community. The good part here, through this tool, most ORM authors keeps improving the framework to be always on the top, which is a free benefit to you as a user.

4. Higher in Quality

This factor is related to the stability of the code the ORM has over the customized code you can write.

It is important to know that most ORM comes with package of Unit and Integration Tests. These are covering the real-world scenarios and are used to guard the features written on the ORM. So that, any developer (like you) who is using an ORM is free of error.

In addition, most ORM authors are continuously collecting the issues from the community, have those issues fixed by them and embed on the framework. After the fixes, a new dedicated tests suite will be written to guard the newly embedded scenarios.

In the case of usability, various developers (not just you) are using the same ORM framework, but in a different use-case. In that way, the ORM is also being tested beyond your own scenario.

I am trying explain the reality where the position of ORM really is. As most ORM is open-source, the community and handful of developers are directly improving it, making the framework more stable and capable of addressing certain use-cases.

Do you usually do the mentioned activities above when writing your own implementation? If not, then that is being said, an ORM implementation is always more mature than your own implementation.

Image above is taken from this link

I am grateful that you made this far. You already have digested most of my intentions on this article in relation to ORM. However, it would be better if you can read further to understand the low-level explanation of the technical implementation behind the ORM as a framework.

# Part 3 — The Technicalities ✔️

Further readings below will be specific to C# language (or .NET as a whole). I will explain the low-level technicalities of how the ORM is being implemented in .NET point-of-view.

In addition to this and being an author of an ORM, I will do explain and share the techniques that I used when implementing RepoDb. Though, most of the techniques I used are collective, conceptualized by me, colleagues and/or other ORM creators. To emphasize, most ORM are the same in technique, whether you use the ORM framework A over B, or whatever.

Note: Please bare in mind that I am blank whether the concepts to be discussed are present on the language that you are opt to use for Software Development.

1. Ahead-of -Time (AOT) Compilation

Ahead-of-Time (AOT) is a very low-level terminology which is common used in compiler.

Ahead-of-Time (AOT) compilation is a concept applied when compiling an application code. It is a technique used to enhance the performance of the code execution by pre-compiling a function during the runtime and utilize it for future use.

This compilation helps your application interacts with the lower-level code, commonly used as Intermediate Language, so it will be easily understood by the computer. The compiled code is resulted to a binary that is opt for the computer to understand and executed it natively, thus giving you a maximum performance when it comes to execution.

I would generally say, you are not doing a pre-compilation when writing your own code. It is just an overkill for you as a developer and as a maintainer. This technique only applies to a framework/library that is opt for everyone-to-use, not for a commercial Product or Software.

2. Skips the Database NULL-Checks

The DbDataReader.IsDbNull() method is known to be a slow method. In addition, by adding a condition that checks the class property NULL values is an additional execution path.

This is a a customized logic embedded to an ORM (not to all) that skips the unnecessary check when extracting a resultset back to a language native data type (vice versa).

To be specific, if you have a Customer table with 15 columns and 5 of the columns are required, then ORM are projecting those column to be excluded for NULL checking. Since ORM is speaking to your database when calling an operation, ORM knows how to project it automatically. Required columns are always having a value and by eliminating the NULL-check will improve the performance of the execution.

Though in general, you are most likely projecting that some columns of the table are already required and you do skipping it by default. But, we cannot do it perfectly to all cases (i.e.: all columns, all table, all components and/or use-cases).

3. Column/Property Projection

During the data extraction and transformation, ORM uses the class properties if the number is lower than the number of of table properties, otherwise it uses the table properties.

What do I mean by this? There is a case that your class object properties are not equal to the actual table columns, and with this, not all properties/columns can be mapped. When this happen, which is also a very basic use-case, ORM will identify whether the table columns or the class properties will be used for extraction. It then use that list in all execution paths, like…

  • Iteration/Skipping
  • Data Retrieval/Assignment
  • Method Compilation
  • Etc

As mentioned many times, behind the scene, ORM is pre-touching your database to get the schema. With the information of the class properties and table columns, ORM knows what/which columns to project in all compilation. This projection eliminates the unnecessary compilation on the compiled-function, resulting to a much more efficient and performant execution.

4. Type Extraction/Conversion

The best way to extract a data from the DbDataReader is through Get<Type>() methods.

Do you happen to always write code to use the Get<Type>() method of the DbDataReader? I would say, NOT.

By using that method is quietly tricky, it is not safe as well. Imagine you have the query below.

SELECT Id, Name, Address FROM [dbo].[Customer];

And happen to write the equivalent C# code below.

var list = new List<Customer>();
while (reader.Read())
{
var customer = new Customer();
customer.Id = reader.GetInt32(0);
customer.Name = reader.GetString(1);
customer.Address = reader.GetString(2);
customers.Add(customer);
}
return list;

The drawback to this is the data alignment. What will happen if the order of columns in the query selection has been modified by your colleague? Then, you also require to modify the underlying code that does the extraction. It is an just an overkill to use these methods.

The problem above is the “reason” why most developers are writing it using the indexer reader["PropertyName"] so the alignment will not be affected. The problem to this is the requirement of conversion. That method returns an object and you are required to cast it to the correct CLR Type, though underneath, the data type is already correct after your extraction. This conversion is an additional logic in your application that could result to a more memory-usage and slower execution.

In the case of ORM, it uses the Get<Type>() methods in all case of extraction and conversion. Since the ORM knows how to project the class properties and table columns, it also knows the position of the mapping. This projection and the call to the correct Get<Type>() method is then a part of the compilation, resulting to a much more robust compiled-function.

5. Cache Re-usability

To make the execution more faster and efficient, cache the compiled function and re-use it for future executions.

Lastly, compilation takes time, but why compile all the time if we can just cache it for future use? This is exactly what ORM is doing. Behind the scene, without you knowing it, ORM does place the already-generated compiled functions into the memory of the computer where the application runs. It then uses the same cached function when the same method is invoked again in the future.

Not just that, ORM also does the kind of caching below.

  • Type/Object Caching — where all object reflections are stored.
  • Schema Caching — where all retrieved schema from DB are stored.
  • Mapping Caching — where all the pre-defined project of the properties/columns are stored.
  • Property/Column Caching — where all the extracted type properties are stored.
  • SQL Text Caching — where all the already-generated operation command-texts are stored.
  • Primary/Identity Caching — where all the object primary/identity columns are stored.

It takes a little time to compile the functions as it requires memory allocation when instantiating the necessary objects (i.e.: Lists, Dictionary, etc), and also determining the execution paths. This is also the reason why most “first” execution in ORM is slow. However, once everything is compiled and cache, further execution would be extremely-fast.

I hope this article gives you enough information about an ORM and will help you decide to choose an ORM on your Software Development.

~ Thank you for reading this article!~

If you are interested in learning RepoDb, please visit any of the links below.

Or, you can visit the official blog of our great Scott Hanselman.

Lastly, the official documentation can be found at RepoDb.NET.

--

--

Michael Pendon

A Father | Author of RepoDb (ORM) | Senior Application Architect at Ørsted A/S (Denmark) | OSS Contributor | Technical Blogger | MCPD-EA