{"record":{"id":"ff15a0cc460023fc","repo":"stride3d/stride","slug":"the-strideassetsviewmodel-class-can-be-instanced-only-once","errorCode":null,"errorMessage":"The StrideAssetsViewModel class can be instanced only once.","messagePattern":"The StrideAssetsViewModel class can be instanced only once\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"critical","filePath":"sources/editor/Stride.Assets.Presentation/ViewModel/StrideAssetsViewModel.cs","lineNumber":21,"sourceCode":"using System;\nusing System.Threading.Tasks;\nusing Stride.Core.Assets.Editor.ViewModel;\nusing Stride.Core.Presentation.ViewModels;\n\nnamespace Stride.Assets.Presentation.ViewModel\n{\n    public class StrideAssetsViewModel : DispatcherViewModel\n    {\n        private static readonly TaskCompletionSource<StrideAssetsViewModel> instance = new TaskCompletionSource<StrideAssetsViewModel>();\n\n        public StrideAssetsViewModel(SessionViewModel session) : base(session.ServiceProvider)\n        {\n            Session = session;\n\n            Code = new CodeViewModel(this);\n\n            if (Instance != null)\n                throw new InvalidOperationException($\"The {nameof(StrideAssetsViewModel)} class can be instanced only once.\");\n\n            instance.TrySetResult(this);\n            Instance = this;\n        }\n\n        public static Task<StrideAssetsViewModel> InstanceTask => instance.Task;\n\n        public static StrideAssetsViewModel Instance { get; private set; }\n\n        public SessionViewModel Session { get; }\n\n        public CodeViewModel Code { get; }\n    }\n}\n","sourceCodeStart":3,"sourceCodeEnd":36,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/editor/Stride.Assets.Presentation/ViewModel/StrideAssetsViewModel.cs#L3-L36","documentation":"StrideAssetsViewModel is a process-wide singleton for the asset editor session. Its constructor throws InvalidOperationException if a static Instance already exists, because duplicate instances would create two competing views of the same asset session. Note the throw happens AFTER constructing CodeViewModel but the TaskCompletionSource `instance.TrySetResult(this)` runs after the check, so a second construction aborts before satisfying the instance task.","triggerScenarios":"Constructing `new StrideAssetsViewModel(...)` a second time in the same AppDomain, e.g. re-running editor startup code, creating a second assets view/window, or unit tests that build the view model per test without resetting the static Instance.","commonSituations":"Opening a second editor window for the same session; plugin/extension code initializing the assets view model independently; test fixtures that don't isolate static state; hot-reload scenarios re-running initialization.","solutions":["Reuse the existing instance via `StrideAssetsViewModel.InstanceTask` (await it) instead of constructing a new one.","Guard construction with `if (StrideAssetsViewModel.Instance == null)` before instantiating.","In tests, create the view model in a fresh AppDomain/assembly-reload context or accept it as a shared fixture.","For multi-session needs, refactor to hold per-session view models instead of relying on the singleton."],"exampleFix":"// before\nvar vm = new StrideAssetsViewModel(session);\n// after\nvar vm = await StrideAssetsViewModel.InstanceTask; // or: if (StrideAssetsViewModel.Instance != null) return;","handlingStrategy":"fallback","validationCode":"if (StrideAssetsViewModel.Instance != null)\n    return await StrideAssetsViewModel.InstanceTask;\nvar vm = new StrideAssetsViewModel(session);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always acquire the assets view model through StrideAssetsViewModel.InstanceTask, never via direct construction in feature code.","Centralize construction in one composition-root location.","In tests, treat it as a shared singleton fixture or isolate per AppDomain."],"tags":["singleton","invalid-state-transition","wpf","initialization"],"backgroundTag":"invalid-state-transition","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"}