Practical ASP.NET

ORM-Less Data Storage with Document Databases and Marten

Document databases are a form of NoSQL database that may store all of the information for a given object in a single instance in the database. Contrast this with relational databases that might store a given object across multiple tables in the database.

Marten is a .NET open source library that allows the easy storage, loading, updating and deleting of objects in an underlying document store. The underlying store that Marten is built on is the open source PostgreSQL database server.

Marten makes use of the advanced JSON capabilities of PostgreSQL to store .NET objects as JSON data.

Marten can improve developer productivity by not requiring complex schema changes as the application evolves, as can be the case with using a relational database. Marten also doesn't require the up-front definition of indexes for the .NET objects/JSON data that will be stored.

Getting Started
Marten can be installed into a Visual Studio project via the Marten NuGet package.

Once installed, Marten can talk to a PostgreSQL instance and store .NET objects.

The central point of communication between the application and Marten is a DocumentStore instance. This DocumentStore defines a connection to a PostgreSQL instance in addition to other Marten configuration options. Normally there's only going to be a single instance of the document store in the application. To create a DocumentStore instance, the static For method can be called and a connection string provided.

The actual work of storing, loading, updating and deleting documents is done in the context of a session. There are a number of different types of sessions and these can easily be created from a document store instance. For example, to create a session that allows documents in the database to be manipulated, the LightweightSession method can be called and it will return an IDocumentSession instance that can be used to work with documents. This code demonstrates the creation of a DocumentStore and a session:

DocumentStore store = DocumentStore.For(
  "host = localhost; database = RPGDatabase; password = g7qo84nck22i; username = postgres");

using (IDocumentSession session = store.LightweightSession())
{
  // Document manipulation code
}

Defining Documents
The .NET objects in the application are stored as documents by Marten. The basic requirement is that the .NET objects that are to be stored need an identity field or property. One way to define this is to follow a naming convention and name the property/field "Id." This code shows the definition of a Player class to represent a player in a role-playing game:

class Player
{
  public int Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public IList<Weapon> Weapons { get; set; } = new List<Weapon>();
}

internal class Weapon
{
  public string WeaponName { get; set; }
  public int WeaponDamage { get; set; }
}

The Player class also has a nested list of weapons that the player currently possesses.

Now instances of players can be stored in the session. Listing 1 shows the code required to store a new instance.

Listing 1: The Code Required to Store a New Instance
using (IDocumentSession session = store.LightweightSession())
{
  Player newPlayer = new Player
  {
    Name = "Krondure",
    Weapons =
    {
      new Weapon {WeaponName = "Sword", WeaponDamage = 42},
      new Weapon {WeaponName = "Axe", WeaponDamage = 230}
    }
  };
  // Add the object to the session
  session.Store(newPlayer);

  // Update database
  session.SaveChanges();
}

In the underlying PostgreSQL table, a new row will be written to the mt_doc_player table with a field containing the following document data:

{"Weapons":[{"WeaponDamage":42,"WeaponName":"Sword"},
  {"WeaponDamage":230,"WeaponName":"Axe"}],"Id":1,"Name":"Krondure"}

To update an existing document using an automatic change-tracking session, the following code could be used, which loads the player with an Id of 1, updates the name and then saves those changes:

// Create a session with auto-change-tracking
using (IDocumentSession session = store.DirtyTrackedSession())
{
  Player player = session.Load<Player>(1);

  player.Name = "Krondure the magnificent";
                
  session.SaveChanges();
}

To learn more about Marten, check out the GitHub documentation page.

About the Author

Jason Roberts is a Microsoft C# MVP with over 15 years experience. He writes a blog at http://dontcodetired.com, has produced numerous Pluralsight courses, and can be found on Twitter as @robertsjason.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube