Practical ASP.NET

Enforce Referential Integrity Between Documents in Marten

Marten is PostgreSQL-based, sotake advantage of relational features where it makes sense. Here's an example.

Marten is an open source .NET document database library that allows the storage, loading, updating and deleting of objects as documents in an underlying PostgreSQL database.

Even though Marten stores .NET objects as JSON documents, because Marten is built on top of PostgreSQL (which is a relational database), it can take advantage of relational features where they make sense.

Suppose you have a role-playing game that has the classes as shown in Listing 1. In the Player class there's a reference to the Kingdom in which the player was born. This int should refer to a Kingdom document in the underlying document store.

Listing 1: Document Classes
class Player
{
  public int Id { get; set; }
  public string Name { get; set; }
  public int BirthKingdom { get; set; }
}

class Kingdom
{
  public int Id { get; set; }
  public string Name { get; set; }
  public string History { get; set; }
}

If you make use of the classes from Listing 1 to insert a new Player, even if the BirthKingdom is non-existent, there will be no error on insert. Listing 2 shows code to insert a new Player document.

Listing 2: Inserting a Player with an Invalid Kingdom Reference
DocumentStore store =
  DocumentStore.For(
    "host = localhost; database = RPGDatabase; password = g7qo84nck22i; 
    username = postgres");


using (IDocumentSession session = store.LightweightSession())
{
  Player newPlayer = new Player
  {
    Name = "Krondure",
    BirthKingdom = 123456 // non-existent Kingdom document
  };

  // Add the object to the session
  session.Store(newPlayer);

  // Update database
  session.SaveChanges(); // No error
}

Executing the code in Listing 2 results in the new Player document being stored without error.

Using Marten, it's possible to enforce the referential integrity between the Player and Kingdom. There are two methods to accomplish this.

Configuring Referential Integrity with Attributes
The first method is to decorate the relevant property with the Marten [ForeignKey] attribute. In the case of the Player class, this attribute would be applied to the BirthKingdom property and as a parameter to the attribute, the target document type is specified as shown in here:

{
  public int Id { get; set; }
  public string Name { get; set; }

  [ForeignKey(typeof(Kingdom))]
  public int BirthKingdom { get; set; }
}

Now, trying to insert a new Player with a non-existent Kingdom will result in an exception: insert or update on table mt_doc_player violates foreign key constraint mt_doc_player_birth_kingdom_fkey.

Configuring Referential Integrity with Document Stores
As an alternative to using the Marten [ForeignKey] attribute, the foreign key relationship can be configured at the document store level as shown here:

DocumentStore store =
  DocumentStore.For(configure =>
  {
    configure.Connection(
      "host = localhost; database = RPGDatabase; password = g7qo84nck22i; us
       ername = postgres");

    configure.Schema.For<Player>().ForeignKey<Kingdom>(on => on.BirthKingdom);
  });

Once again this will cause the aforementioned exception to be thrown when the Kingdom does not exist.

Inserting Document with Referential Integrity
To prevent the exception from being thrown, the BirthKingdom set in the new Player must refer to an existing Kingdom document Id. Notice in Listing 3 that it's possible to create both a new Kingdom and Player at the same time.

Listing 3: Inserting Related Documents
using (IDocumentSession session = store.LightweightSession())
{
  Kingdom newKingdom = new Kingdom
  {
    Name = "Island of Hrothdo're",
    History = "An island of mystery...."
  };

  session.Store(newKingdom);

  Player newPlayer = new Player
  {
    Name = "Krondure",
    BirthKingdom = newKingdom.Id
  };

  session.Store(newPlayer);

  // Update database
  session.SaveChanges();
}

When SaveChanges is called, Marten will insert the dependent document first -- the Kingdom document in this case -- before inserting the Player document.

To learn more about Marten, check out the Marten 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

  • Hands On: New VS Code Insiders Build Creates Web Page from Image in Seconds

    New Vision support with GitHub Copilot in the latest Visual Studio Code Insiders build takes a user-supplied mockup image and creates a web page from it in seconds, handling all the HTML and CSS.

  • Naive Bayes Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the naive Bayes regression technique, where the goal is to predict a single numeric value. Compared to other machine learning regression techniques, naive Bayes regression is usually less accurate, but is simple, easy to implement and customize, works on both large and small datasets, is highly interpretable, and doesn't require tuning any hyperparameters.

  • VS Code Copilot Previews New GPT-4o AI Code Completion Model

    The 4o upgrade includes additional training on more than 275,000 high-quality public repositories in over 30 popular programming languages, said Microsoft-owned GitHub, which created the original "AI pair programmer" years ago.

  • Microsoft's Rust Embrace Continues with Azure SDK Beta

    "Rust's strong type system and ownership model help prevent common programming errors such as null pointer dereferencing and buffer overflows, leading to more secure and stable code."

  • Xcode IDE from Microsoft Archrival Apple Gets Copilot AI

    Just after expanding the reach of its Copilot AI coding assistant to the open-source Eclipse IDE, Microsoft showcased how it's going even further, providing details about a preview version for the Xcode IDE from archrival Apple.

Subscribe on YouTube

Upcoming Training Events