Store Providers
Two packages hold rules outside the process. Both implement the same contracts and both pass the same conformance suite as the in-memory store, so swapping one for the other changes nothing about behaviour.
Redis
dotnet add package DynamicWhere.ex.Policies.Redis --version 3.0.0var redis = await ConnectionMultiplexer.ConnectAsync(connectionString);
var store = new RedisPolicyStore(redis);
var provider = await StorePolicyProvider.CreateAsync(store, options);
DwPolicy.Configure(options, provider);Entity Framework Core
dotnet add package DynamicWhere.ex.Policies.EntityFrameworkCore --version 3.0.0var policyDbOptions = new DbContextOptionsBuilder<DwPolicyDbContext>()
.UseNpgsql(connection, sql => sql.MigrationsAssembly("YourProject"))
.Options;
// A new context per read: the provider polls on a background timer, and a
// context shared with request threads would be used concurrently.
var store = new EfPolicyStore(() => new DwPolicyDbContext(policyDbOptions));
var provider = await StorePolicyProvider.CreateAsync(store, options);
DwPolicy.Configure(options, provider);DwPolicyDbContext is declared in the NuGet package, and EF Core looks for migrations in the assembly that declares the context. Left alone, dotnet ef migrations add refuses outright with "your target project does not match your migrations assembly", and a running application finds no migrations to apply. Point it at your own project in both places you build the context — the runtime registration and any design-time factory.Two tables, on their own migration history:
| Table | Holds |
|---|---|
DwPolicyRules | One row per rule, indexed by entity and field, and by subject. |
DwPolicyVersion | A single row carrying the snapshot version. |
One serializer, three stores
PolicyPayload is the only place a rule payload is read or written. Neither provider parses JSON of its own, because three parsers would become three standards. Every enumeration is written and read by name, in JSON and in a database column alike: the zero member of several enumerations is the permissive one, so an unparsed value must not read as a plausible-looking default.
Read-only deployments
Register only IDwPolicyStore and writes become impossible by construction rather than by convention. IDwPolicyWritableStore is a separate interface, so a replica that should never accept a rule simply does not implement it.
The conformance suite
One abstract suite runs against in-memory, a real Redis and a real PostgreSQL, covering load, version, watch and poll, the atomic swap, the zone split, startup failure, refresh failure, the staleness ceiling, the sealed-field refusal and the read-only store. Any future provider inherits all of it.