ConfigurationClient Setup

Client Setup

Configure your application to use IClusterClient for querying grains by their state.

Basic Configuration

The search client is configured alongside your Orleans services:

Program.cs
var builder = WebApplication.CreateBuilder(args);
 
// Configure Orleans (silo or client)
builder.UseOrleans(siloBuilder =>
{
    siloBuilder.UseLocalhostClustering();
    siloBuilder.AddMemoryGrainStorage("InnerStorage");
    siloBuilder.AddSearchableGrainStorage("InnerStorage");
});
 
// Add Orleans.Search - this adds the Search extension method
builder.Services.AddOrleansSearch()
    .UsePostgreSql("Host=localhost;Database=orleanssearch;Username=postgres;Password=postgres");
 
var app = builder.Build();

Using IClusterClient

Inject IClusterClient wherever you need to search for grains:

public class UserService
{
    private readonly IClusterClient _client;
 
    public UserService(IClusterClient client)
    {
        _client = client;
    }
 
    public async Task<IUserGrain?> FindByEmailAsync(string email)
    {
        return await _client.Search<IUserGrain>()
            .Where(u => u.Email == email)
            .FirstOrDefaultAsync();
    }
 
    public async Task<List<IUserGrain>> GetActiveUsersAsync()
    {
        return await _client.Search<IUserGrain>()
            .Where(u => u.IsActive == true)
            .ToListAsync();
    }
}

In Controllers

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly IClusterClient _client;
 
    public UsersController(IClusterClient client)
    {
        _client = client;
    }
 
    [HttpGet("search")]
    public async Task<IActionResult> Search([FromQuery] string email)
    {
        var users = await _client.Search<IUserGrain>()
            .Where(u => u.Email.Contains(email))
            .ToListAsync();
 
        var results = new List<object>();
        foreach (var user in users)
        {
            results.Add(new
            {
                Id = user.GetPrimaryKeyString(),
                Email = await user.GetEmailAsync()
            });
        }
 
        return Ok(results);
    }
}

In Minimal APIs

app.MapGet("/api/users/search", async (
    string email,
    IClusterClient client) =>
{
    var users = await client.Search<IUserGrain>()
        .Where(u => u.Email.Contains(email))
        .ToListAsync();
 
    return Results.Ok(users.Select(u => u.GetPrimaryKeyString()));
});

Client-Only Applications

If you’re building a client that connects to an Orleans cluster (not hosting a silo):

var builder = Host.CreateApplicationBuilder(args);
 
// Configure Orleans client
builder.UseOrleansClient(clientBuilder =>
{
    clientBuilder.UseLocalhostClustering();
});
 
// Add search services
builder.Services.AddOrleansSearch()
    .UsePostgreSql("Host=localhost;Database=orleanssearch;Username=postgres;Password=postgres");
 
var host = builder.Build();
 
// Get the searchable client
var client = host.Services.GetRequiredService<IClusterClient>();
 
// Search for grains
var users = await client.Search<IUserGrain>()
    .Where(u => u.IsActive == true)
    .ToListAsync();

Standard Grain Operations

Since Search is an extension method on IClusterClient, you use the same client for both standard grain operations and search:

public class UserService
{
    private readonly IClusterClient _client;
 
    public UserService(IClusterClient client)
    {
        _client = client;
    }
 
    // Get a specific grain by ID (standard Orleans)
    public IUserGrain GetUser(string id)
    {
        return _client.GetGrain<IUserGrain>(id);
    }
 
    // Search for grains by state (Orleans.Search)
    public async Task<List<IUserGrain>> SearchUsersAsync(string emailDomain)
    {
        return await _client.Search<IUserGrain>()
            .Where(u => u.Email.Contains(emailDomain))
            .ToListAsync();
    }
}

Next Steps