如何正确使用 EntityRepository 实例?

问题描述

文档强调我应该为每个请求使用一个新的 EntityManager,甚至还有一个中间件可以自动生成它,或者我可以使用 em.fork()。到目前为止一切顺利。

EntityRepository 是使代码可读的好方法。我在文档中找不到关于它们如何与 EntityManager 实例相关的任何内容express-ts-example-app 示例使用存储库的单个实例和 RequestContext 中间件。这表明有一些幕后魔法可以至少使用 EntityManager 找到正确的 RequestContext 实例。真的是这样吗?

另外,如果我手动分叉 EM,它还能找到合适的吗?考虑以下示例:

(async () => {
  DI.orm = await MikroORM.init();
  DI.em = DI.orm.em;
  DI.companyRepository = DI.orm.em.getRepository(Company);
  DI.invoiceRepository = DI.orm.em.getRepository(Invoice);
  ...
  fetchInvoices(em.fork());
}

async function fetchInvoices(em) {
  for (const company of await DI.companyRepository.findAll()) {
    fetchInvoicesOfACompany(company,em.fork())
  }
}

async function fetchInvoicesOfACompany(company,em) {
  let done = false;
  while (!done) {
    const invoice = await getNextInvoice(company.taxnumber,company.lastInvoice);
    if ( invoice ) {
      DI.invoiceRepository.persist(invoice);
      company.lastInvoice = invoice.id;
      em.flush();
    } else {
      done = true;
    }
  }
}

DI.invoiceRepository.persist() 中的 fetchInvoicesOfACompany() 是否使用了正确的 EM 实例?如果没有,我该怎么办?

另外,如果我没记错的话,em.flush() 中的 fetchInvoicesOfACompany() 不会更新公司,因为它属于另一个 EM - 我应该如何处理这样的情况?

解决方法

首先,repository 只是 EM 之上的一个薄层(如果需要,可以是扩展点),它暴露了实体名称,因此您不必将其传递给 EM 方法的第一个参数(例如 { {1}} 对 em.find(Ent,...)

然后是上下文 - 每个请求都需要一个专用的上下文,因此它有自己的身份映射。如果您使用 repo.find(...) 帮助程序,上下文将通过 RequestContext API 创建和保存。多亏了这一点,在域处理程序中执行的所有方法都将自动使用正确的实例 - 这发生在 domain 方法中,该方法首先检查 em.getContext() 助手。

WAL enable-disable is known to have issues

检查测试以更好地了解其工作原理:

https://mikro-orm.io/docs/identity-map/#requestcontext-helper-for-di-containers

因此,如果您使用存储库,使用 RequestContext 助手它会正常工作,因为单例存储库实例将使用单例 EM 实例,然后该实例将通过 RequestContext 使用适当的基于请求的实例。

但是如果您改用手动分叉,则您有责任使用正确的存储库实例 - 每个 EM 分叉都有自己的一个。所以在这种情况下你不能使用单例,你需要做 em.getContext()

顺便说一句,如果您在节点 12+ 上,您也可以使用速度更快的 forkedEm.getRepository(Ent)(并且不会被弃用)。 AsyncLocalStorage 助手实现将在 v5 中使用 ALS,因为将需要节点 12+。

https://github.com/mikro-orm/mikro-orm/blob/master/tests/RequestContext.test.ts

您可以做的另一件事是手动使用 RequestContext 助手而不是通过中间件 - 类似于以下内容:

RequestContext