MACRIM.Core
6.8.1
See the version list below for details.
dotnet add package MACRIM.Core --version 6.8.1
NuGet\Install-Package MACRIM.Core -Version 6.8.1
<PackageReference Include="MACRIM.Core" Version="6.8.1" />
<PackageVersion Include="MACRIM.Core" Version="6.8.1" />
<PackageReference Include="MACRIM.Core" />
paket add MACRIM.Core --version 6.8.1
#r "nuget: MACRIM.Core, 6.8.1"
#:package MACRIM.Core@6.8.1
#addin nuget:?package=MACRIM.Core&version=6.8.1
#tool nuget:?package=MACRIM.Core&version=6.8.1
MACRIM.Caching Project
Purpose
The MACRIM.Caching project extends the MACRIM framework’s caching capabilities to support table-based caching in Redis, replacing the in-memory LookupService with a scalable, persistent solution. It caches IEntity instances for SQL tables (or other data sources), enabling fast lookups for batch processing (5-10K rows) with dynamic indexing and segmented caching. The rebuilt RedisService addresses compile errors from the original CacheService<T>, integrating with IConfigService for configuration-driven caching.
Phase 1: Table-Based Caching
Phase 1 implements caching in Redis, focusing on:
- Caching: Stores
IEntityinstances as Redis Hashes (segment:cacheName:segment:catalog) with TTL fromexpires. - Indexing: Indexes
jsondataviakeypatternexpressions (e.g.,{catalog},{name},{catalog}:{name}) using Redis Sets (segment:cacheName:index:value). - Loading: Retrieves data via
IRepositoryBypass.SearchAsync, usingLoadJSONforIEntitycreation. Synchronized to ensure only one load per cache, with concurrent requests waiting. - Upsert: Updates cache with
IEntityinstances (SQL sync deferred to Phase 2). - Configuration: Uses
IConfigService(_configs[segment]["commands"]["caching"][cacheName]) fortable,profile,keypattern,expires,segment. - Synchronization: Syncs on initial load, TTL expiration, and cache misses. No ongoing sync within TTL.
- Lookups: Supports lookups by any
keypatternindex entry (e.g.,svc123) usingGetRecord/GetRecordAsync, with expressions evaluated viaApplyLookups.
Usage Example
To perform a lookup like {?:all-services(svc123,(catalog))} (retrieve a service by svc123 and return its catalog):
Configure Cache:
<command name="all-services" table="services" profile="sql" expires="600" segment="true" keypattern="{catalog},{name},{servicename},{catalog}:{name}"> <methods> <method name="retrieve" type="MACRIM.Data.Extensions.Base.Search"> <parameters> <parameter name="model" value="services" /> <parameter name="profile" value="sql" /> <parameter name="segment" value="{var:tenant}" /> </parameters> </method> </methods>Set Up DI:
services.AddSingleton<ITableCachingService, RedisService>();Update ApplyLookups (as static extension method):
case "lookup": case "?": var cacheSvc = me.Context.Services.GetRequiredService<ITableCachingService>(); if (cacheSvc == null) break; var argStart = what.IndexOf('('); var instruct = argStart > -1 ? what.Substring(++argStart) : string.Empty; instruct = instruct.Length > 0 ? instruct.Remove(instruct.Length - 1) : string.Empty; what = argStart > -1 ? what.Substring(0, argStart - 1) : what; if (what == ".") { var pstart = instruct.IndexOf(','); var lkey = instruct.Split(',').FirstOrDefault(); var lexp = pstart > -1 ? instruct.Substring(++pstart) : string.Empty; if (lkey.StartsWith('(')) lkey = (string)me.ApplyLookups(lkey.Replace('(', '{').Replace(')', '}'), returnType, subReturnType, command, defaults); var el = me.LookupCache.ContainsKey(lkey) ? me.LookupCache[lkey] : me.Spawn(); if (el != null && el.Found) { oFound = el.ApplyLookups(lexp.Replace('(', '{').Replace(')', '}'), returnType, subReturnType, command, defaults); } } else { var pstart = instruct.IndexOf(','); var lkey = instruct.Split(',').FirstOrDefault(); var lexp = pstart > -1 ? instruct.Substring(++pstart) : string.Empty; if (string.IsNullOrEmpty(lkey) || string.IsNullOrEmpty(lexp)) { oFound = string.Empty; break; } var keyvalue = me.Attributes.GetValueOrDefault(lkey, ""); if (lkey.StartsWith('(')) keyvalue = (string)me.ApplyLookups(lkey.Replace('(', '{').Replace(')', '}'), returnType, subReturnType, command, defaults); if (string.IsNullOrEmpty(keyvalue)) { oFound = string.Empty; break; } var record = cacheSvc.GetRecord( keyvalue: keyvalue, cacheName: what, segment: me.Context?.Segment ?? "default" ); if (record != null && record.Found) { oFound = record.ApplyLookups(lexp.Replace('(', '{').Replace(')', '}'), returnType, subReturnType, command, defaults); } else { oFound = string.Empty; } } break;Test Lookup:
- Input:
{?:all-services(svc123,(catalog))}withme.Attributes["servicename"] = "svc123". - Output:
oFound = "cat1"(fromrecord.ApplyLookups("{catalog}", ...)). - Complex Input:
{?:all-services(svc123,(user:{id}:name:upper))}→oFound = "JOHN".
- Input:
Phase 2: Queuing (Deferred)
Phase 2 will implement async SQL sync using Redis Lists, leveraging StartWatchingList, Polly retries, and a poison queue.
Key Components
- Interfaces (in
MACRIM.Core.Abstractions):ITableCachingService:LoadCacheAsync,GetRecordAsync,GetRecord,UpsertRecordAsync(version: a37df99a-233d-424d-9c2d-fd5b56875548).IQueueService,QueueTask(Phase 2).
- RedisService (in
MACRIM.Caching):- Implements
ICachingServiceandITableCachingService(version: b9f8c7b5-4d2e-4f9b-9e2c-6f7a8b9c0f4e). - Uses
ConnectionHelperfor Redis. - Resolves
IRepositoryBypassviaIContextService.Services.
- Implements
- Configuration:
IEntitywith attributes (table,profile,keypattern,expires,segment).
IEntity Mapping
- segment:
Attributes["segment"], partition key. - catalog:
Attributes["catalog"], typicallyKey. - jsondata:
Attributes(e.g.,{"segment": "seg1", "catalog": "cat1", "name": "John", "age": "30"}). - Key:
Attributes["catalog"]. - Element: Table name (e.g.,
contacts). - Value: Null.
- Index:
keypatternexpressions (e.g.,Index["cat1"],Index["cat1:John"]).
Implementation Status
- Phase 1 (Complete):
RedisService.csrebuilt for caching, supporting lookups by anykeypatternindex entry usingkeyvalue(e.g.,svc123).- Removed
profileparameter, deriving it from configuration. - Fixed batch execution error (
ITransaction.ExecuteAsync). - Added synchronous
GetRecordforApplyLookups, optimized for high-frequency calls without logging. - Synchronized
LoadCacheAsyncto ensure one load per cache, with concurrent requests waiting.
- Phase 2 (Deferred): Queuing pending prioritization.
Next Steps
Test Phase 1:
- Test
RedisServicewith sample data (e.g.,contacts,keypattern="{catalog},{name},{ssn},{catalog}:{name}"). - Verify concurrent lookups (e.g., 20 queued processes) to ensure single cache load.
- Validate
{?:cacheName(keyvalue,(expression))}inApplyLookups(e.g.,{?:all-services(svc123,(catalog))}).
- Test
Sync Enhancements:
- Decide if
sync_versionchecks are needed for Phase 1 to detect table changes within TTL. - Plan Phase 2 for cache-to-SQL sync.
- Decide if
DI Confirmation:
- Confirm
Microsoft.Extensions.DependencyInjectionand singleton lifetime.
- Confirm
T Handling:
- Confirm
BaseEntityConvertibleforIRepository<T>, or specify models (e.g.,Documents).
- Confirm
Configuration:
- Provide example
IEntityoutput for_configs[segment]["commands"]["caching"][cacheName].
- Provide example
Phase 2:
- Define queuing requirements when ready.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Azure.Messaging.ServiceBus (>= 7.19.0)
- Azure.Security.KeyVault.Certificates (>= 4.5.1)
- Azure.Security.KeyVault.Secrets (>= 4.5.0)
- Azure.Storage.Blobs (>= 12.24.0)
- Base64Helper (>= 0.0.7)
- BouncyCastle.Cryptography (>= 2.4.0)
- CsvHelper (>= 33.0.1)
- MailKit (>= 4.5.0)
- Microsoft.AspNetCore.Authentication.Abstractions (>= 2.2.0)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.2.0)
- Microsoft.Azure.Cosmos (>= 3.36.0)
- Microsoft.Azure.Functions.Extensions (>= 1.1.0)
- Microsoft.Azure.WebJobs (>= 3.0.39)
- Microsoft.Azure.WebJobs.Extensions.Storage (>= 5.2.1)
- Microsoft.CodeAnalysis.CSharp.Scripting (>= 4.14.0)
- Microsoft.Data.SqlClient (>= 5.1.5)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.2)
- Microsoft.Extensions.Configuration.AzureKeyVault (>= 3.1.24)
- Microsoft.Extensions.DependencyInjection (>= 9.0.2)
- Microsoft.Extensions.Http (>= 9.0.2)
- Microsoft.Extensions.Logging (>= 9.0.2)
- Microsoft.Extensions.Options (>= 9.0.2)
- MimeKit (>= 4.7.1)
- System.IdentityModel.Tokens.Jwt (>= 7.3.1)
- Twilio (>= 6.14.1)
NuGet packages (21)
Showing the top 5 NuGet packages that depend on MACRIM.Core:
| Package | Downloads |
|---|---|
|
MACRIM.Extensions
Package of IEntity extensions for MSSQL, Azure, PDF and more |
|
|
MACRIM.Encryption
Default encryption provider used in MACRIM.net |
|
|
MACRIM.Models
Support for Documents, Events, and Reports |
|
|
MACRIM.Web
Controllers, services and middleware supporting MACRIM.net web applications |
|
|
MACRIM.Transfers
IEntity Extensions related to file transfer technologies |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 6.11.15-beta.2 | 207 | 11/14/2025 |
| 6.11.15-beta.1 | 263 | 11/12/2025 |
| 6.8.1 | 487 | 7/31/2025 |
| 6.6.15 | 544 | 6/15/2025 |
| 6.6.15-alpha.2 | 331 | 6/12/2025 |
| 6.5.15 | 361 | 5/24/2025 |
| 5.5.1 | 440 | 6/13/2024 |
| 4.12.15 | 891 | 12/15/2023 |
| 4.11.1 | 625 | 11/2/2023 |
| 4.10.15 | 664 | 10/20/2023 |