Fork me on GitHub

Foreign Keys Edit on GitHub


Marten is built on top of a relational database, so why not take advantage of those abilities where they still add value? In this case, Marten allows for a special kind of "Searchable" column that also adds a foreign key constraint to enforce referential integrity between document types.

One of our sample document types in Marten is the Issue class that has a couple properties that link to the id's of related User documents:


public class Issue
{
    public Issue()
    {
        Id = Guid.NewGuid();
    }

    public string[] Tags { get; set; }

    public Guid Id { get; set; }

    public string Title { get; set; }

    public int Number { get; set; }

    public Guid? AssigneeId { get; set; }

    public Guid? ReporterId { get; set; }
}

If I want to enforce referential integrity between the Issue document and the User documents, I can use this syntax shown below to configure Marten:


var store = DocumentStore.For(_ =>
{
    _.Connection("some database connection");

    // In the following line of code, I'm setting
    // up a foreign key relationship to the User document
    _.Schema.For<Issue>().ForeignKey<User>(x => x.AssigneeId);
});

With the configuration above, Marten will make an assignee_id field in the database table and build a foreign key constraint to the User document like so:

ALTER TABLE public.mt_doc_issue
ADD CONSTRAINT mt_doc_issue_assignee_id_fkey FOREIGN KEY (assignee_id)
REFERENCES public.mt_doc_user (id);

CREATE INDEX mt_doc_issue_idx_assignee_id ON public.mt_doc_issue ("assignee_id");

And some other things you probably want to know about how this works internally:

Marten is smart enough to order the "upsert" operations to make the dependent documents be updated last. In the Issue referencing User example above, this means that if you create a new User and a new Issue in the same session, when you call IDocumentSession.SaveChanges()/SaveChangesAsync(), Marten will know to save the new user first so that the issue will not fail with referential integrity violations.

Configuring with Attributes

You can optionally configure properties or fields as foreign key relationships with the [ForeignKey] attribute:


public class Issue
{
    public Issue()
    {
        Id = Guid.NewGuid();
    }

    public Guid Id { get; set; }

    [ForeignKey(typeof(User))]
    public Guid UserId { get; set; }

    public Guid? OtherUserId { get; set; }
}