EF Core Adapter is the EF Core adapter for Casbin. With this library, Casbin can load policy from EF Core supported database or save policy to it.
The current version supported all databases which EF Core supported, there is a part list:
You can see all the list at Database Providers.
dotnet add package Casbin.NET.Adapter.EFCore
The adapter supports the following .NET target frameworks:
using Casbin.Adapter.EFCore; using Microsoft.EntityFrameworkCore; using NetCasbin; namespace ConsoleAppExample { public class Program { public static void Main(string[] args) { // You should build a DbContextOptions for CasbinDbContext<TKey>. // The example use the SQLite database named "casbin_example.sqlite3". var options = new DbContextOptionsBuilder<CasbinDbContext<int>>() .UseSqlite("Data Source=casbin_example.sqlite3") .Options; var context = new CasbinDbContext<int>(options); // If it doesn't exist, you can use this to create it automatically. context.Database.EnsureCreated(); // Initialize a EF Core adapter and use it in a Casbin enforcer: var efCoreAdapter = new EFCoreAdapter<int>(context); var e = new Enforcer("examples/rbac_model.conf", efCoreAdapter); // Load the policy from DB. e.LoadPolicy(); // Check the permission. e.Enforce("alice", "data1", "read"); // Modify the policy. // e.AddPolicy(...) // e.RemovePolicy(...) // Save the policy back to DB. e.SavePolicy(); } } }
When using the adapter with dependency injection (e.g., in ASP.NET Core), you should use the IServiceProvider constructor or the extension method to avoid issues with disposed DbContext instances.
using Casbin.Persist.Adapter.EFCore; using Casbin.Persist.Adapter.EFCore.Extensions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; // Register services services.AddDbContext<CasbinDbContext<int>>(options => options.UseSqlServer(connectionString)); // Register the adapter using the extension method services.AddEFCoreAdapter<int>(); // The adapter will resolve the DbContext from the service provider on each operation, // preventing issues with disposed contexts when used with long-lived services.
// In your startup configuration services.AddDbContext<CasbinDbContext<int>>(options => options.UseSqlServer(connectionString)); services.AddCasbinAuthorization(options => { options.DefaultModelPath = "model.conf"; // Use the IServiceProvider constructor options.DefaultEnforcerFactory = (sp, model) => new Enforcer(model, new EFCoreAdapter<int>(sp)); });
This approach resolves the DbContext from the service provider on each database operation, ensuring that:
ObjectDisposedException is thrown when the adapter outlives the scope that created itThe adapter supports storing different policy types in separate database contexts, allowing you to:
// Create ONE shared connection object var sharedConnection = new SqlConnection(connectionString); // Create contexts with shared connection var policyContext = new CasbinDbContext<int>( new DbContextOptionsBuilder<CasbinDbContext<int>>() .UseSqlServer(sharedConnection).Options, // Shared connection schemaName: "policies"); var groupingContext = new CasbinDbContext<int>( new DbContextOptionsBuilder<CasbinDbContext<int>>() .UseSqlServer(sharedConnection).Options, // Same connection schemaName: "groupings"); // Create a provider that routes policy types to contexts var provider = new PolicyTypeContextProvider(policyContext, groupingContext); // Use the provider with the adapter var adapter = new EFCoreAdapter<int>(provider); var enforcer = new Enforcer("rbac_model.conf", adapter); // All operations work transparently across contexts enforcer.AddPolicy("alice", "data1", "read"); // → policyContext enforcer.AddGroupingPolicy("alice", "admin"); // → groupingContext enforcer.SavePolicy(); // Atomic across both
⚠️ Transaction Integrity Requirements
For atomic multi-context operations:
- Share DbConnection: All contexts must use the same
DbConnectionobject (reference equality)- Disable AutoSave: Use
enforcer.EnableAutoSave(false)and callSavePolicyAsync()to batch commit- Supported databases: PostgreSQL, MySQL, SQL Server, SQLite (same file)
Why disable AutoSave? With
EnableAutoSave(true)(default), each policy operation commits immediately and independently. If a later operation fails, earlier operations remain committed. WithEnableAutoSave(false), all changes stay in memory untilSavePolicyAsync()commits them atomically across all contexts using a shared connection-level transaction.
- ✅ Atomic: Same
DbConnectionobject +EnableAutoSave(false)+SavePolicyAsync()- ❌ Not Atomic: AutoSave ON, separate
DbConnectionobjects, different databasesSee detailed explanation in EnableAutoSave and Transaction Atomicity.
This project is under Apache 2.0 License. See the LICENSE file for the full license text.