To answer your question “how do I use Ecto.Multi in a loop”, you would use Enum.reduce/3 to build up the Multi as you process elements in an enumerable.
def parse(_) do
multi = Ecto.Multi.new()
multi =
File.stream!("uploads/#{file}", [:trim_bom])
|> CSV.decode(headers: true)
|> Enum.reduce(multi, &process_row/2)
Repo.transaction(multi)
end
defp process_row(row, multi) do
parsed_row = ...
Ecto.Multi.insert(multi, ...)
end