如何将本地JSON文件作为ASP.NET Core API数据源并保持同步?
优化ASP.NET Core内存数据库与JSON文件同步方案
你的思路是对的,但重复在每个Action里写JSON写入逻辑确实冗余,这里给你两种更简洁的实现方式:
方案一:重写DbContext的SaveChangesAsync方法
把JSON同步逻辑统一放到DbContext里,所有调用SaveChangesAsync的地方都会自动触发同步,不用在每个接口里重复写代码:
public class YourDbContext : DbContext { public DbSet<Person> People { get; set; } private readonly string _jsonFilePath = Path.Combine(AppContext.BaseDirectory, "people.json"); public YourDbContext(DbContextOptions<YourDbContext> options) : base(options) { // 初始化时从JSON加载数据 InitializeFromJson(); } private void InitializeFromJson() { if (File.Exists(_jsonFilePath)) { var json = File.ReadAllText(_jsonFilePath); var people = JsonSerializer.Deserialize<List<Person>>(json); if (people != null && !People.Any()) { People.AddRange(people); SaveChanges(); } } } public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) { var result = await base.SaveChangesAsync(cancellationToken); // 保存后同步到JSON文件 await SyncToJsonAsync(cancellationToken); return result; } private async Task SyncToJsonAsync(CancellationToken cancellationToken) { var people = await People.ToListAsync(cancellationToken); var json = JsonSerializer.Serialize(people, new JsonSerializerOptions { WriteIndented = true }); await File.WriteAllTextAsync(_jsonFilePath, json, cancellationToken); } }
这样你的Post接口就能回到最简洁的状态:
[HttpPost] public async Task<ActionResult<Person>> PostPerson(Person person) { _context.People.Add(person); await _context.SaveChangesAsync(); return CreatedAtAction(nameof(GetPerson), new { id = person.Id }, person); }
方案二:使用EF Core的SaveChangesInterceptor
如果不想修改DbContext的核心方法,可以用EF的拦截器扩展,实现逻辑解耦:
首先定义拦截器:
public class JsonSyncInterceptor : SaveChangesInterceptor { private readonly string _jsonFilePath; private readonly YourDbContext _context; public JsonSyncInterceptor(string jsonFilePath, YourDbContext context) { _jsonFilePath = jsonFilePath; _context = context; } public override async ValueTask<int> SavedChangesAsync(SaveChangesCompletedEventData eventData, int result, CancellationToken cancellationToken = default) { var people = await _context.People.ToListAsync(cancellationToken); var json = JsonSerializer.Serialize(people, new JsonSerializerOptions { WriteIndented = true }); await File.WriteAllTextAsync(_jsonFilePath, json, cancellationToken); return await base.SavedChangesAsync(eventData, result, cancellationToken); } }
然后在Program.cs里注册拦截器和DbContext:
builder.Services.AddDbContext<YourDbContext>(options => { options.UseInMemoryDatabase("YourDbName"); options.AddInterceptors(serviceProvider => new JsonSyncInterceptor( Path.Combine(AppContext.BaseDirectory, "people.json"), serviceProvider.GetRequiredService<YourDbContext>() ) ); });
同时别忘了在DbContext构造函数里添加从JSON初始化数据的逻辑(和方案一中的InitializeFromJson方法一致)。
额外提示
- 用
Path.Combine(AppContext.BaseDirectory, "people.json")获取项目根目录下的文件路径,避免硬编码路径问题。 - 如果存在并发请求场景,建议给文件写入加锁,比如用
FileStream配合FileShare.Read参数,防止写入冲突。
内容的提问来源于stack exchange,提问作者J. James
相关产品推荐
相关产品推荐

