{"record":{"id":"12af0af06c2389f0","repo":"stride3d/stride","slug":"the-given-iviewmodelserviceprovider-instance-does-not","errorCode":null,"errorMessage":"The given IViewModelServiceProvider instance does not contain an service implementing IUndoRedoService.","messagePattern":"The given IViewModelServiceProvider instance does not contain an service implementing IUndoRedoService\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"sources/presentation/Stride.Core.Presentation/ViewModels/EditableViewModel.cs","lineNumber":29,"sourceCode":"using Stride.Core.Presentation.Dirtiables;\n\nnamespace Stride.Core.Presentation.ViewModels;\n\npublic abstract class EditableViewModel : DispatcherViewModel\n{\n    private readonly Dictionary<string, object?> preEditValues = [];\n    private readonly HashSet<string> uncancellableChanges = [];\n    private readonly List<string> suspendedCollections = [];\n\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"EditableViewModel\"/> class.\n    /// </summary>\n    /// <param name=\"serviceProvider\">A service provider that can provide a <see cref=\"IDispatcherService\"/> and an <see cref=\"IUndoRedoService\"/> to use for this view model.</param>\n    protected EditableViewModel(IViewModelServiceProvider serviceProvider)\n        : base(serviceProvider)\n    {\n        if (serviceProvider.TryGet<IUndoRedoService>() == null)\n            throw new ArgumentException($\"The given {nameof(IViewModelServiceProvider)} instance does not contain an service implementing {nameof(IUndoRedoService)}.\");\n    }\n\n    public abstract IEnumerable<IDirtiable> Dirtiables { get; }\n\n    /// <summary>\n    /// Gets the undo/redo service used by this view model.\n    /// </summary>\n    public IUndoRedoService UndoRedoService => ServiceProvider.Get<IUndoRedoService>();\n\n    protected void RegisterMemberCollectionForActionStack(string name, INotifyCollectionChanged collection)\n    {\n        ArgumentNullException.ThrowIfNull(collection);\n\n        collection.CollectionChanged += (sender, e) => CollectionChanged(sender, e, name);\n    }\n\n    protected IDisposable SuspendNotificationForCollectionChange(string name)\n    {","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/presentation/Stride.Core.Presentation/ViewModels/EditableViewModel.cs#L11-L47","documentation":"EditableViewModel depends on an IUndoRedoService (for transactions and dirty-state management) in addition to the base services. The constructor validates the service provider up front and throws ArgumentException if one cannot be resolved, giving a clear message instead of a later NullReferenceException.","triggerScenarios":"Constructing a concrete EditableViewModel with an IViewModelServiceProvider that was not registered with an IUndoRedoService implementation (e.g. a minimal service provider or a mock missing the undo/redo service).","commonSituations":"Unit tests building a provider with only a dispatcher service; wiring view models before registering undo/redo services; replacing UndoRedoService with a stub that is not registered under the IUndoRedoService interface.","solutions":["Register an IUndoRedoService implementation in the provider before constructing the view model","Use the standard ViewModelServiceProvider helper that wires the default undo/redo service","In tests, add a mock/stub UndoRedoService to the provider","Verify with serviceProvider.TryGet<IUndoRedoService>() before constructing"],"exampleFix":"// before\nvar provider = new ViewModelServiceProvider(new DispatcherService(dispatcher));\nvar vm = new MyEditableViewModel(provider); // throws\n// after\nvar provider = new ViewModelServiceProvider(new DispatcherService(dispatcher), new UndoRedoService());\nvar vm = new MyEditableViewModel(provider);","handlingStrategy":"validation","validationCode":"if (serviceProvider.TryGet<IUndoRedoService>() == null)\n    throw new ArgumentException(\"Provider must include an IUndoRedoService before constructing an EditableViewModel.\");","typeGuard":"static bool HasUndoRedo(IViewModelServiceProvider p) => p.TryGet<IUndoRedoService>() != null;","tryCatchPattern":"try { vm = new MyEditableViewModel(provider); }\ncatch (ArgumentException ex) when (ex.Message.Contains(nameof(IUndoRedoService))) { /* fix provider wiring */ }","preventionTips":["Always register an IUndoRedoService in the provider used for EditableViewModel descendants","In tests, include a stub UndoRedoService in the provider","Construct providers via the standard factory that wires default services"],"tags":["viewmodel","dependency-injection","service-provider","undo"],"backgroundTag":"missing-dependency","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}