1

文档强调我应该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()infetchInvoicesOfACompany()不会更新公司,因为它属于另一个 EM - 我应该如何处理这种情况?

4

1 回答 1

1

首先,存储库只是 EM 之上的一个薄层(如果需要,可以作为扩展点),它包含实体名称,因此您不必将其传递给 EM 方法的第一个参数(例如em.find(Ent, ...)vs repo.find(...).

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

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

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

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

因此,如果您使用存储库,使用RequestContexthelper 它将工作得很好,因为单例存储库实例将使用单例 EM 实例,然后将通过适当的em.getContext()位置使用基于请求的正确实例。

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

AsyncLocalStorage顺便说一句,如果您在节点 12+ 上,您也可以使用哪个更快(并且不被弃用)。助手实现将RequestContext在 v5 中使用 ALS,因为将需要节点 12+。

https://mikro-orm.io/docs/async-local-storage

您可以做的另一件事是RequestContext手动使用帮助程序而不是通过中间件 - 如下所示:

(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);
...
  await RequestContext.createAsync(DI.em, async () => {
    await fetchInvoices();
  })
});

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

async function fetchInvoicesOfACompany(company) {
  let done = false;

  while (!done) {
    const invoice = await getNextInvoice(company.taxnumber, company.lastInvoice);
    
    if (invoice) {
      company.lastInvoice = invoice; // passing entity instance, no need to persist as `company` is managed entity and this change will be cascaded
      await DI.em.flush();
    } else {
      done = true;
    }
  }
}
于 2021-03-25T10:50:46.313 回答