The info is provided at runtime by the ModelInfoProvider, which contains a FieldInfoProvider instance which contains all the field information data for the fields in an entity. The FieldInfoProvider also has precalculated objects for fields for an entity, so only the values are non-static. These have a fixed length (namely the length related to the # of fields in the entity).
To make that dynamic you have to make sure the FieldInfoprovider provides the right EntityFields(2) objects at runtime. I would only focus on Adapter. See e.g. FieldInfoProvider.cs, like 396 (public IEntityFields2 GetEntityFields(IInheritanceInfoProvider inheritanceProvider, string entityName)
) how this works.
For your situation you have to make sure you provide the fields object using the dynamic size you want. That's one part.
Another other part is that for the entity itself it has to read/write from/to that object in the entity so you have to add your properties in some form the same way as the generated ones.
For generating queries, you have to make sure when you fetch entities of a given type, the DQE will produce a projection of all the fields in the projection. The information from that is from the PersistenceInfoProvider. This object (which is generated and an instance is available in the adapter) contains the mapping information for an entity and its fields. This has to be in sync with the fields in the field info provider, so a field at offset 3 in the EntityFields2 object has its persistence info also at offset 3 in the array with fieldpersistenceinfo's.
The PersistenceInfoProvider too uses precalculated static objects as it's readonly data. To dynamically change these, you have to make sure it works well in a multi-threaded environment. This is crucial as you'll otherwise run into issues and likely come here asking why things break and it'll be hard to track down. So be sure to lock everything with readwrite spin locks (as the nature of the activity on the data is many reads and rare writes).
As a last remark I'd like to add that none of this is really supported, as in: it's not an official api, so if you make these changes, we can't know what you changed so if you run into issues because of these changes, it's highly likely you have to fix it yourself too. But I hope with the above info it's abit more clear how things work internally. It's not rocketscience, so once you see how the various elements relate to each other it's fairly straight forward.