DataDepositPool Data Storage Pool
DataDepositPool is a temporary data storage pool with capacity limits, suitable for scenarios that need to temporarily store and retrieve data.
Namespace
Section titled “Namespace”using MiCake.Util.Store;Constructor
Section titled “Constructor”// Default capacity is 1000var pool = new DataDepositPool();
// Custom capacityvar pool = new DataDepositPool(maxCapacity: 5000);Core Methods
Section titled “Core Methods”Deposit - Store Data
Section titled “Deposit - Store Data”// Store datapool.Deposit("user-1", userData);
// Replace existing datapool.Deposit("user-1", newUserData, isReplace: true);Parameter description:
key: the unique identifier of the datavalue: the data object to storeisReplace: whether to replace existing data (default false)
TakeOut - Get Data
Section titled “TakeOut - Get Data”// Get data (returns object)object? data = pool.TakeOut("user-1");
// Get data of a specified typeUser? user = pool.TakeOut<User>("user-1");
// Returns null when the data doesn't existvar notFound = pool.TakeOut<User>("not-exists"); // nullTakeOutByType - Get by Type
Section titled “TakeOutByType - Get by Type”// Get all data of a specified typeList<object> users = pool.TakeOutByType(typeof(User));
// Convert to a strongly-typed listList<User> typedUsers = users.Cast<User>().ToList();ReleaseAll - Release All Data
Section titled “ReleaseAll - Release All Data”pool.ReleaseAll();Properties
Section titled “Properties”| Property | Description |
|---|---|
Count |
The current number of stored data items |
MaxCapacity |
The maximum capacity |
Usage Examples
Section titled “Usage Examples”Temporary Data Caching
Section titled “Temporary Data Caching”public class SessionStore : ISingletonService{ private readonly DataDepositPool _pool = new(maxCapacity: 10000);
public void StoreSession(string sessionId, SessionData data) { _pool.Deposit(sessionId, data, isReplace: true); }
public SessionData? GetSession(string sessionId) { return _pool.TakeOut<SessionData>(sessionId); }
public void RemoveSession(string sessionId) { _pool.TakeOut(sessionId); // get and remove }
public void ClearAllSessions() { _pool.ReleaseAll(); }
public int GetActiveSessionCount() { return _pool.Count; }}Intermediate Result Storage
Section titled “Intermediate Result Storage”public class BatchProcessor{ private readonly DataDepositPool _resultPool = new();
public async Task ProcessBatchAsync(List<Item> items) { // Process in parallel await Parallel.ForEachAsync(items, async (item, ct) => { var result = await ProcessItemAsync(item); _resultPool.Deposit($"result-{item.Id}", result); }); }
public List<ProcessResult> GetAllResults() { return _resultPool.TakeOutByType(typeof(ProcessResult)) .Cast<ProcessResult>() .ToList(); }
public ProcessResult? GetResult(string itemId) { return _resultPool.TakeOut<ProcessResult>($"result-{itemId}"); }
public void Complete() { _resultPool.ReleaseAll(); _resultPool.Dispose(); }}Workflow State Management
Section titled “Workflow State Management”public class WorkflowEngine{ private readonly DataDepositPool _statePool = new(maxCapacity: 1000);
public void SaveWorkflowState(string workflowId, WorkflowState state) { try { _statePool.Deposit(workflowId, state, isReplace: true); } catch (InvalidOperationException ex) when (ex.Message.Contains("capacity")) { // Not enough capacity, clean up old states CleanupOldStates(); _statePool.Deposit(workflowId, state, isReplace: true); } }
public WorkflowState? LoadWorkflowState(string workflowId) { return _statePool.TakeOut<WorkflowState>(workflowId); }
public List<WorkflowState> GetAllActiveWorkflows() { return _statePool.TakeOutByType(typeof(WorkflowState)) .Cast<WorkflowState>() .Where(s => s.IsActive) .ToList(); }
private void CleanupOldStates() { var allStates = _statePool.TakeOutByType(typeof(WorkflowState)) .Cast<WorkflowState>() .ToList();
// Keep only the most recent 800 var toKeep = allStates .OrderByDescending(s => s.LastModified) .Take(800) .ToList();
_statePool.ReleaseAll();
foreach (var state in toKeep) { _statePool.Deposit(state.WorkflowId, state); } }}Request Context Storage
Section titled “Request Context Storage”public class RequestContextStore{ private readonly DataDepositPool _pool = new();
public void StoreContext(string requestId, RequestContext context) { _pool.Deposit(requestId, context); }
public RequestContext? GetContext(string requestId) { return _pool.TakeOut<RequestContext>(requestId); }
// Clean up at the end of the request public void CleanupRequest(string requestId) { _pool.TakeOut(requestId); }}
// Using in middlewarepublic class RequestContextMiddleware{ private readonly RequestContextStore _store;
public async Task InvokeAsync(HttpContext context, RequestDelegate next) { var requestId = Guid.NewGuid().ToString();
_store.StoreContext(requestId, new RequestContext { RequestId = requestId, StartTime = DateTime.UtcNow, UserId = context.User.FindFirst("sub")?.Value });
try { await next(context); } finally { _store.CleanupRequest(requestId); } }}Exception Handling
Section titled “Exception Handling”| Scenario | Exception | Description |
|---|---|---|
| key is null | ArgumentNullException |
The key cannot be null |
| key already exists and isReplace = false | InvalidOperationException |
The key already exists |
| Capacity limit exceeded | InvalidOperationException |
Exceeds the maximum capacity |
| Operation after disposal | ObjectDisposedException |
The object has been disposed |
Exception Handling Example
Section titled “Exception Handling Example”public class SafeDataPool{ private readonly DataDepositPool _pool = new(maxCapacity: 1000);
public bool TryDeposit(string key, object value) { try { _pool.Deposit(key, value); return true; } catch (ArgumentNullException) { _logger.LogError("Key cannot be null"); return false; } catch (InvalidOperationException ex) when (ex.Message.Contains("capacity")) { _logger.LogWarning("Pool capacity exceeded"); return false; } catch (InvalidOperationException ex) when (ex.Message.Contains("already exists")) { _logger.LogInformation("Key already exists, use isReplace=true to update"); return false; } catch (Exception ex) { _logger.LogError(ex, "Unexpected error"); return false; } }}Best Practices
Section titled “Best Practices”1. Set a Reasonable Capacity
Section titled “1. Set a Reasonable Capacity”// ✅ Correct: set the capacity based on the expected data volumevar pool = new DataDepositPool(maxCapacity: EstimateMaxDataItems());
// ❌ Wrong: capacity too smallvar pool = new DataDepositPool(maxCapacity: 10); // easily exceeds the limit
// ❌ Wrong: capacity too largevar pool = new DataDepositPool(maxCapacity: 1000000); // consumes too much memory2. Release Promptly
Section titled “2. Release Promptly”// ✅ Correct: release after useusing (var pool = new DataDepositPool()){ // Use the pool}
// Or release manuallyvar pool = new DataDepositPool();try{ // Use the pool}finally{ pool.Dispose();}3. Use Generic Methods
Section titled “3. Use Generic Methods”// ✅ Correct: use generic methods to get strongly-typed datavar user = pool.TakeOut<User>("user-1");
// ❌ Not recommended: requires manual conversionobject? obj = pool.TakeOut("user-1");var user = obj as User; // requires extra conversion4. Handle Capacity Exceptions
Section titled “4. Handle Capacity Exceptions”// ✅ Correct: handle capacity overflowspublic void AddData(string key, object value){ try { _pool.Deposit(key, value); } catch (InvalidOperationException ex) when (ex.Message.Contains("capacity")) { // Cleanup strategy 1: release all data _pool.ReleaseAll(); _pool.Deposit(key, value);
// Or strategy 2: remove the oldest data // RemoveOldestData(); // _pool.Deposit(key, value); }}5. Register as a Singleton
Section titled “5. Register as a Singleton”// ✅ Correct: register as a singletonpublic class MyModule : MiCakeModule{ public override void ConfigureServices(ModuleConfigServiceContext context) { context.Services.AddSingleton<DataDepositPool>(sp => new DataDepositPool(maxCapacity: 1000));
base.ConfigureServices(context); }}
// ❌ Wrong: register as transient or scopedservices.AddScoped<DataDepositPool>(); // a new instance per request, losing the caching purposePerformance Considerations
Section titled “Performance Considerations”| Operation | Time complexity | Description |
|---|---|---|
Deposit |
O(1) | Dictionary insertion |
TakeOut |
O(1) | Dictionary lookup and removal |
TakeOutByType |
O(n) | Requires iterating over all data |
ReleaseAll |
O(1) | Clears the dictionary |
Performance Optimization Suggestions
Section titled “Performance Optimization Suggestions”// ✅ Recommended: use TakeOut (O(1))var data = pool.TakeOut<User>("user-1");
// ⚠️ Note: TakeOutByType has worse performance (O(n))var allUsers = pool.TakeOutByType(typeof(User)); // avoid frequent callsImportant Notes
Section titled “Important Notes”- Capacity limit: exceeding the capacity throws an exception, handle it in advance
- Data removal:
TakeOutremoves the data; retrieving it again returns null - Thread safety: internally uses a dictionary, thread safety is not guaranteed, external synchronization is needed
- Memory usage: stored data occupies memory, release unneeded data promptly
- Type checking:
TakeOutByTypeusesisfor type checking
Suitable Scenarios
Section titled “Suitable Scenarios”✅ Suitable for
Section titled “✅ Suitable for”- Session storage
- Intermediate result caching
- Workflow state management
- Request context storage
- Temporary data exchange
❌ Not suitable for
Section titled “❌ Not suitable for”- Long-term data storage (use a database)
- Large object storage (consumes too much memory)
- Data that needs to be persisted
- Distributed scenarios (single-machine memory)
- High-concurrency writes (requires extra synchronization)
