【问题标题】:Ecto 3 - Start Repo in custom CLI taskEcto 3 - 在自定义 CLI 任务中启动 Repo
【发布时间】:2019-05-13 12:21:22
【问题描述】:

如果您需要为混合任务访问数据库 - 您需要手动启动 ecto repo。当我们使用 ecto 2 时,我们有 ensure_started 函数。

所以使用 ecto 2 的自定义 CLI 任务看起来像

defmodule App.Task do
  use Mix.Task
  import Mix.Ecto

  def run(args) do
    repos = parse_repo(args)

    Enum.each repos, fn repo ->
      ensure_repo(repo, args)
      ensure_started(repo, []) # was deleted from ecto 3

ensure_started 已从 ecto 3 中删除。我尝试了一些方法,但它们对我不起作用。有人知道 ecto 3 的解决方法吗?

更新

正如@starbelly 提到的ensure_started 已迁移到Mix.EctoSQL,因此我们需要添加额外的import Mix.EctoSQL 以使其工作

defmodule App.Cli.Task do
  import Mix.Ecto
  import Mix.EctoSQL

  def start_ecto do
    repos = parse_repo([])

    Enum.each(repos, fn repo ->
      ensure_repo(repo, [])
      {:ok, _pid, _apps} = ensure_started(repo, [])
    end)
  end
end

【问题讨论】:

  • 不会Application.ensure_all_started/2 帮忙吗?喜欢Application.ensure_all_started(Ecto)
  • 我试过但没有运气 {:ok, apps} = Application.ensure_all_started(:ecto_sql) IO.inpsect apps [:telemetry, :connection, :db_connection, :decimal, :ecto, :ecto_sql ] 但是一旦我需要进行查询 ** (RuntimeError) 就无法查找 Smd.Repo,因为它没有启动或它不存在 (ecto) lib/ecto/repo/registry.ex:18: Ecto.Repo .Registry.lookup/1 (ecto) lib/ecto/repo/queryable.ex:131: Ecto.Repo.Queryable.execute/4 (ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable .all/3 lib/smd/app/search/sync_es.ex:76:Mix.Tasks.App.SyncEs.sync_all_pages/4

标签: elixir ecto


【解决方案1】:

您尝试导入和调用的函数已移至 ecto_sql。你应该import Mix.EctoSQLensure_started/2

例子:

defmodule Mix.Tasks.Friends.Hello do
  use Mix.Task
  import Mix.Ecto
  import Mix.EctoSQL

  def run(args) do
    repos = parse_repo(args)

    Enum.each(repos, fn repo ->
      ensure_repo(repo, args)
      {:ok, _pid, _apps} = ensure_started(repo, [])
      Friends.Repo.all(Friends.Person)
    end)
  end
end

【讨论】:

    【解决方案2】:

    这些方法都不再适用于最新版本的 ecto_sql (3.1.2),因为 Mix.EctoSQL.ensure_started/2 是一个私有函数,不适合在 Ecto 之外使用。

    As per Jose,正确的做法是使用

    Mix.Task.run("app.start")
    

    所以@starbelly 共享的示例将被修改为

    defmodule Mix.Tasks.Friends.Hello do
      use Mix.Task
    
      def run(args) do
        Mix.Task.run("app.start")
        repos = parse_repo(args)
    
        Enum.each(repos, fn repo ->
          repo.all(Friends.Person)
        end)
      end
    end
    

    这种方法的最大缺点是它会启动您的整个应用程序,包括您拥有的任何后台进程。因此,您可能需要修改 application.ex 文件以不启动那些由混合任务启动的文件。

    【讨论】:

    • 您将如何实现这一目标? (修改 application.ex 以不启动某些东西是由混合任务启动的)
    • @RobinKomiwes 有几种方法可以做到这一点,但最简单的方法之一是在 mix 任务中的 run 函数的顶部执行 Application.put_env(:your_app_name, :process_type, :mix_task) 之类的操作。然后在您的application.ex 文件中,您可以限制您告诉Supervisor 启动的进程仅包含基于:process_type 值的Repo 进程
    【解决方案3】:

    正如 Dennis 指出的,Mix.EctoSQL.ensure_started/2 是自 ecto_sql 3.1.2 以来的私有函数,所以我们不能再使用它了。

    假设您定义了一个 OTP 应用程序,这个混合任务是其中的一部分,您可以做的是

    defmodule Mix.Tasks.Friends.Hello do
      use Mix.Task
    
      def run(args) do
        {:ok, _started} = Application.ensure_all_started(:your_otp_app_name)
        repos = parse_repo(args)
    
        Enum.each(repos, fn repo ->
          repo.all(Friends.Person)
        end)
      end
    end
    

    基本上启动整个 OTP 应用程序,以便所有依赖项(包括 repos)也将启动。

    【讨论】:

      【解决方案4】:

      对于 Ecto 3.x,使用 Repo.start_link() 或 Mix.Task.run('app.start')

      这个问题/答案可能会有所帮助。 How to get data from Ecto in a custom mix task

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      • 谁推荐的?
      • 添加了参考@Andrew。早该包括在内。 (根据何塞·瓦利姆)
      猜你喜欢
      • 2016-01-18
      • 2011-07-02
      • 1970-01-01
      • 2016-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-29
      • 1970-01-01
      相关资源
      最近更新 更多