【问题标题】:Adding new items to List obtained from DbSet.ToList将新项目添加到从 DbSet.ToList 获得的 List
【发布时间】:2021-06-27 13:21:08
【问题描述】:

EFCore 的文档(使用 .NET 5)显示,要添加项目,您需要直接使用底层的 DbSet。但是,我目前的设计如下:

我有一个用于获取底层数据集的接口:

public interface IServiceCache
{
    IEnumerable<T> Load<T>();
    void Save<T>(IEnumerable<T> objList);
}

这支持访问我的底层缓存持久存储。最初我只使用由 JSON 文件支持的文件系统存储来实现这一点。我现在使用DbContext 来实现这一点,这样我就可以在 SQLite 的表中的记录中保存缓存。

现在我已经这样实现了:

internal class DatabaseServiceCache : IServiceCache
{
    private readonly IDbContextFactory<DatabaseContext> _contextFactory;

    public DatabaseServiceCache(IDbContextFactory<DatabaseContext> contextFactory)
    {
        _contextFactory = contextFactory;
    }

    public IEnumerable<T> Load<T>()
    {
        var context = _contextFactory.CreateDbContext();
        return context.Set<T>();
    }

    public void Save<T>(IEnumerable<T> objList)
    {
        var context = _contextFactory.CreateDbContext();
        // TODO: What to do with `objList`?
        // Something like this? `context.Set<T>() = objList` (this won't work obviously)
        context.SaveChanges(); // in case elements were modified directly
    }
}

还有我的DatabaseContext 班级:

public class DatabaseContext : DbContext
{
    public DbSet<IdMapping> CacheIdMapping { get; set; }
}

所以我会像这样使用IServiceCache

var cache = new DatabaseServiceCache(contextFactory); // assume DI-injected, and that we give it the `contextFactory`
var theList = cache.Load<IdMapping>().ToList();
theList[0].SomeProperty = 100; // modify existing objects
theList.Add(new IdMapping()); // add new objects
cache.Save(theList); // save modified and added items

我的示例中可能存在一些编译器错误;我在运行中修改了我的真实代码以精简它。

所以基本的想法是:

  1. 我在数据库中查询代表缓存项列表的完整记录集
  2. 我修改了该列表和/或将新项目添加到同一列表中
  3. 我保存了列表,其中应该包括修改和添加的项目。

但是,到目前为止,我认为这不会起作用,因为在ToList() 之后执行的唯一更改跟踪是对现有项目的修改。假设该陈述是正确的,我的问题是:

如何将新添加的项目保存回数据库?

请注意,我很高兴得知我的设计需要更改。最初我使用磁盘上的 JSON 文件作为我的主要用例来设计它。所以我可能没有“跳出框框思考”,可能正在尝试将方形钉子装入圆孔中。

我是 EFCore 的新手,所以我也可能缺少一些基本概念。

【问题讨论】:

  • 您基本上只需要context.Set&lt;T&gt;().Add(newT); context.SaveChanges();没有别的。代码中的所有这些组件只会掩盖这些简单操作的路径。
  • “模糊路径”意味着什么? DbSet 应该到处传播吗?关注点分离似乎是对我正在做的事情的更积极描述,而不是“模糊”。但就像我在帖子中所说的那样,我很高兴被告知我从设计的角度来处理这个错误。我正在尝试将 EFCore 改造到我现有的架构中,这可能并不理想。

标签: c# entity-framework entity-framework-core


【解决方案1】:

当您调用 Save (objList) 方法时,您的 dbContext 是全新的,并且对列表的更改一无所知。 一种解决方案是遍历所有列表对象,从 db 读取和更新实体,然后 SaveChanges()。 显然,这是一个非常糟糕的主意。 DBContext 会跟踪实体和所有修改,因此如果可能,请尽量让一个上下文对象不进行读写操作。

新添加的项目的主键 = 0。使用 Add 或 AddRange 方法将它们添加到 dbset。

我假设 JSON 文件会刷新所有覆盖旧文件的内容。 在数据库的情况下,您正在处理一条记录,并且“没有必要”遍历所有记录。

上下文工厂很好,但如果您所做的只是获取对象、操作它们并保存到 dbase,您可以将 dbcontext 保留为私有字段。仅在阅读之前启动它们,但在保存之前保持打开状态。

另一种方法是在数据库上下文中实现 IServiceCache。 与对现有记录的修改肯定会被 ef 跟踪,您所要做的就是将新的 objectc 添加到 dbset,然后调用 saveChanges。

这样的

class DatabaseContext: IServiceCache {...}



var cache= contextFactory.Create(..)
..
theList = cache.Load();
..Add();
cache.Save(theList) {

DBSet<T>().AddRange(theList.Where(x=>x.Id == 0));
}

【讨论】:

  • 谢谢,我使用工厂是因为this advice。基本上,由于我在 Blazor Server 应用程序中工作,因此我开始使用工厂来确保线程安全。即使这段代码离 UI 层本身很深/很远。我假设我应该在方法调用之间创建一个 dbContext 实例。我认为传递 DbContext 会很糟糕。我可能有比我最初想象的更大的设计问题。我是否应该保留一个 dbContext 作为成员并在构造函数中对其进行初始化?
  • 关于您的最新编辑:首先,谢谢。其次,DbContext 可以有这样的业务逻辑吗?我读到虽然您可以拥有多个 DbContext 对象,但这可能不是一个好主意。我不想让我的DatabaseContext 实现多个不相关的接口,因为我将所有数据库模型都放在其中。
  • 我没有看到 IServiceCache 中的业务逻辑。为澄清起见,您的缓存只有两个职责:返回所有实体并将它们保存回来。但当然,您对用例和架构了解得更多。尝试先将 dbcontext 保存为私有成员,您将看看这是否是一个好方向。您的目标是保存更改,所以...
  • 谢谢,我也开始阅读有关存储库模式的内容,它只是在我正在做的事情之上增加了另一层巨大的混乱和复杂性。我认为您的想法是一个好主意,我只是想知道将DbContext 掩盖为IServiceCache 是否会引起一些人的注意作为“反模式”;从技术上讲,虽然我猜它是一个只使用继承而不是组合的存储库。
  • 我认为这个界面在很多地方都会用到,所以如果你想让你的项目快速启动并运行,这是一种捷径。这不是代码的第一个版本,也不是最后一个版本,相信我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-10
  • 1970-01-01
  • 1970-01-01
  • 2021-03-01
  • 2017-05-07
  • 1970-01-01
相关资源
最近更新 更多