Elixir 如何正确编写事务并冒泡错误
好吧,因为有多个层次的回报,我有点迷路了 我对埃克托很陌生,所以我来了 我正试图将我的帐户创建包装在一个事务中,因为它会创建许多子记录等 到目前为止,我有一个:Elixir 如何正确编写事务并冒泡错误,elixir,phoenix-framework,ecto,Elixir,Phoenix Framework,Ecto,好吧,因为有多个层次的回报,我有点迷路了 我对埃克托很陌生,所以我来了 我正试图将我的帐户创建包装在一个事务中,因为它会创建许多子记录等 到目前为止,我有一个: def create_account(company_name, ...) do Repo.transaction(fn -> case Account.create_account(%{ # ... attributes here }) do ???
def create_account(company_name, ...) do
Repo.transaction(fn ->
case Account.create_account(%{
# ... attributes here
}) do
????
end
# insert other model records here using the same above case pattern matching
account
end) # transaction
end
EXTO架构模型上的create_帐户如下所示:
Account.ex
def create_account(attrs \\ %{}) do
%Account{}
|> Account.changeset(attrs)
|> Repo.insert()
end
因此,现在有3个级别的返回值,我不确定如何一起处理:
def create_account(company_name, ...) do
Repo.transaction(fn ->
with {:ok, account} <- Account.create_account(...),
{:ok, _} <- AnotherModel.create_record(...),
...
{:ok, _} <- LastModel.create_record(...) do
IO.puts("All fine")
account
else
error ->
IO.inspect(error, label: "Error happened")
Repo.rollback(:error_in_transaction)
end
end) # transaction
end
def创建账户(公司名称,…)做什么
回购交易(fn->
使用{:ok,account}时,您应该在失败时使用。文档说事务将返回作为{:error,value}
给定的值,因此这可以通过您提到的模式匹配来完成:
def create_account(company_name, ...) do
Repo.transaction(fn ->
account = case Account.create_account(%{ # ... attributes here }) do
{:ok, account} -> account
{:error, changeset} -> Repo.rollback(changeset)
end
# insert other model
{:ok, account}
end)
end
通过这种方式,函数将在成功时返回{:ok,account}
,在遇到任何失败时返回{:error,changeset}
。因为插入了多个内容,您可能希望区分它们,可能是这样的:
account = case Account.create_account(%{ # ... attributes here }) do
{:ok, account} -> account
{:error, changeset} -> Repo.rollback({:account, changeset})
end
case User.create_user(account, %{ # ... attributes here }) do
{:ok, user} -> :ok
{:error, changeset} -> Repo.rollback({:user, changeset})
end
在这种情况下,如果一切正常,函数将返回{:ok,account}
,{:error,{:account,account\u changeset}
,如果account插入失败,以及{:error,{:user,user\u changeset}
如果用户插入失败。对您的意图的描述听起来像是exto.Multi
的完美用例。它是exto的一个功能,允许您定义复杂的数据处理管道。这里有更多示例的详细说明,但总体上想法简单而可靠
account = Account.changeset(%Account{}, params)
subscription = %Subscription{valid_until: ~D[2020-09-30]}
create_account =
Ecto.Multi.new()
|> Ecto.Multi.insert(:insert_account, account)
|> Ecto.Multi.run(:insert_subscription, fn repo, %{insert_account: account} ->
subscription
|> Map.put(:account_id, account.id)
|> repo.insert()
end)
Repo.transaction(create_account)
您可以随意对其进行重构;其基本思想是将每个步骤定义为一个速记操作,例如insert
,或者定义为一个返回{:ok,record}
或{:error,{/code>-的函数,就像在Multi.run
中一样,因为它需要引用上一步的工件
管道在create\u account
变量中定义,然后仅在调用Repo.transaction(create\u account)
时执行。这样,所有步骤都作为单个事务运行
- 如果所有步骤都成功,
{:确定,%%{insert\u用户:%user{…},insert\u订阅:%subscription{…}
,事务提交
- 如果任何步骤失败(对于定义为函数的步骤,它意味着返回
{:error,}
),将返回一个错误元组,例如{:error,:insert\u user,%exto.Changeset{}
,并回滚事务。在这种情况下,失败发生在insert\u user
步骤中
这非常感谢,对于如何优雅地处理此问题以及如何正确处理所有情况,我们确实感到失望。with使事情变得干净,但这可能更适合于获取而不是处理每个事务。为什么?Kernel.SpecialForms.with/1
明确设计用于处理此类情况。gazil如何处理Repo.callback
重复的狮子会更好吗?哦,我没有意识到这一点,让我研究一下,谢谢。还有,检查一下