Massive

Massive is a simple, dynamic .NET data access library. It achieves ultimate lightweight design with single-file implementation and zero dependencies, making it optimized for prototyping and small projects. With dynamic typing and simple API, it's suitable for rapid development and REPL environment usage.

ORMC#DynamicMicro-ORMSimpleLightweight.NET

GitHub Overview

FransBouma/Massive

A small, happy, dynamic MicroORM for .NET that will love you forever.

Stars1,792
Watchers108
Forks323
Created:February 15, 2011
Language:C#
License:Other

Topics

None

Star History

FransBouma/Massive Star History
Data as of: 7/19/2025, 09:28 AM

Library

Massive

Overview

Massive is a simple, dynamic .NET data access library. It achieves ultimate lightweight design with single-file implementation and zero dependencies, making it optimized for prototyping and small projects. With dynamic typing and simple API, it's suitable for rapid development and REPL environment usage.

Details

Massive 2025 edition continues to be used in projects prioritizing simplicity. It is valued for prototyping and REPL environment development, maintaining the spirit of Rob Conery's original Massive while being maintained by Frans Bouma. Supporting SQL Server, PostgreSQL, MySQL, and SQLite, its dynamic ExpandoObject-based approach allows flexible adaptation to schema changes.

Key Features

  • Single File Implementation: Complete in just one C# file
  • Zero Dependencies: No external libraries required
  • Dynamic Typing: Flexible data access through ExpandoObject
  • Simple API: Intuitive with minimal learning cost
  • Multi-database: Support for SQL Server, PostgreSQL, MySQL, SQLite
  • Lightweight: Minimal memory footprint

Pros and Cons

Pros

  • Extremely easy to implement (just copy the file)
  • Nearly flat learning curve
  • Perfect for prototyping
  • Flexible adaptation to schema changes
  • Suitable for REPL environment usage
  • Minimal overhead for small projects

Cons

  • Lack of type safety
  • Limited IntelliSense support
  • Not suitable for large projects
  • Few performance optimization options
  • No advanced ORM features (relationship management, etc.)
  • Lack of enterprise features

References

Examples

Basic Setup

// Just add Massive.cs file to your project
// No NuGet package required

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using Massive;

// Define table classes
public class Users : DynamicModel
{
    public Users() : base("BlogConnection", "Users", "Id") { }
}

public class Posts : DynamicModel
{
    public Posts() : base("BlogConnection", "Posts", "Id") { }
}

// app.config / appsettings.json
// <connectionStrings>
//   <add name="BlogConnection" 
//        connectionString="Server=localhost;Database=BlogDB;..." />
// </connectionStrings>

Basic CRUD Operations

public class BasicOperations
{
    public static void DemoCRUD()
    {
        var users = new Users();
        
        // CREATE - Create new record
        // Create with dynamic object
        var newUser = new ExpandoObject() as dynamic;
        newUser.Name = "John Doe";
        newUser.Email = "[email protected]";
        newUser.Age = 30;
        newUser.CreatedAt = DateTime.Now;
        
        var userId = users.Insert(newUser);
        Console.WriteLine($"Created user with ID: {userId}");
        
        // Can also create with anonymous object
        var anotherUser = users.Insert(new {
            Name = "Jane Smith",
            Email = "[email protected]",
            Age = 25,
            CreatedAt = DateTime.Now
        });
        
        // READ - Read records
        // Get single record
        dynamic user = users.Single(userId);
        Console.WriteLine($"User: {user.Name}, Email: {user.Email}");
        
        // Get all records
        var allUsers = users.All();
        foreach (dynamic u in allUsers)
        {
            Console.WriteLine($"{u.Name} ({u.Age})");
        }
        
        // Conditional search
        var adults = users.All(where: "Age >= @0", args: 18);
        var searchResults = users.All(
            where: "Name LIKE @0 OR Email LIKE @0", 
            args: "%John%"
        );
        
        // UPDATE - Update record
        user.Age = 31;
        user.UpdatedAt = DateTime.Now;
        users.Update(user, userId);
        
        // Partial update
        users.Update(new { Age = 32 }, userId);
        
        // DELETE - Delete record
        users.Delete(userId);
        
        // Conditional delete
        users.Delete(where: "CreatedAt < @0", args: DateTime.Now.AddYears(-1));
    }
}

Advanced Query Operations

public class AdvancedQueries
{
    private readonly Users _users = new Users();
    private readonly Posts _posts = new Posts();
    
    // Pagination
    public IEnumerable<dynamic> GetPagedUsers(int page, int pageSize)
    {
        var offset = (page - 1) * pageSize;
        
        // SQL Server
        var sql = @"
            SELECT * FROM Users 
            ORDER BY Name 
            OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY";
        
        return _users.Query(sql, offset, pageSize);
    }
    
    // Aggregate queries
    public dynamic GetUserStatistics()
    {
        var sql = @"
            SELECT 
                COUNT(*) as TotalUsers,
                AVG(CAST(Age as float)) as AverageAge,
                MAX(Age) as MaxAge,
                MIN(Age) as MinAge
            FROM Users
            WHERE Age IS NOT NULL";
        
        return _users.Query(sql).FirstOrDefault();
    }
    
    // JOIN operations
    public IEnumerable<dynamic> GetPostsWithAuthors()
    {
        var sql = @"
            SELECT 
                p.Id,
                p.Title,
                p.Content,
                p.CreatedAt,
                u.Name as AuthorName,
                u.Email as AuthorEmail
            FROM Posts p
            INNER JOIN Users u ON p.AuthorId = u.Id
            ORDER BY p.CreatedAt DESC";
        
        return _posts.Query(sql);
    }
    
    // Grouping
    public IEnumerable<dynamic> GetPostCountByUser()
    {
        var sql = @"
            SELECT 
                u.Id,
                u.Name,
                COUNT(p.Id) as PostCount
            FROM Users u
            LEFT JOIN Posts p ON u.Id = p.AuthorId
            GROUP BY u.Id, u.Name
            ORDER BY PostCount DESC";
        
        return _users.Query(sql);
    }
    
    // Dynamic condition building
    public IEnumerable<dynamic> SearchUsers(
        string name = null, 
        int? minAge = null, 
        int? maxAge = null)
    {
        var conditions = new List<string>();
        var parameters = new List<object>();
        
        if (!string.IsNullOrEmpty(name))
        {
            conditions.Add("Name LIKE @" + parameters.Count);
            parameters.Add($"%{name}%");
        }
        
        if (minAge.HasValue)
        {
            conditions.Add("Age >= @" + parameters.Count);
            parameters.Add(minAge.Value);
        }
        
        if (maxAge.HasValue)
        {
            conditions.Add("Age <= @" + parameters.Count);
            parameters.Add(maxAge.Value);
        }
        
        var where = conditions.Any() 
            ? "WHERE " + string.Join(" AND ", conditions) 
            : "";
        
        var sql = $"SELECT * FROM Users {where} ORDER BY Name";
        
        return _users.Query(sql, parameters.ToArray());
    }
    
    // Batch operations
    public void BatchInsertUsers(List<dynamic> users)
    {
        // Batch insert within transaction
        using (var conn = _users.OpenConnection())
        using (var trans = conn.BeginTransaction())
        {
            try
            {
                foreach (var user in users)
                {
                    _users.Insert(user, conn, trans);
                }
                trans.Commit();
            }
            catch
            {
                trans.Rollback();
                throw;
            }
        }
    }
    
    // Custom SQL execution
    public int ExecuteCustomCommand(string sql, params object[] args)
    {
        return _users.Execute(sql, args);
    }
}

Practical Usage Example

// RESTful API usage example
using Microsoft.AspNetCore.Mvc;
using System.Dynamic;

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly Users _users = new Users();
    
    [HttpGet]
    public IActionResult GetUsers(
        [FromQuery] string search,
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 10)
    {
        try
        {
            var offset = (page - 1) * pageSize;
            var where = string.IsNullOrEmpty(search) 
                ? "" 
                : "WHERE Name LIKE @2 OR Email LIKE @2";
            
            var sql = $@"
                SELECT * FROM Users {where}
                ORDER BY Name 
                OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY";
            
            var users = _users.Query(sql, offset, pageSize, $"%{search}%");
            
            // Get total count
            var countSql = $"SELECT COUNT(*) as Count FROM Users {where}";
            var totalCount = string.IsNullOrEmpty(search)
                ? _users.Scalar(countSql)
                : _users.Scalar(countSql, $"%{search}%");
            
            Response.Headers.Add("X-Total-Count", totalCount.ToString());
            
            return Ok(users);
        }
        catch (Exception ex)
        {
            return StatusCode(500, new { error = ex.Message });
        }
    }
    
    [HttpGet("{id}")]
    public IActionResult GetUser(int id)
    {
        try
        {
            var user = _users.Single(id);
            if (user == null)
                return NotFound();
            
            return Ok(user);
        }
        catch (Exception ex)
        {
            return StatusCode(500, new { error = ex.Message });
        }
    }
    
    [HttpPost]
    public IActionResult CreateUser([FromBody] CreateUserDto dto)
    {
        try
        {
            dynamic user = new ExpandoObject();
            user.Name = dto.Name;
            user.Email = dto.Email;
            user.Age = dto.Age;
            user.CreatedAt = DateTime.UtcNow;
            
            var id = _users.Insert(user);
            user.Id = id;
            
            return CreatedAtAction(nameof(GetUser), new { id }, user);
        }
        catch (Exception ex)
        {
            return StatusCode(500, new { error = ex.Message });
        }
    }
    
    [HttpPut("{id}")]
    public IActionResult UpdateUser(int id, [FromBody] UpdateUserDto dto)
    {
        try
        {
            var existing = _users.Single(id);
            if (existing == null)
                return NotFound();
            
            _users.Update(new
            {
                Name = dto.Name,
                Email = dto.Email,
                Age = dto.Age,
                UpdatedAt = DateTime.UtcNow
            }, id);
            
            return NoContent();
        }
        catch (Exception ex)
        {
            return StatusCode(500, new { error = ex.Message });
        }
    }
    
    [HttpDelete("{id}")]
    public IActionResult DeleteUser(int id)
    {
        try
        {
            var result = _users.Delete(id);
            if (result == 0)
                return NotFound();
            
            return NoContent();
        }
        catch (Exception ex)
        {
            return StatusCode(500, new { error = ex.Message });
        }
    }
}

// DTO classes
public class CreateUserDto
{
    public string Name { get; set; }
    public string Email { get; set; }
    public int? Age { get; set; }
}

public class UpdateUserDto
{
    public string Name { get; set; }
    public string Email { get; set; }
    public int? Age { get; set; }
}

// Helper class
public static class MassiveExtensions
{
    // Convert dynamic object to typed object
    public static T ToTyped<T>(this dynamic obj) where T : new()
    {
        var typed = new T();
        var dict = obj as IDictionary<string, object>;
        
        if (dict != null)
        {
            var properties = typeof(T).GetProperties();
            foreach (var prop in properties)
            {
                if (dict.ContainsKey(prop.Name) && dict[prop.Name] != null)
                {
                    prop.SetValue(typed, dict[prop.Name]);
                }
            }
        }
        
        return typed;
    }
    
    // Convert dynamic object list to typed list
    public static List<T> ToTypedList<T>(this IEnumerable<dynamic> list) 
        where T : new()
    {
        return list.Select(item => ToTyped<T>(item)).ToList();
    }
}

// Service class usage example
public class BlogService
{
    private readonly Users _users = new Users();
    private readonly Posts _posts = new Posts();
    
    public async Task<dynamic> CreateBlogPost(
        string title, 
        string content, 
        int authorId)
    {
        // Verify user exists
        var author = _users.Single(authorId);
        if (author == null)
            throw new InvalidOperationException("Author not found");
        
        // Create post
        var post = new ExpandoObject() as dynamic;
        post.Title = title;
        post.Content = content;
        post.AuthorId = authorId;
        post.CreatedAt = DateTime.UtcNow;
        post.ViewCount = 0;
        
        var postId = _posts.Insert(post);
        
        // Return created post
        return _posts.Query(@"
            SELECT 
                p.*,
                u.Name as AuthorName
            FROM Posts p
            INNER JOIN Users u ON p.AuthorId = u.Id
            WHERE p.Id = @0", postId).FirstOrDefault();
    }
    
    public IEnumerable<dynamic> GetPopularPosts(int limit = 10)
    {
        return _posts.Query(@"
            SELECT TOP(@0)
                p.Id,
                p.Title,
                p.ViewCount,
                p.CreatedAt,
                u.Name as AuthorName,
                (SELECT COUNT(*) FROM Comments WHERE PostId = p.Id) as CommentCount
            FROM Posts p
            INNER JOIN Users u ON p.AuthorId = u.Id
            ORDER BY p.ViewCount DESC, p.CreatedAt DESC", limit);
    }
    
    public void IncrementViewCount(int postId)
    {
        _posts.Execute(
            "UPDATE Posts SET ViewCount = ViewCount + 1 WHERE Id = @0", 
            postId);
    }
}