This is a very persistent exception I’m facing when selecting a value in any of my dropdowns that previously worked, before the whole thing with restructuring the sub view models as to be nullable and instantiated at PostBack.
It is trying to construct Operation, not the MeterOperation where you have the constructor.
Even if MeterOperation inherits from Operation, DotVVM will not try to instantiate any other type than the declared property type. The “polymorphism” only works (somewhat) if you create the instance before deserialization.
EDIT - solution: make sure it is Newtonsoft.Json.JsonConstructor
This is related to a piece of code shown in another post:
I thought that if OperationInstance will be nullable, I will instantiate it only when it is needed. That is here, in the command handler for button click.
The property:
It crashes.
But what I’ve discovered is that if go like this:
It works.
And if I go instead like this:
It also crashes.
LE:
My desire is to instantiate it only when it is needed. Apparently it needs instantiation always. Any idea?
holy mother of piglets! This seems to be it. Working if like this:
But!
Unfortunately I have to renounce using primary constructors if I must go with this attribute What do you think? Can we have best of both worlds?
Meanwhile, I will revert all this mess and return with a confirmation…
Ok, great! This JsonConstructor requirement is there for backwards compatibility with DotVVM 4.0, so I’d very happily just allow primary constructor by default, since it could not be used by the old code. Unfortunately, it seems that I cannot easily tell it apart using reflection
If the constructor parameters are used in method/property bodies, C# creates compiler generated fields named <{parameterName}>P. We could tell it apart by this, but no fields are generated if the parameter is only used to initialize properties or other fields, so it wouldn’t be reliable. If you (or anyone else) would have ideas, they are definitely welcome
I just use JsonConstructor to make the app work and not crash when I PostBack, that’s all. I would be happy to not use this attr but what alternative there is?
Now I have another problem , with the nested view models that have properties from the parent view model fed in them through the primary constructor (see below code).
I tried to find these exceptions on the web and no luck, suggesting me that they are very specific to DotVVM, more so with the declaring type being DotVVM.Framework.
And the failing primary constructor of the SVM:
Here, InboundSVM is a sub view model (S-V-M) part of the view model of the MarkupControl.
The SectionNewAAASVM is instantiated here:
At PostBack, in a method called by the parent view model of the MarkupControl (the grandparent view model, that is), here:
Aaaand, those things passed in (InboundParams, UserAlerts etc.) are properties in the VM of the MarkupControl:
I don’t know what to do about this, I suck at dependency injection and I’m going in circles here… Any idea?
PS:
The reason for passing InboundParams, UserAlerts etc. is that the SVMs need to read from or write to them.
PS2:
And InboundParams, UserAlerts etc. are local tot the VM of the MarkupControl, I don’t want to expose them and flood Program.cs with registering them along with many others from other pages, as services and complicating things
if I remove inboundParams, the exception is the same, only that it now targets the next param in line: userAlerts.
PS:
Same for popupCache (which is the only one with its type declared in the MarkupControl’s VM aka the popup aka OperationInstance aka instance of Operation)
So, DotVVM needs some way to get the values to pass into the constructor it needs to call. There are two options
It is a registered service (which it isn’t, it’s a view model)
It is a property from the received JSON (which it isn’t, we can’t serialize loops in JSON)
Options A: Some people do put all the root view models into DI as Scoped services, then you can get the root view model in any nested VM. This works fine (although I’m personally not a fan), but I’d advice agains registering all the nested view models as well - it would get tricky when you’d have more than one instance of it (will be a fun debugging session).
There are tools (Scrutor) to help with the service registration, which registers them based on some conventions (I’d use an empty marker interface or attribute, anything else is the same shit as COME FROM)
Option B is to remove these arguments from the JsonConstructor and fill them in using properties from the parent, in the Load method (Load of the parent if always called before the children). I’d probably do this
Option C is pre-initialize the object, so DotVVM doesn’t have to call the constructors. From the other context, I suppose this is problematic in your case. However, AFAIK, if DotVVM gets null from the client, it will replace your pre-initialized object with the null, so I it’s a possibility if the other options are bad for other reasons.