A MongoDB database is an excellent choice when we want to build scalable and flexible applications without worrying about the database schema. MongoDB is a document database that gives us flexibility and makes developers’ lives easier, supporting multiple languages and platforms.
Are you looking to build scalable and flexible apps quickly? The combination of MongoDB and .NET provides a powerful solution.
MongoDB Installation
To get started, install the MongoDB Community Edition, which provides a local setup of a document database for testing, database creation, and queries.
- Download MongoDB: Go to the official MongoDB Community Edition download page and select the version for your operating system.
- Install and Run MongoDB: After installation, MongoDB will run as a service on your system, making it easy to connect and perform actions based on your application’s needs.
- Install MongoDB Compass: Download MongoDB Compass, a user-friendly interface for viewing and managing your data. This GUI tool simplifies database operations and allows you to visualize data and execute queries easily.
- Connect to MongoDB: Open MongoDB Compass, and click the “+” button at the top left corner, next to the CONNECTIONS section, to add a new MongoDB connection. By default, MongoDB uses port 27017.

Press Save & Connect and you will see a green connected database to start interacting with.
IoT Sensor Data Management System
With MongoDB running, it’s time to build our application. We will develop an IoT app that collects, stores, and analyzes data from environmental sensors.
We can support various less structured data types and schemas, allowing quick adaptations in any format. Scalability is not a problem, as Mongo can support large amounts of data with powerful real-time sensor data analysis capabilities.
Let’s create a new .NET web API project and the necessary Mongo library to start our development:
Document Model and Database Service
We’ll create a document model and a service for database connectivity and operations to manage sensor data efficiently.
First, we’ll define a MongoSettings class to hold our database connection settings:
123456 | public class MongoSettings
{
public string ConnectionURI { get; set; } = null!;
public string DatabaseName { get; set; } = null!;
public string CollectionName { get; set; } = null!;
} |
Next, we’ll define the schema for sensor data by creating a SensorData class:
123456789101112 | public class SensorData
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
public DateTime Timestamp { get; set; }
public double Temperature { get; set; }
public double Humidity { get; set; }
public double CO2 { get; set; }
public double ParticulateMatter { get; set; }
}
|
And the service that will be injected into our controllers to use Mongo:
12345678910 | public class MongoService
{
private readonly IMongoCollection<SensorData> _sensorDataCollection;
public MongoService(IOptions<MongoSettings> mongoDBSettings)
{
MongoClient client = new MongoClient(mongoDBSettings.Value.ConnectionURI);
IMongoDatabase database = client.GetDatabase(mongoDBSettings.Value.DatabaseName);
_sensorDataCollection = database.GetCollection<SensorData>(mongoDBSettings.Value.CollectionName);
}
}
|
By using the BsonId annotation, we designate which property serves as the unique identifier for the sensor data. Additionally, [BsonRepresentation(BsonType.ObjectId)] specifies the property’s type as an ObjectId.
After that, the data that will be used from MongoSettings will be found in our appsettings.json, which will match its model:
1234567891011121314 | {
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"MongoDB": {
"ConnectionURI": "mongodb://localhost:27017",
"DatabaseName": "sensordb",
"CollectionName": "sensors"
}
}
|
And finally, the service registration in Program.cs:
12 | builder.Services.Configure<MongoSettings>(builder.Configuration.GetSection("MongoDB"));
builder.Services.AddSingleton<MongoService>();
|
Crop Monitoring
Let’s explore a real-world application of IoT in agriculture: crop monitoring. IoT sensors have transformed how farmers monitor and maintain the health and life of their agriculture.
They can get early signs of multiple parameters like temperature and humidity, allowing them to respond quickly to changing conditions and protect crop health. As developers, we will create a core system that gathers and organizes this sensor data for analysis and visualization.
Let’s dive in.
Our app is the core system that will gather all the data from sensors, creating a data collection for analysis and visualization. We will begin by implementing a method in the SensorDataService class to handle incoming sensor data:
1234 | public async Task SaveDataAsync(SensorData sensorData)
{
await _sensorDataCollection.InsertOneAsync(sensorData);
}
|
SaveDataAsync method uses MongoDB’s asynchronous InsertOneAsync method to insert sensor readings as new documents in the collection.
In the same class, we will add another method to get sensor data by ID with the related fields. The GetDataByIdAsync method looks like this:
1234 | public async Task<SensorData?> GetDataByIdAsync(string id)
{
return await _sensorDataCollection.Find(data => data.Id == id).FirstOrDefaultAsync();
}
|
Using MongoDB’s Find method, this retrieves the first document that matches the specified ID.
Next, we have a more advanced requirement: calculating the average temperature recorded by a sensor within a specific time period. We will introduce Mongo Builders to help us build complex queries efficiently.
12345678 | public async Task UpdateSensorDataAsync(string id, double newTemperature, double newHumidity)
{
FilterDefinition<SensorData> filter = Builders<SensorData>.Filter.Eq(data => data.Id, id);
UpdateDefinition<SensorData> update = Builders<SensorData>.Update
.Set(data => data.Temperature, newTemperature)
.Set(data => data.Humidity, newHumidity);
await _sensorDataCollection.UpdateOneAsync(filter, update);
}
|
We utilize the UpdateOneAsync method overload, which accepts a FilterDefinition as the first argument and an UpdateDefinition as the second. With the builders, we first filter our data based on the unique ID, and with the second, we build the query to update temperature and humidity.
Having methods to insert and update data into our MongoDB, we want to get the average temperature within a specific period. That’s how we can do it:
12345 | public async Task<double> GetAverageTemperatureAsync(DateTime start, DateTime end)
{
var data = await _sensorDataCollection.Find(d => d.Timestamp >= start && d.Timestamp <= end).ToListAsync();
return data.Average(d => d.Temperature);
}
|
This method retrieves sensor data entries within the given time range and calculates their average temperature.
We created 4 methods for our use case that can be expanded as the application grows. We also built the foundation for constructing the API controller.
API Controller
Here’s the API controller that manages sensor data, providing a RESTful interface.
1234567891011121314151617181920212223242526272829303132333435363738 | [ApiController]
[Route("[controller]")]
public class SensorDataController : ControllerBase
{
private readonly MongoService _sensorDataService;
public SensorDataController(MongoService sensorDataService)
{
_sensorDataService = sensorDataService;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] SensorData sensorData)
{
await _sensorDataService.SaveDataAsync(sensorData);
return CreatedAtAction(nameof(GetById), new { id = sensorData.Id }, sensorData);
}
[HttpGet("{id}")]
public async Task<ActionResult<SensorData>> GetById(string id)
{
var sensorData = await _sensorDataService.GetDataByIdAsync(id);
if (sensorData == null)
{
return NotFound();
}
return Ok(sensorData);
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(string id, [FromBody] SensorData updatedData)
{
await _sensorDataService.UpdateSensorDataAsync(id, updatedData.Temperature, updatedData.Humidity);
return NoContent();
}
[HttpGet("average-temperature")]
public async Task<ActionResult<double>> GetAverageTemperature([FromQuery] DateTime start, [FromQuery] DateTime end)
{
var averageTemperature = await _sensorDataService.GetAverageTemperatureAsync(start, end);
return Ok(averageTemperature);
}
}
|
POST /api/sensordata: Add new sensor data.
GET /api/sensordata/{id}: Retrieve a sensor data entry by ID.
PUT /api/sensordata/{id}: Update temperature and humidity fields for a specific sensor data entry.
GET /api/sensordata/average-temperature?start={start}&end={end}: Calculate average temperature over a specified period.
Add Mock Data using Mongo Compass
It’s time to test our application and sensor data endpoints, but we don’t have data in our database. We will use the MongoDB Compass interface to import a JSON sensor data collection. As we saw, MongoDB fully supports documents and a JSON array is the correct fit.
- Open MongoDB Compass and navigate to the sensordb database.

2. Click ADD DATA > Insert Document, paste the JSON data below, and save.
12345678910111213141516171819202122232425262728293031323334353637 | [
{
"Timestamp": { "$date": "2024-11-01T08:30:00Z" },
"Temperature": 22.5,
"Humidity": 45.0,
"CO2": 400.0,
"ParticulateMatter": 12.0
},
{
"Timestamp": { "$date": "2024-11-01T09:00:00Z" },
"Temperature": 23.1,
"Humidity": 46.0,
"CO2": 410.0,
"ParticulateMatter": 13.0
},
{
"Timestamp": { "$date": "2024-11-01T09:30:00Z" },
"Temperature": 21.9,
"Humidity": 44.5,
"CO2": 405.0,
"ParticulateMatter": 11.5
},
{
"Timestamp": { "$date": "2024-11-01T10:00:00Z" },
"Temperature": 22.8,
"Humidity": 45.5,
"CO2": 402.0,
"ParticulateMatter": 12.3
},
{
"Timestamp": { "$date": "2024-11-01T10:30:00Z" },
"Temperature": 23.3,
"Humidity": 47.0,
"CO2": 415.0,
"ParticulateMatter": 13.2
}
]
|
3. The final result with the sensor data in the database:

Run and Verify
Now our database has some data and we can start our application to test our endpoints. When you run the application, you should see the Swagger UI as below:

Let’s try out the average temperature endpoint with a start date of 2024-11-01T08:30:00Z and an end date of 2024-11-01T10:30:00Z. The result that we get is 22.72, as you can see in the following image:

We developed a foundational IoT Sensor Data Management System using .NET Core and MongoDB. This initial setup provides a flexible foundation that can be readily expanded to handle more complex data requirements. Our focus on a local setup simplifies the development process, making it easier to build and test features in a controlled environment.
Implementing TTL in MongoDB for IoT Sensor Data
Data is at the core of modern decision-making. We use them in many decisions and circumstances as humans to evolve, predict and create a better future for ourselves. How to handle this data is also an essential step in this direction. However, not all data remains relevant forever. Old readings, once past their usefulness, become dead weight: they consume storage, slow queries, and drive up hosting costs.
To tackle this challenge in MongoDB, Time-to-Live (TTL) indexes can be used. They are special single-field indexes that automatically remove documents from a collection after a specified period of time. IoT devices generate tons of unstructured data, and after a few days, they are outdated.
Gain full access to the complete guide to implementing TTL in MongoDB for IoT sensor data. Fill the form to get:
- A step-by-step TTL index implementation walkthrough
- Best practices for managing high-volume time-series data
- Code examples and real-world performance tips
- Key pitfalls to avoid when using TTL in production