MongoFramework Documentation

repository·main·Indexed 18 days ago

https://github.com/turnersoftware/mongoframework

An 'Entity Framework'-like ORM for MongoDB in C# built on the official MongoDB C# driver. It provides high-level abstractions for entity mapping via attributes or a fluent API, index management (including compound, text, and geospatial), and change tracking with diff-updates. Key features include entity buckets for high-density data, runtime type discovery for polymorphic types, and specialized query extensions for text and geospatial searches.

Tokens
3.4K
Snippets
8
Records
10
Agent score
14%

What's inside MongoFramework

  1. Overview of MongoFramework features

    main

    MongoFramework is an "Entity Framework"-like interface for MongoDB built on top of the official MongoDB C# driver. It provides several high-level abstractions to simplify MongoDB development in .NET:

    • Entity Mapping: Map collections, IDs, and properties using attributes or a fluent API.
    • Indexing: Define single-field, compound, and multikey indexes via attributes or fluent API (including text and geospatial).
    • Change Tracking: Includes entity change tracking, changeset support (batching updates), and diff-updates (writing only changed fields).
    • Entity Buckets: Implements the bucket pattern to cluster small documents into larger ones for improved performance.
    • Runtime Type Discovery: Automatically handles serialization/deserialization of polymorphic types without needing to pre-register all known subtypes.
    • Special Queries: Built-in support for text search and geospatial (intersecting and near) queries.
  2. Handle Extra Elements in BSON

    main

    If your MongoDB documents contain fields that are not defined in your C# model, you can control how they are handled:

    1. Ignore them: Apply the [IgnoreExtraElements] attribute to the entity class.
    2. Capture them: Apply the [ExtraElements] attribute to a property of type IDictionary<string, object>. This will map all unknown fields into that dictionary.
  3. Use Entity Buckets for high-density data

    main

    Entity Buckets allow you to store many small documents inside a single larger document to improve index performance and reduce storage overhead.

    To use them, define a MongoDbBucketSet<TGrouping, TItem> in your context and decorate it with the [BucketSetOptions] attribute.

    • bucketSize: The maximum number of items allowed in a single bucket.
    • entityTimeProperty: The name of the property in the sub-entity (TItem) that stores the timestamp.

    Note: Managing buckets is currently limited to add-only operations via AddRange.

    public class MyBucketGrouping
    {
      public string SensorId { get; set; }
      public DateTime Date { get; set; }
    }
    
    public class MyBucketItem
    {
      public DateTime EntryTime { get; set; }
      public int Value { get; set; }
    }
    
    public class MyContext : MongoDbContext
    {
      public MyContext(IMongoDbConnection connection) : base(connection) { }
      [BucketSetOptions(bucketSize: 1000, entityTimeProperty = nameof(MyBucketItem.EntryTime))]
      public MongoDbBucketSet<MyBucketGrouping, MyBucketItem> MyBuckets { get; set; }
    }
    
    // Usage
    using (var context = new MyContext(MongoDbConnection.FromConnectionString("mongodb://localhost:27017/MyDatabase")))
    {
      context.MyBuckets.AddRange(new MyBucketGrouping
      {
        SensorId = "ABC123",
        Date = DateTime.Parse("2020-04-04")
      }, new []
      {
        new MyBucketItem { EntryTime = DateTime.Parse("2020-04-04T01:00"), Value = 123 },
        new MyBucketItem { EntryTime = DateTime.Parse("2020-04-04T02:00"), Value = 456 }
      });
    
      await context.SaveChangesAsync();
    }
  4. Configure indexes via Fluent Mapping or Attributes

    main

    Indexes are applied to properties and are synchronized with the database when the context is saved.

    Attribute Mapping

    Use the [Index] attribute on properties. You can specify the index name and sort order using IndexSortOrder.

    public class IndexExample
    {
      public string Id { get; set; }
    
      [Index("Email", IndexSortOrder.Ascending)]
      public string EmailAddress { get; set; }
    
      public string Name { get; set; }
    }

    Fluent Mapping

    Use HasIndex within the OnConfigureMapping method. For compound indexes, define multiple properties with the same index name. You can control the order of fields in a compound index using IndexPriority (for attributes) or by the order of declaration/configuration.

    mappingBuilder.Entity<IndexExample>()
      .HasIndex(e => e.EmailAddress, b => b.HasName("Email").IsDescending(false));

    Compound Indexes

    To create a compound index, ensure multiple properties share the same index name. For complex structures or arrays, you can use an anonymous object in the fluent API:

    mappingBuilder.Entity<TestModel>()
      .HasIndex(m => new
      {
        m.SomethingIndexable,
        m.OneOfThem.Description,
        m.ManyOfThem.First().AnotherThingIndexable
      }, b => {
        b.HasName("MyIndex")
          .IsDescending(true, false, false)
      });

    Special Index Types

    MongoFramework supports Text and 2dSphere indexes. For attribute mapping, set the IndexType property on the [Index] attribute.

  5. Initialize MongoDbContext and IMongoDbConnection

    main

    The MongoDbContext is the central point for interacting with your database. It requires an IMongoDbConnection to be instantiated. You can create a connection using a URL or a connection string from the official MongoDB driver.

    // 1. Create the connection
    IMongoDbConnection connection = MongoDbConnection.FromConnectionString("mongodb://localhost:27017/MyDatabase");
    
    // 2. Instantiate the context
    using (var context = new MyContext(connection))
    {
        // Use context.MyEntities, etc.
    }
    IMongoDbConnection connection;
    
    //FromUrl
    connection = MongoDbConnection.FromUrl(new MongoUrl("mongodb://localhost:27017/MyDatabase"));
    
    //FromConnectionString
    connection = MongoDbConnection.FromConnectionString("mongodb://localhost:27017/MyDatabase");
  6. Map entities using Fluent Mapping

    main

    You can configure how entities map to MongoDB collections and how properties map to BSON elements using the MappingBuilder inside your MongoDbContext.OnConfigureMapping override.

    Use .Entity<T>() to start configuration, .ToCollection("Name") to specify the collection, and .HasProperty(expr, config) to remap property names.

    using MongoFramework;
    
    public class MyEntity
    {
      public string Id { get; set; }
      public string Name { get; set; }
      public string Description { get; set; }
    }
    
    public class MyContext : MongoDbContext
    {
      public MyContext(IMongoDbConnection connection) : base(connection) { }
      public MongoDbSet<MyEntity> MyEntities { get; set; }
    
      protected override void OnConfigureMapping(MappingBuilder mappingBuilder)
      {
        mappingBuilder.Entity<MyEntity>()
          .HasProperty(m => m.Name, b => b.HasElementName("MappedName"))
          .ToCollection("MyCustomEntities");
      }
    }
  7. Map entities using Attributes

    main

    Alternatively, you can use attributes directly on your entity classes to define mapping behavior. Many core attributes are part of the System.ComponentModel.Annotations package.

    AttributeDescription
    [Table("Name", Schema = "Namespace")]Maps the entity to a specific collection. If Schema is provided, it is prefixed with a . separator.
    [Key]Marks a property as the entity's ID. Required if the property name is not a standard ID name (e.g., Id).
    [NotMapped]Skips the class or property during read/write operations.
    [Column("Name")]Remaps the property to a different element name in BSON.
    using MongoFramework;
    using System.ComponentModel.DataAnnotations;
    
    [Table("MyCustomEntities")]
    public class MyEntity
    {
      public string Id { get; set; }
      [Column("MappedName")]
      public string Name { get; set; }
      public string Description { get; set; }
    }
    
    public class MyContext : MongoDbContext
    {
      public MyContext(IMongoDbConnection connection) : base(connection) { }
      public MongoDbSet<MyEntity> MyEntities { get; set; }
    }
  8. Implement a complete MongoFramework workflow

    main

    To use MongoFramework, you need to define your entities, create a context class inheriting from MongoDbContext, and manage operations through a MongoDbSet<T>.

    1. Define an Entity: Create a class with properties (e.g., Id, Name).
    2. Create a Context: Inherit from MongoDbContext and expose your entities as MongoDbSet<T> properties.
    3. Initialize Connection: Use MongoDbConnection.FromConnectionString to establish a connection.
    4. Perform Operations: Use the context to query entities via LINQ and persist changes using SaveChangesAsync().
    using MongoFramework;
    using System.ComponentModel.DataAnnotations;
    
    public class MyEntity
    {
      public string Id { get; set; }
      public string Name { get; set; }
      public string Description { get; set; }
    }
    
    public class MyContext : MongoDbContext
    {
      public MyContext(IMongoDbConnection connection) : base(connection) { }
      public MongoDbSet<MyEntity> MyEntities { get; set; }
    }
    
    // Usage
    var connection = MongoDbConnection.FromConnectionString("YOUR_CONNECTION_STRING");
    using (var myContext = new MyContext(connection))
    {
      var myEntity = myContext.MyEntities.Where(myEntity => myEntity.Name == "MongoFramework").FirstOrDefault();
      myEntity.Description = "An 'Entity Framework'-like interface for MongoDB";
      await myContext.SaveChangesAsync();
    }
  9. Enable Runtime Type Discovery for Polymorphism

    main

    To support deserializing polymorphic types (where a property might be a base class but the actual stored document is a derived class) without manually registering every subtype via BsonKnownTypes, use the [RuntimeTypeDiscovery] attribute.

    Apply [RuntimeTypeDiscovery] to the base class. MongoFramework will then automatically discover and handle any classes that inherit from it during runtime.

    [RuntimeTypeDiscovery]
    public class KnownBaseModel
    {
    }
    
    public class UnknownChildModel : KnownBaseModel
    {
    }
    
    public class UnknownGrandChildModel : UnknownChildModel
    {
    }
  10. Perform Special Queries (Text and Geospatial)

    main

    MongoFramework provides extension methods on MongoDbSet<T> to perform specialized MongoDB queries. These return IQueryable<T>, allowing you to chain standard LINQ operators.

    • Text Search: Use .SearchText("query") (requires a Text index).
    • Geospatial Intersecting: Use .SearchGeoIntersecting(expr, polygon) (requires a 2dSphere index).
    • Geospatial Near: Use .SearchGeoNear(expr, point) (requires a 2dSphere index).
    myContext.MyDbSet.SearchText("text to search");
    myContext.MyDbSet.SearchGeoIntersecting(e => e.FieldWithCoordinates, yourGeoJsonPolygon);
    myContext.MyDbSet.SearchGeoNear(e => e.FieldWithCoordinates, yourGeoJsonPoint);