diff --git a/src/Worker/Core/DependencyInjection/DurableTaskWorkerBuilderExtensions.cs b/src/Worker/Core/DependencyInjection/DurableTaskWorkerBuilderExtensions.cs
index d42845f60..972fdcf3a 100644
--- a/src/Worker/Core/DependencyInjection/DurableTaskWorkerBuilderExtensions.cs
+++ b/src/Worker/Core/DependencyInjection/DurableTaskWorkerBuilderExtensions.cs
@@ -143,10 +143,10 @@ public static IDurableTaskWorkerBuilder UseOrchestrationFilter(this IDurableTask
///
/// The builder to set the builder target for.
/// The instance of a to use.
- /// If null, the auto-generated default filters will be cleared.
+ /// If null, any previously configured filters will be cleared and filtering will be disabled.
/// The same instance, allowing for method chaining.
- /// Work item filters are auto-generated from the registry by default.
- /// Use this method with explicit filters to override the defaults, or with null to opt out of filtering entirely.
+ /// By default, no work item filters are applied and the worker processes all work items.
+ /// Use this method with explicit filters to enable filtering, or with null to disable filtering.
public static IDurableTaskWorkerBuilder UseWorkItemFilters(this IDurableTaskWorkerBuilder builder, DurableTaskWorkerWorkItemFilters? workItemFilters)
{
Check.NotNull(builder);
@@ -172,4 +172,43 @@ public static IDurableTaskWorkerBuilder UseWorkItemFilters(this IDurableTaskWork
return builder;
}
+
+ ///
+ /// Enables work item filtering by auto-generating filters from the .
+ /// When enabled, the backend will only dispatch work items for registered orchestrations, activities,
+ /// and entities to this worker.
+ ///
+ /// The builder to set the builder target for.
+ /// The same instance, allowing for method chaining.
+ ///
+ ///
+ /// Work item filtering can improve efficiency in multi-worker deployments by ensuring each worker
+ /// only receives work items it can handle. However, if an orchestration calls a task type
+ /// (e.g., an entity, activity, or sub-orchestrator) that is not registered with any connected worker,
+ /// the call may hang indefinitely instead of failing with an error.
+ ///
+ ///
+ /// Only use this method when all task types referenced by orchestrations are guaranteed to be
+ /// registered with at least one connected worker.
+ ///
+ ///
+ public static IDurableTaskWorkerBuilder UseWorkItemFilters(this IDurableTaskWorkerBuilder builder)
+ {
+ Check.NotNull(builder);
+
+ builder.Services.AddOptions(builder.Name)
+ .PostConfigure, IOptionsMonitor>(
+ (opts, registryMonitor, workerOptionsMonitor) =>
+ {
+ DurableTaskRegistry registry = registryMonitor.Get(builder.Name);
+ DurableTaskWorkerOptions workerOptions = workerOptionsMonitor.Get(builder.Name);
+ DurableTaskWorkerWorkItemFilters generated =
+ DurableTaskWorkerWorkItemFilters.FromDurableTaskRegistry(registry, workerOptions);
+ opts.Orchestrations = generated.Orchestrations;
+ opts.Activities = generated.Activities;
+ opts.Entities = generated.Entities;
+ });
+
+ return builder;
+ }
}
diff --git a/src/Worker/Core/DependencyInjection/ServiceCollectionExtensions.cs b/src/Worker/Core/DependencyInjection/ServiceCollectionExtensions.cs
index 9e8a93c6c..e68c550cf 100644
--- a/src/Worker/Core/DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/Worker/Core/DependencyInjection/ServiceCollectionExtensions.cs
@@ -86,21 +86,6 @@ static IServiceCollection ConfigureDurableOptions(IServiceCollection services, s
}
});
- // Auto-generate work item filters from the registry by default.
- // Users can override these by calling UseWorkItemFilters(customFilters) on the builder.
- services.AddOptions(name)
- .Configure, IOptionsMonitor>(
- (opts, registryMonitor, workerOptionsMonitor) =>
- {
- DurableTaskRegistry registry = registryMonitor.Get(name);
- DurableTaskWorkerOptions workerOptions = workerOptionsMonitor.Get(name);
- DurableTaskWorkerWorkItemFilters generated =
- DurableTaskWorkerWorkItemFilters.FromDurableTaskRegistry(registry, workerOptions);
- opts.Orchestrations = generated.Orchestrations;
- opts.Activities = generated.Activities;
- opts.Entities = generated.Entities;
- });
-
return services;
}
diff --git a/src/Worker/Core/DurableTaskWorkerWorkItemFilters.cs b/src/Worker/Core/DurableTaskWorkerWorkItemFilters.cs
index c29ff95ca..8a5df2f1d 100644
--- a/src/Worker/Core/DurableTaskWorkerWorkItemFilters.cs
+++ b/src/Worker/Core/DurableTaskWorkerWorkItemFilters.cs
@@ -6,9 +6,9 @@ namespace Microsoft.DurableTask.Worker;
///
/// A class that represents work item filters for a Durable Task Worker. These filters are passed to the backend
/// and only work items matching the filters will be processed by the worker. If no filters are provided,
-/// the worker will process all work items. By default, these are auto-generated from the registered orchestrations,
-/// activities, and entities in the . To opt-out of filters, provide a null
-/// value to the method when configuring the worker.
+/// the worker will process all work items. To opt-in to work item filtering, call
+/// on the worker builder with either
+/// explicit filters or auto-generated filters from the .
///
public class DurableTaskWorkerWorkItemFilters
{
diff --git a/test/Worker/Core.Tests/DependencyInjection/UseWorkItemFiltersTests.cs b/test/Worker/Core.Tests/DependencyInjection/UseWorkItemFiltersTests.cs
index 9e325fe7b..adaac30a7 100644
--- a/test/Worker/Core.Tests/DependencyInjection/UseWorkItemFiltersTests.cs
+++ b/test/Worker/Core.Tests/DependencyInjection/UseWorkItemFiltersTests.cs
@@ -63,7 +63,7 @@ public void UseWorkItemFilters_ReturnsBuilder_ForChaining()
}
[Fact]
- public void WorkItemFilters_DefaultFromRegistry_WhenNoExplicitFiltersConfigured()
+ public void WorkItemFilters_DefaultFromRegistry_WhenExplicitlyOptedIn()
{
// Arrange
ServiceCollection services = new();
@@ -74,6 +74,7 @@ public void WorkItemFilters_DefaultFromRegistry_WhenNoExplicitFiltersConfigured(
registry.AddOrchestrator();
registry.AddActivity();
});
+ builder.UseWorkItemFilters();
});
// Act
@@ -88,7 +89,7 @@ public void WorkItemFilters_DefaultFromRegistry_WhenNoExplicitFiltersConfigured(
}
[Fact]
- public void WorkItemFilters_DefaultWithEntity_WhenNoExplicitFiltersConfigured()
+ public void WorkItemFilters_DefaultWithEntity_WhenExplicitlyOptedIn()
{
// Arrange
ServiceCollection services = new();
@@ -98,6 +99,7 @@ public void WorkItemFilters_DefaultWithEntity_WhenNoExplicitFiltersConfigured()
{
registry.AddEntity();
});
+ builder.UseWorkItemFilters();
});
// Act
@@ -111,7 +113,7 @@ public void WorkItemFilters_DefaultWithEntity_WhenNoExplicitFiltersConfigured()
}
[Fact]
- public void WorkItemFilters_DefaultNullWithVersioningCurrentOrOlder_WhenNoExplicitFiltersConfigured()
+ public void WorkItemFilters_DefaultNullWithVersioningCurrentOrOlder_WhenExplicitlyOptedIn()
{
// Arrange
ServiceCollection services = new();
@@ -130,6 +132,7 @@ public void WorkItemFilters_DefaultNullWithVersioningCurrentOrOlder_WhenNoExplic
MatchStrategy = DurableTaskWorkerOptions.VersionMatchStrategy.CurrentOrOlder,
};
});
+ builder.UseWorkItemFilters();
});
// Act
@@ -144,7 +147,7 @@ public void WorkItemFilters_DefaultNullWithVersioningCurrentOrOlder_WhenNoExplic
}
[Fact]
- public void WorkItemFilters_DefaultNullWithVersioningNone_WhenNoExplicitFiltersConfigured()
+ public void WorkItemFilters_DefaultNullWithVersioningNone_WhenExplicitlyOptedIn()
{
// Arrange
ServiceCollection services = new();
@@ -163,6 +166,7 @@ public void WorkItemFilters_DefaultNullWithVersioningNone_WhenNoExplicitFiltersC
MatchStrategy = DurableTaskWorkerOptions.VersionMatchStrategy.None,
};
});
+ builder.UseWorkItemFilters();
});
// Act
@@ -177,7 +181,7 @@ public void WorkItemFilters_DefaultNullWithVersioningNone_WhenNoExplicitFiltersC
}
[Fact]
- public void WorkItemFilters_DefaultVersionWithVersioningStrict_WhenNoExplicitFiltersConfigured()
+ public void WorkItemFilters_DefaultVersionWithVersioningStrict_WhenExplicitlyOptedIn()
{
// Arrange
ServiceCollection services = new();
@@ -196,6 +200,7 @@ public void WorkItemFilters_DefaultVersionWithVersioningStrict_WhenNoExplicitFil
MatchStrategy = DurableTaskWorkerOptions.VersionMatchStrategy.Strict,
};
});
+ builder.UseWorkItemFilters();
});
// Act
@@ -232,7 +237,34 @@ public void WorkItemFilters_DefaultEmptyRegistry_ProducesEmptyFilters()
}
[Fact]
- public void WorkItemFilters_ExplicitFiltersOverrideDefaults()
+ public void WorkItemFilters_DefaultNoFilters_WhenNoExplicitOptIn()
+ {
+ // Arrange - register tasks but do NOT call UseWorkItemFilters()
+ ServiceCollection services = new();
+ services.AddDurableTaskWorker("test", builder =>
+ {
+ builder.AddTasks(registry =>
+ {
+ registry.AddOrchestrator();
+ registry.AddActivity();
+ registry.AddEntity();
+ });
+ });
+
+ // Act
+ ServiceProvider provider = services.BuildServiceProvider();
+ IOptionsMonitor filtersMonitor =
+ provider.GetRequiredService>();
+ DurableTaskWorkerWorkItemFilters actual = filtersMonitor.Get("test");
+
+ // Assert - with no explicit opt-in, filters should be empty (legacy behavior)
+ actual.Orchestrations.Should().BeEmpty();
+ actual.Activities.Should().BeEmpty();
+ actual.Entities.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void WorkItemFilters_ExplicitFiltersOverrideAutoGenerated()
{
// Arrange
ServiceCollection services = new();
@@ -266,7 +298,7 @@ public void WorkItemFilters_ExplicitFiltersOverrideDefaults()
}
[Fact]
- public void WorkItemFilters_NullOverwritesDefaults()
+ public void WorkItemFilters_NullClearsAutoGeneratedFilters()
{
// Arrange
ServiceCollection services = new();
@@ -277,7 +309,8 @@ public void WorkItemFilters_NullOverwritesDefaults()
registry.AddOrchestrator();
registry.AddActivity();
});
- builder.UseWorkItemFilters(null);
+ builder.UseWorkItemFilters(); // opt-in to auto-generated filters
+ builder.UseWorkItemFilters(null); // then clear them
});
// Act
@@ -293,7 +326,7 @@ public void WorkItemFilters_NullOverwritesDefaults()
}
[Fact]
- public void WorkItemFilters_EmptyFiltersOverrideDefaults()
+ public void WorkItemFilters_EmptyFiltersClearAutoGenerated()
{
// Arrange
ServiceCollection services = new();
@@ -311,7 +344,8 @@ public void WorkItemFilters_EmptyFiltersOverrideDefaults()
registry.AddOrchestrator();
registry.AddActivity();
});
- builder.UseWorkItemFilters(emptyFilters);
+ builder.UseWorkItemFilters(); // opt-in to auto-generated filters
+ builder.UseWorkItemFilters(emptyFilters); // then clear them with empty
});
// Act
@@ -327,17 +361,19 @@ public void WorkItemFilters_EmptyFiltersOverrideDefaults()
}
[Fact]
- public void WorkItemFilters_NamedBuilders_HaveUniqueDefaultFilters()
+ public void WorkItemFilters_NamedBuilders_HaveUniqueDefaultFilters_WhenExplicitlyOptedIn()
{
// Arrange
ServiceCollection services = new();
services.AddDurableTaskWorker("worker1", builder =>
{
builder.AddTasks(registry => registry.AddOrchestrator());
+ builder.UseWorkItemFilters();
});
services.AddDurableTaskWorker("worker2", builder =>
{
builder.AddTasks(registry => registry.AddActivity());
+ builder.UseWorkItemFilters();
});
// Act
diff --git a/test/Worker/Grpc.Tests/DurableTaskWorkerWorkItemFiltersExtensionTests.cs b/test/Worker/Grpc.Tests/DurableTaskWorkerWorkItemFiltersExtensionTests.cs
index 5921b4dee..e8c454831 100644
--- a/test/Worker/Grpc.Tests/DurableTaskWorkerWorkItemFiltersExtensionTests.cs
+++ b/test/Worker/Grpc.Tests/DurableTaskWorkerWorkItemFiltersExtensionTests.cs
@@ -300,7 +300,7 @@ public void ToGrpcWorkItemFilters_WithMixedFilters_ConvertsAll()
}
[Fact]
- public void WorkerConstruction_DefaultFilters_FlowToWorker()
+ public void WorkerConstruction_NoOptIn_HasEmptyFilters()
{
// Arrange
ServiceCollection services = new();
@@ -325,10 +325,10 @@ public void WorkerConstruction_DefaultFilters_FlowToWorker()
.GetField("workItemFilters", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!
.GetValue(hosted);
- // Assert
+ // Assert - without UseWorkItemFilters(), filters should be empty (no filtering)
filters.Should().NotBeNull();
- filters!.Orchestrations.Should().ContainSingle(o => o.Name == nameof(TestOrchestrator));
- filters.Activities.Should().ContainSingle(a => a.Name == nameof(TestActivity));
+ filters!.Orchestrations.Should().BeEmpty();
+ filters.Activities.Should().BeEmpty();
}
[Fact]