Metadata
MongoObject supports custom metadata types for storing document-level information separate from your business data, such as versioning, timestamps, and ownership.
Overview
Metadata in MongoObject is stored separately from your document data in the MongoDocument<T> wrapper:
public class MongoDocument<T>
{
public string Id { get; set; } // MongoDB _id
public T? Document { get; set; } // Your business data
public BsonDocument Metadata { get; set; } // Metadata (version, timestamps, etc.)
}
Defining a Metadata Type
Create a partial record to define your metadata fields:
public partial record UserMeta
{
public string? CreatedBy { get; set; }
public string? Department { get; set; }
public DateTime? LastLoginAt { get; set; }
public int LoginCount { get; set; }
}
Then associate it with your document using the MetadataType property:
[MongoObject(
CollectionName = "Users",
DatabaseName = "MyApp",
MetadataType = typeof(UserMeta)
)]
public partial class User
{
public partial string Name { get; set; }
public partial string Email { get; set; }
}
Automatic Metadata Fields
MongoObject automatically manages certain metadata fields:
Version
Every document has an auto-incrementing version number:
// Automatically updated on each SaveChanges()
{
"Metadata": {
"Version": 5 // Incremented automatically
}
}
The update pipeline includes:
"Metadata.Version": { $add: [{ $ifNull: ["$Metadata.Version", 0] }, 1] }
LastModifiedAt
Timestamp of the last modification:
// Automatically set using MongoDB's $$NOW
{
"Metadata": {
"LastModifiedAt": ISODate("2026-01-15T10:30:00Z")
}
}
CreatedAt
Timestamp at the time of Creation
{
"Metadata": {
"CreatedAt": ISODate("2026-01-15T10:30:00Z")
}
}
Generated Metadata Types
When you specify a MetadataType, the source generator creates:
1. Metadata Query Type
For searching documents by metadata:
// Auto-generated
public class UserMetaQuery : IMetadataSearchBase
{
public string? CreatedBy { get; set; }
public string? Department { get; set; }
public DateTime? LastLoginAt { get; set; }
public int? LoginCount { get; set; }
}
2. Metadata Record Type
For storing metadata values:
// Auto-generated
public class UserMetaRecord : IMetadataBase
{
public string? CreatedBy { get; set; }
public string? Department { get; set; }
public DateTime? LastLoginAt { get; set; }
public int LoginCount { get; set; }
}
Setting Metadata on Creation
When adding a new document, you can set initial metadata values:
var user = new User
{
Name = "John Doe",
Email = "john@example.com"
};
// Add with metadata
await monitor.Add(user, metadata =>
{
metadata.CreatedBy = "admin";
metadata.Department = "Engineering";
});
Querying by Metadata
Use the generated metadata query type to search documents:
// Find all users in the Engineering department
var users = await monitor.MetadataSearch<UserMetaQuery>(meta =>
{
meta.Department = "Engineering";
});
// Find documents created by a specific user
var myDocs = await monitor.MetadataSearch<UserMetaQuery>(meta =>
{
meta.CreatedBy = currentUser.Id;
});
Metadata Best Practices
1. Keep Metadata Simple
Metadata should contain simple, serializable values:
// ✓ Good - simple types
public partial record DocumentMeta
{
public int Version { get; set; }
public DateTime CreatedAt { get; set; }
public string? OwnerId { get; set; }
public DocumentStatus Status { get; set; } // Enum is fine
}
// ✗ Avoid - complex nested objects
public partial record DocumentMeta
{
public List<AuditEntry> AuditTrail { get; set; } // Too complex
public Dictionary<string, object> CustomData { get; set; } // Not type-safe
}
2. Use Metadata for Cross-Cutting Concerns
public partial record AuditableMeta
{
// Ownership
public string? CreatedBy { get; set; }
public string? ModifiedBy { get; set; }
// Timestamps
public DateTime CreatedAt { get; set; }
// Soft delete
public bool IsDeleted { get; set; }
public DateTime? DeletedAt { get; set; }
// Multi-tenancy
public string? TenantId { get; set; }
}
3. Don't Duplicate Document Data
// ✗ Bad - duplicating document data in metadata
public partial record UserMeta
{
public string? UserName { get; set; } // Already in User.Name
}
// ✓ Good - metadata contains supplementary info
public partial record UserMeta
{
public string? CreatedBy { get; set; }
public DateTime? LastLoginAt { get; set; }
}
Example: Complete Metadata Setup
// 1. Define metadata type
public partial record ArticleMeta
{
public string? AuthorId { get; set; }
public string? Category { get; set; }
public ArticleStatus Status { get; set; }
public DateTime? PublishedAt { get; set; }
public int ViewCount { get; set; }
}
public enum ArticleStatus
{
Draft,
Review,
Published,
Archived
}
// 2. Define document with metadata
[MongoObject(
CollectionName = "Articles",
DatabaseName = "Blog",
MetadataType = typeof(ArticleMeta)
)]
public partial class Article
{
public partial string Title { get; set; }
public partial string Content { get; set; }
public partial List<string> Tags { get; set; }
}
// 3. Use in service
public class ArticleService
{
private readonly IDocumentMonitor<Article> _monitor;
private readonly IDocumentMonitorInternal<Article> _monitorInternal;
public async Task PublishArticle(string articleId, string publisherId)
{
var article = await _monitor.Get(articleId);
// Update document
article.Status = ArticleStatus.Published;
// Save with metadata update
await _monitorInternal.Add(article, meta =>
{
meta.Status = ArticleStatus.Published;
meta.PublishedAt = DateTime.UtcNow;
meta.ModifiedBy = publisherId;
});
}
public async Task<IEnumerable<Article>> GetPublishedArticles()
{
return await _monitorInternal.MetadataSearch<ArticleMetaQuery>(meta =>
{
meta.Status = ArticleStatus.Published;
});
}
}
Next Steps
- Searching - Learn about document and metadata search
- Change Tracking - Understand how metadata versioning works
- Dependency Injection - Configure metadata options