View Issue Details

IDProjectCategoryView StatusLast Update
0001840NoesisGUIC# SDKpublic2021-01-21 20:28
Reporterai_enabled Assigned Tosfernandez  
PrioritynormalSeveritymajor 
Status assignedResolutionopen 
Product Version3.0.6 
Summary0001840: NoesisGUI reloading issue: RunClassConstructor invokes static constructor to reinit dependency properties
Description

Hi guys,

I was investigating a few random issues we have and noticed NoesisGUI calling static constructors for our types even when they're already constructed. I understand why it's required (to force reconstructing the dependency properties), however, this is a very hacky solution as NoesisGUI just cannot be 100% sure that it will operate only with the UI-related code. In some cases it invoked static constructors causing reconstructing singletons and other fields that are totally not related to user controls or even UI logic.

For example, imagine we have an item tooltip control that has a dependency property for the item itself. Item is a business entity so it will be never used in the UI directly, but NoesisGUI attempts to register the whole type hierarchy for it including all its public properties that may include things such as physics and audio. While it's a separate big problem in itself (how to handle that case to prevent NoesisGUI from registering certain types), it's a good demonstration when NoesisGUI should not just straight away "reconstruct" types. The side effects could be awful and very hard to debug!

Here is the block of code that is causing the issue:
https://github.com/Noesis/Managed/blob/c2ccfc45ca8a39b8d0d063cdcded1a7d0230678f/Src/Noesis/Core/Src/Core/Extend.cs#L1674

An idea how you can resolve this issue: as you're using this solely to reconstruct the dependency/attached properties, perhaps you should cache all the dependency properties in a Dictionary<Type, RegisteredDependencyProperty[]>. When RunClassConstructor notices that the type was once constructed, it can just foreach the RegisteredDependencyProperty[] array and force their registration.

Regards!

PlatformAny

Relationships

related to 0001897 resolvedsfernandez .NET 5 has broken RunClassConstructor workaround 

Activities

sfernandez

sfernandez

2020-11-19 19:23

manager   ~0006774

Hi,

This code is a big hack that we don't like either, but it had no problems when we initially designed it for Unity, because it was only used when assemblies were unloaded and reloaded again.

I think the first thing to do to improve that code is avoid calling the static constructor if the type doesn't contain any DependencyProperty field.
And as you suggested in the Github issue, directly ignore all BCL types.

Keeping track of previously defined DependencyProperties is not an option because the code of the classes could have changed and new dependency properties added or removed.

What do you think?

ai_enabled

ai_enabled

2020-11-19 19:53

updater   ~0006775

avoid calling the static constructor if the type doesn't contain any DependencyProperty field.
It's definitely a better approach (if no other solutions are available) but there is still a chance that it may cause unexpected side effects.

because the code of the classes could have changed and new dependency properties added or removed.
Do you refer to Unity assembly hot-reloading? Does it actually able to expand existing Type objects with new properties? Or it creates new Type objects as they're coming from a new (rebuilt) assembly?

sfernandez

sfernandez

2020-11-19 20:16

manager   ~0006776

Yes, Unity unloads current assembly, compiles a new one with any changes you have made to your classes, and loads the new assembly.
So although the types have the same namespace+name, they are in fact new type objects on a new assembly (and old type objects don't exist anymore).

ai_enabled

ai_enabled

2020-11-19 20:30

updater   ~0006777

Ok, then It's the same as what we have for CryoFall live reloading—assemblies are just rebuilt and the data is re-serialized.

Then my original suggestion should work fine:

perhaps you should cache all the dependency properties in a Dictionary<Type, RegisteredDependencyProperty[]>. When RunClassConstructor notices that the type was once constructed, it can just foreach the RegisteredDependencyProperty[] array and force their registration.

As the old types are no longer used their instances will just lie down forever in the dictionary. New types will be registered as new entries in the dictionary, and have their static constructor called only once. And if you need to rebuild the type in RunClassConstructor, you will need to call re-registering of the previously registered dependency properties instead of force calling the static constructor. The only challenge here is to wrap all the dependency properties (when they're registered for the first time), so you can call their registration again when necessary for the specific type.

ai_enabled

ai_enabled

2021-01-21 20:28

updater   ~0006985

Further discussion in this ticket https://www.noesisengine.com/bugs/view.php?id=1897#bugnotes

Issue History

Date Modified Username Field Change
2020-11-19 18:01 ai_enabled New Issue
2020-11-19 19:23 sfernandez Assigned To => sfernandez
2020-11-19 19:23 sfernandez Status new => feedback
2020-11-19 19:23 sfernandez Note Added: 0006774
2020-11-19 19:53 ai_enabled Note Added: 0006775
2020-11-19 19:53 ai_enabled Status feedback => assigned
2020-11-19 20:16 sfernandez Status assigned => feedback
2020-11-19 20:16 sfernandez Note Added: 0006776
2020-11-19 20:30 ai_enabled Note Added: 0006777
2020-11-19 20:30 ai_enabled Status feedback => assigned
2021-01-21 18:11 sfernandez Relationship added related to 0001897
2021-01-21 20:28 ai_enabled Note Added: 0006985