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

Subscribe on YouTube