Cannot call "EntityManager.AddComponentData" with an unknown IComponentData type

Hi all,

I’m am trying to dynamically create an entity with IComponentData components based on data deserialised from JSON.

This results in the following code:

EntityManager.AddComponentData(entity, componentData);```

The return type of compDef.CreateComponentData is IComponentData and this causes the following error:

```The type 'Unity.Entities.IComponentData' must be a non-nullable value type in order to use it as parameter 'T'```

Is there a way of working around this and adding components without knowing which type they are?

Make sure IComponentData is a struct (where T : struct) and only has blittable variables.

1 Like

System.String is not blittable what to use instead?

The types returned from that method are all structs and are all using non-blittable types. The error is a compile-time error as it can’t determine if the return type is non-nullable. I’ve tried adding a type parameter with the “where T : struct, IComponentData” but then the consumer needs to pass in the type which it doesn’t know.

Surely there is some way of making this work? It seems like a fairly major missing feature to not be able to populate an entity dynamically.

@SubPixelPerfect I think you should make a separate thread for that.

I think you need a specific class because its a generic method call!

You call AddComponent(…)
IComponentData is nullable and can’t be used directly here.

Try to cast your componentData to the specific ComponentType like:

MyComponent componentData = (MyComponent)compDef.CreateComponentData();

Now you can call AddComponent(…) and it will work!

That’s my point, I cannot know what the type is at compile time. I’m dynamically generating the components to be added to an entity.

var componentData = compDef.CreateComponentData<Cell>();
EntityManager.SetComponentData(entity, componentData);

I’m enumerating through a collection of “CompDef” instances each of which return a different IComponentData for example Cell or Buildable etc. This means I cannot possibly know what those types are going to be at compile time.

Use a Tag to get the information of what type the specific component is and then cast to it!

Otherwise it will not work!

Do you have an example of what you mean by that? If I need to manually cast to a type then that’s not going to result in particularly clean code considering there will be a large number of component types to handle.

I don’t know what jsonlib you’re using.

Anyway, may your JSON string looks like this:

{
    "Cell":
    {
        "x":"1901",
        "y":"412"
    }
   "Buildable":
   {
      //What ever
   }
}

You need to read the type of the current json object e.g. “Cell”
If you have this information you can do the following:

string myClassString = // some call from your json lib to retreive the current class

switch(myClassString ){
    case "Cell":
        Cell cellData = compDef.CreateComponentData<Cell>();
        EntityManager.SetComponentData(entity, cellData);
    break;

    //other cases ...

    default:
         throw new NotImplementedException();
}

That’s what I thought you may have meant, having a mammoth switch case statement seems like an absolute last resort since there would end up being a huge number of cases. I was hoping for an alternative to hard-coding all of the different component data types.

EDIT:
I’ve another idee… Maybe this apporach is better…
Take use of an Interface

interface IJsonbase {
     void AssignToEntity(EntityManager manager, Entity entity);
}

struct Cell : IJsonbase, IComponentData {
       public int X;
       public int Y;

       public override void AssignToEntity(EntityManager manager, Entity entity) {
             manager.SetComponentData(entity, this);
       }
}




//Now you can do something like this
IJsonbase componentData = compDef.CreateComponentData();
componentData.AssignToEntity(EntityManager, entity);

You can’t use inheritance with structs, this would have been my preferred option otherwise.

you can use interface (it will box but you are already boxing). still requires each concrete type to implement the method.
or you can use reflection to generate the ‘SetComponentData’ method at runtime (it may be worse)

I think EntityManager has a ‘SetComponentBoxed(Entity, object)’ or similar… I saw some post in this forum mentioning it

@M_R looks like that SetComponentBoxed method is internal so can’t rely on that. Any chance you could provide an example regarding your first point?

it was my tread mentioning SetComponentBoxed method
i’m trying to do exactly opposite thing to what you looking for - i’m trying to serialize entity with all its components and values
and there is a very similar problem with generics

for now I ended up with generation generic GetComponentData methods using reflection

public void SerializeEntiy(Entity e){
   
      var types =  this.EntityManager.GetComponentTypes( e );
      for( int j = 0; j != types.Length; j++ ){
        ComponentType ct = types[j];
        Type t = ct.GetManagedType();
        
        if( t.GetInterfaces().Contains( typeof( IComponentData ) ) ){
          MethodInfo methodInfo = typeof(EntityManager).GetMethod("GetComponentData");
          MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(t);
          var parameters = new object[]{e};
          object componentData = genericMethodInfo.Invoke( EntityManager, parameters );
          Debug.Log(t +" : "+ JsonUtility.ToJson( componentData ) );
        }

      }
      types.Dispose();
    }

very ugly solution but works, can’t find a better way so far

the interface approach is what @Spy-Shifty_1 did in their post. (it was edited)
the reflection approach is almost exactly as @SubPixelPerfect has
(1. I thought GetMethod(“name”) didn’t work with generic methods, I usually had to do GetMethods().First(m => m.MethodName == “name”). maybe it’s something that got fixed with the new runtime.
2. I’d cache heavily though, reflection is heavy)

Thanks all!

I’ve ended up using the reflection approach to reduce the amount of code duplication, seems to be working and hasn’t added too much overhead.

Would be useful to have this scenario supported directly by the ECS API but we’ll see if that changes between now and release.