Document Identity
Besides being serializable, Marten's only other requirement for a .Net type to be a document is the existence of an identifier field or property that Marten can use as the primary key for the document type. The Id
can be either a public field or property, and the name must be either id
or Id
or ID
. As of this time, Marten supports these Id
types:
String
. It might be valuable to use a natural key as the identifier, especially if it is valuable within the Identity Map feature of Marten Db. In this case, the user will be responsible for supplying the identifier.Guid
. If the id is a Guid, Marten will assign a new value for you when you persist the document for the first time if the id is empty. And for the record, it's pronounced "gwid".CombGuid
is a sequential Guid algorithm. It can improve performance over the default Guid as it reduces fragmentation of the PK index.Int
orLong
. As of right now, Marten uses a HiLo generator approach to assigning numeric identifiers by document type. Marten may support Postgresql sequences or star-based algorithms as later alternatives.- When the ID member of a document is not settable or not-public a
NoOpIdGeneration
strategy is used. This ensures that Marten does not set the ID itself, so the ID should be generated manually. - A
Custom
ID generator strategy is used to implement the ID generation strategy yourself.
Marten by default uses the identity value set on documents and only assigns one in case it has no value (Guid.Empty
, 0
, string.Empty
etc).
INFO
When using a Guid
/CombGuid
, Int
, or Long
identifier, Marten will ensure the identity is set immediately after calling IDocumentSession.Store
on the entity.
You can see some example id usages below:
cs
public class Division
{
// String property as Id
public string Id { get; set; }
}
public class Category
{
// Guid's work, fields too
public Guid Id;
}
public class Invoice
{
// int's and long's can be the Id
// "id" is accepted
public int id { get; set; }
}
Overriding the Choice of Id Property/Field
If you really want to or you're migrating existing document types from another document database, Marten provides the [Identity]
attribute to force Marten to use a property or field as the identifier that doesn't match the "id" or "Id" or "ID" convention:
cs
public class NonStandardDoc
{
[Identity]
public string Name;
}
The identity property or field can also be configured through StoreOptions
by using the Schema
to obtain a document mapping:
cs
storeOptions.Schema.For<OverriddenIdDoc>().Identity(x => x.Name);
Guid Identifiers
INFO
As of Marten 1.0, the default Guid mechanism is a sequential or "Comb" Guid. While more expensive to generate, this makes inserts into the underlying document tables more efficient.
To use CombGuid generation you should enabled it when configuring the document store. This defines that the CombGuid generation strategy will be used for all the documents types.
cs
options.Policies.ForAllDocuments(m =>
{
if (m.IdType == typeof(Guid))
{
m.IdStrategy = new CombGuidIdGeneration();
}
});
It is also possible use the SequentialGuid id generation algorithm for a specific document type.
cs
options.Schema.For<UserWithGuid>().IdStrategy(new CombGuidIdGeneration());
Sequential Identifiers with Hilo
The Hilo sequence generation can be customized with either global defaults or document type specific overrides. By default, the Hilo sequence generation in Marten increments by 1 and uses a "maximum lo" number of 1000.
To set different global defaults, use the StoreOptions.HiloSequenceDefaults
property like this sample:
cs
var store = DocumentStore.For(_ =>
{
_.Advanced.HiloSequenceDefaults.MaxLo = 55;
_.Connection(ConnectionSource.ConnectionString);
_.DatabaseSchemaName = "sequences";
});
It's also possible to use one sequence with multiple document types by specifying the same "sequence name".
cs
var store = DocumentStore.For(_ =>
{
_.Advanced.HiloSequenceDefaults.SequenceName = "Entity";
_.Connection(ConnectionSource.ConnectionString);
_.DatabaseSchemaName = "sequences";
});
To override the Hilo configuration for a specific document type, you can decorate the document type with the [HiloSequence]
attribute as in this example:
cs
[HiloSequence(MaxLo = 66, SequenceName = "Entity")]
public class OverriddenHiloDoc
{
public int Id { get; set; }
}
You can also use the MartenRegistry
fluent interface to override the Hilo configuration for a document type as in this example:
cs
var store = DocumentStore.For(_ =>
{
// Overriding the Hilo settings for the document type "IntDoc"
_.Schema.For<IntDoc>()
.HiloSettings(new HiloSettings {MaxLo = 66});
_.Connection(ConnectionSource.ConnectionString);
_.DatabaseSchemaName = "sequences";
});
Set the HiLo Identifier Floor
Marten 1.2 adds a convenience method to reset the "floor" of the Hilo sequence for a single document type:
cs
var store = DocumentStore.For(opts =>
{
opts.Connection(ConnectionSource.ConnectionString);
opts.DatabaseSchemaName = "sequences";
});
// Resets the minimum Id number for the IntDoc document
// type to 2500
await store.Tenancy.Default.Database.ResetHiloSequenceFloor<IntDoc>(2500);
This functionality was added specifically to aid in importing data from an existing data source. Do note that this functionality simply guarantees that all new id's assigned for the document type will be higher than the new floor. It is perfectly possible and even likely that there will be some gaps in the id sequence.
String Identity
If you use a document type with a string
identity member, you will be responsible for supplying the identity value to Marten on any object passed to any storage API like IDocumentSession.Store()
. You can choose to use the Identity Key option for automatic identity generation as shown in the next section.
Identity Key
WARNING
The document alias is also used to name the underlying Postgresql table and functions for this document type, so you will not be able to use any kind of punctuation characters or spaces.
Let's say you have a document type with a string
for the identity member like this one:
cs
public class DocumentWithStringId
{
public string Id { get; set; }
}
You can use the "identity key" option for identity generation that would create string values of the pattern [type alias]/[sequence]
where the type alias is typically the document class name in all lower case and the sequence is a HiLo sequence number.
You can opt into the identity key strategy for identity and even override the document alias name with this syntax:
cs
var store = DocumentStore.For(opts =>
{
opts.Connection("some connection string");
opts.Schema.For<DocumentWithStringId>()
.UseIdentityKey()
.DocumentAlias("doc");
});
Custom Identity Strategies
A custom ID generator strategy should implement IIdGeneration.
cs
public class CustomIdGeneration : IIdGeneration
{
public IEnumerable<Type> KeyTypes { get; } = new Type[] {typeof(string)};
public bool RequiresSequences { get; } = false;
public void GenerateCode(GeneratedMethod assign, DocumentMapping mapping)
{
var document = new Use(mapping.DocumentType);
assign.Frames.Code($"_setter({{0}}, \"newId\");", document);
assign.Frames.Code($"return {{0}}.{mapping.IdMember.Name};", document);
}
}
The Build()
method should return the actual IdGenerator<T>
for the document type, where T
is the type of the Id field.
For more advances examples you can have a look at existing ID generator: HiloIdGeneration, CombGuidGenerator and the IdentityKeyGeneration,
To use custom id generation you should enabled it when configuring the document store. This defines that the strategy will be used for all the documents types.
cs
options.Policies.ForAllDocuments(m =>
{
if (m.IdType == typeof(string))
{
m.IdStrategy = new CustomIdGeneration();
}
});
It is also possible define a custom id generation algorithm for a specific document type.
cs
options.Schema.For<UserWithString>().IdStrategy(new CustomIdGeneration());