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

  • IDE Irony: Coding Errors Cause 'Critical' Vulnerability in Visual Studio

    In a larger-than-normal Patch Tuesday, Microsoft warned of a "critical" vulnerability in Visual Studio that should be fixed immediately if automatic patching isn't enabled, ironically caused by coding errors.

  • Building Blazor Applications

    A trio of Blazor experts will conduct a full-day workshop for devs to learn everything about the tech a a March developer conference in Las Vegas keynoted by Microsoft execs and featuring many Microsoft devs.

  • Gradient Boosting Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the gradient boosting regression technique, where the goal is to predict a single numeric value. Compared to existing library implementations of gradient boosting regression, a from-scratch implementation allows much easier customization and integration with other .NET systems.

  • Microsoft Execs to Tackle AI and Cloud in Dev Conference Keynotes

    AI unsurprisingly is all over keynotes that Microsoft execs will helm to kick off the Visual Studio Live! developer conference in Las Vegas, March 10-14, which the company described as "a must-attend event."

  • Copilot Agentic AI Dev Environment Opens Up to All

    Microsoft removed waitlist restrictions for some of its most advanced GenAI tech, Copilot Workspace, recently made available as a technical preview.

Subscribe on YouTube