---
title: "Run an ONNX model with multiple inputs in C#"
url: https://recursiveai.org/onemodel/cookbook/named-inputs-and-outputs/
description: "Run an ONNX model with several inputs or outputs in C#: list them, pass named tensors in any order, read every output. Runnable code, with OneModel."
version: "1.0.0"
---

# Run an ONNX model with multiple inputs in C#

Load the model as a `Session`, pass the inputs as a dictionary of named tensors, and read the outputs by name. The model here, `scorer.onnx`, takes `features` and `bias` and returns `score = features × W + bias` and `best`, the largest score of each row.

```shell
dotnet new console -o MultiInput && cd MultiInput
dotnet add package RecursiveAI.OneModel --version 1.0.0
curl -O https://recursiveai.org/onemodel/samples/scorer.onnx
```

Put this in `Program.cs` and run `dotnet run`:

```csharp
using RecursiveAI.OneModel.Graph;
using RecursiveAI.OneModel.Tensors;

using var session = Session.Load("scorer.onnx");
foreach (var i in session.Inputs) Console.WriteLine($"in  {i.Name}: {i.DType} [{string.Join(", ", i.Dims)}]");
foreach (var o in session.Outputs) Console.WriteLine($"out {o.Name}: {o.DType} [{string.Join(", ", o.Dims)}]");

using var features = Tensor.FromSpan<float>([1f, 2f, 3f, 4f, 5f, 6f], 2, 3);
using var bias = Tensor.FromSpan<float>([0.5f, 1.5f], 1, 2);

using SessionOutputs outputs = session.Run(new Dictionary<string, Tensor>
{
    ["bias"] = bias,
    ["features"] = features,
});
Tensor score = outputs["score"], best = outputs["best"];   // owned by `outputs`

Console.WriteLine($"score {score.Shape}: {string.Join(", ", score.ToArray<float>())}");
Console.WriteLine($"best  {best.Shape}: {string.Join(", ", best.ToArray<float>())}");
```

```text
in  features: F32 [batch, 3]
in  bias: F32 [1, 2]
out score: F32 [batch, 2]
out best: F32 [batch]
score [2,2]: 4.5, 6.5, 10.5, 12.5
best  [2]: 6.5, 12.5
```

## How `Session.Run` works

- Every model input is passed once, by name; the order doesn’t matter. Inputs are read, never changed and not kept.
- The result is a `SessionOutputs` that owns the output tensors: read them by name or position, and one `using` releases them all. Copy out what you keep with `ToArray<T>()`, or `Clone()` a tensor.
- `session.Run(inputs, ["best"])` returns only the outputs you name, in that order.
- `batch` is a symbolic dimension: the session makes a plan for each set of input shapes it sees and caches it (16 by default, `ModelOptions.MaxCachedPlans`).
- A wrong name, element type or shape is a `SessionException` (`InvalidInput`) before anything runs, and the message lists the valid names.

In a loop, the array form `session.Run(inputNames, inputs, outputNames, outputs)` reuses your output tensors: an empty slot gets a new tensor you own, and a tensor of the right type and shape is overwritten in place. For one input and one output, `session.Run<float, float>(inputName, input, shape, outputName, output)` copies in and out of your buffers and allocates nothing once warm. Sessions are thread-safe. More in [sessions and plans](https://recursiveai.org/onemodel/docs/graph-runtime/).

## Limits

- **Tensors hold pooled memory that `Dispose` returns to the pool.** Dispose every tensor you create, and the `SessionOutputs`, as the `using` declarations do here. Disposing twice is harmless; reading a tensor after its memory is released throws `StaleTensorException`. The rule in full: [tensors and ownership](https://recursiveai.org/onemodel/docs/guide-tensors/).
- **CPU only**, on .NET 10.

For one input and one output, the shorter form is in [Run an ONNX model in C#](https://recursiveai.org/onemodel/cookbook/run-onnx-model/).
