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
- Search Providers - Configure database providers
- Query API - Available query methods