The Mx Model Reflection module is a technical component from the Mendix App Store that is used in almost every Mendix project. The main reason for this is that other well-known modules from the app store make use of MxModelReflection, such as the ExcelImporter, ExcelExporter, FixedLengthExporter, and DBReplication modules.
It is a very useful component, but what does the MxModelReflection module actually do?
- It allows certain users of the Mendix application to see the module names used in the model.
- Users can select for which modules they want to load the entity information and microflows.
- It adds tables to your database that will save this information.
- By using the objects that represent other entities, there is a meta-model at run-time.
- The meta-model can be used to configure exports and imports of data at run-time, like with the ExcelImporter.
This is very handy! But there are some potential weaknesses:
- When adding this module, you also will add a number of tables to your application database, which is not very elegant. Also, companies may have architectural policy to mimimize the amount of changes to the database for applications that are already in production. So adding new tables that don’t contain business data is sometimes undesired.
- It poses a security risk by allowing some users to see (parts of the) source model of the application. This information could give attackers insight in weaknesses that your application might contain.
- You need to sync the model reflection when you changed something in your model. In every environment (DTAP)… If you forget this on any environment, it can break important functionality, e.g. the ExcelImporter will throw ugly error messages at the user if the model reflection is not synced properly.
In this post I will show a way to get meta-information of your application without using the MxModelReflection module. This will give developers more flexibility and more control over the information shown in the user interface. Also it doesn’t add any tables to the database!
I will create the following Java actions to get the information we need.
I use the following domain model. Note that the entities are non-persistent, so no database tables will be created:
The first Java Action GetAllEntityNames takes no parameters, and returns a list of Entity objects.
public class GetAllEntityNames extends UserAction<java.util.List<IMendixObject>> { public GetAllEntityNames() { super(); } @Override public java.util.List<IMendixObject> executeAction() throws Exception { // BEGIN USER CODE ArrayList<IMendixObject> result = new ArrayList<IMendixObject>(); for(IMetaObject metaObject : Core.getMetaObjects()) { IMendixObject entity = Core.instantiate(getContext(), Entity.getType()); entity.setValue(getContext(), Entity.MemberNames.CompleteName.toString(), metaObject.getName()); result.add(entity); } return result; // END USER CODE }
Note the use of Core.getMetaObjects(). This part of the Mendix Java API will result in a list of all the entities in the application model. Each entity is represented by an object of the IMetaObject interface. See the Mendix Apidocs for more info.
Use this Java action in a datasource microflow for selecting a single entity, like this:
Of course, you can customize this all you want: maybe you only want to have the entity names of a certain module: then you could just filter the list in the microflow (or change the Java code).
Now, if the user has selected an entity, you might want go one level deeper: the members. Members of an entity consist of primitive members (attributes) and assocations (references and reference sets). To get the associations that a certain entity has, I created this Java action GetAssociationsOfEntity, which takes 1 string parameter (entityName), and returns a list of Assocation objects:
public class GetAssociationsOfEntity extends UserAction<java.util.List<IMendixObject>> { private String entityName; public GetAssociationsOfEntity(String entityName) { super(); this.entityName = entityName; } @Override public java.util.List<IMendixObject> executeAction() throws Exception { // BEGIN USER CODE IMetaObject metaObject = Core.getMetaObject(entityName); if (metaObject==null) return null; ArrayList<IMendixObject> result = new ArrayList<IMendixObject>(); for(IMetaAssociation metaAssoc : metaObject.getMetaAssociationsParent()) { IMendixObject assoc = Core.instantiate(getContext(), Association.getType()); assoc.setValue(getContext(), Association.MemberNames.CompleteName.toString(), metaAssoc.getName()); assoc.setValue(getContext(), Association.MemberNames.TargetEntityName.toString(), metaAssoc.getChild().getName()); result.add(assoc); } return result; // END USER CODE
Note the call to Core.getMetaObject(entityName) to get the IMetaObject for the given entity. By calling getMetaAssociationsParent() on the IMetaObject, we get a list of assocations owned by that entity.
Get the members is very similar to the associations. Calling getMetaPrimitives() on the IMetaObject will get a list of its attributes. This Java action GetAttributesOfEntity will take 1 string parameter (entityName), and will return a list of Attribute objects:
public class GetAttributesOfEntity extends UserAction<java.util.List<IMendixObject>> { private String entityName; public GetAttributesOfEntity(String entityName) { super(); this.entityName = entityName; } @Override public java.util.List<IMendixObject> executeAction() throws Exception { // BEGIN USER CODE IMetaObject metaObject = Core.getMetaObject(entityName); if (metaObject==null) return null; ArrayList<IMendixObject> result = new ArrayList<IMendixObject>(); for(IMetaPrimitive metaPrim : metaObject.getMetaPrimitives()) { IMendixObject attribute = Core.instantiate(getContext(), Attribute.getType()); attribute.setValue(getContext(), Attribute.MemberNames.AttributeName.toString(), metaPrim.getName()); attribute.setValue(getContext(), Attribute.MemberNames.AttributeType.toString(), metaPrim.getType().toString()); result.add(attribute); } return result; // END USER CODE
By using these Java actions, you can build your own re-usable modules without having to depend on MxModelReflection!
If you have any questions, remarks or suggestions, feel free to contact me or leave a reply below.
Leave a Reply