Ecore Metamodeling

Metamodel Relationships

Learn how to create relationships between classes using EReference, including containment, cardinality, and bidirectional references.

In this tutorial, you’ll extend the Company metamodel to create relationships between Company and Person, exploring the powerful features of Ecore references.

30 mins Estimated Time

Section 1

Understanding EReference

Whilst EAttribute defines properties with primitive types (strings, numbers), EReference defines relationships between classes.

References are fundamental to modelling complex domains where objects are connected to each other, such as companies having employees or projects having tasks.

EAttribute vs EReference

Step 1

Start with the Company metamodel from previous tutorials.

This metamodel has two independent classes. We’ll add relationships to connect them.

Company.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0"
3 xmlns:xmi="http://www.omg.org/XMI"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
6 name="company"
7 nsURI="http://www.example.org/company"
8 nsPrefix="company">
9
10 <eClassifiers xsi:type="ecore:EClass" name="Person">
11 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
12 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
13 </eClassifiers>
14
15 <eClassifiers xsi:type="ecore:EClass" name="Company">
16 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
17 </eClassifiers>
18
19</ecore:EPackage>

Step 2

Add an EReference from Company to Person for employees.

The employees reference has upperBound="-1" meaning unlimited (zero or more). The eType="#//Person" points to the Person class in this package.

Company.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0"
3 xmlns:xmi="http://www.omg.org/XMI"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
6 name="company"
7 nsURI="http://www.example.org/company"
8 nsPrefix="company">
9
10 <eClassifiers xsi:type="ecore:EClass" name="Person">
11 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
12 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
13 </eClassifiers>
14
15 <eClassifiers xsi:type="ecore:EClass" name="Company">
16 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
17 <eStructuralFeatures xsi:type="ecore:EReference" name="employees" upperBound="-1" eType="#//Person"/>
18 </eClassifiers>
19
20</ecore:EPackage>

Section 2

Containment References

Containment references define ownership—when a Company contains Persons, those Persons belong to the Company and are saved inside it in XMI.

Containment is crucial for defining the structure and scope of your models. Contained objects are owned by their container and typically cannot exist independently.

Step 1

Mark the employees reference as containment.

Adding containment="true" makes this a containment reference. Now Person instances will be nested inside Company in XMI files, establishing clear ownership.

Company.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0"
3 xmlns:xmi="http://www.omg.org/XMI"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
6 name="company"
7 nsURI="http://www.example.org/company"
8 nsPrefix="company">
9
10 <eClassifiers xsi:type="ecore:EClass" name="Person">
11 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
12 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
13 </eClassifiers>
14
15 <eClassifiers xsi:type="ecore:EClass" name="Company">
16 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
17 <eStructuralFeatures xsi:type="ecore:EReference" name="employees" upperBound="-1" eType="#//Person" containment="true"/>
18 </eClassifiers>
19
20</ecore:EPackage>

Step 2

Create a model instance using containment.

With containment, Person elements are nested inside the Company element. The hierarchy shows ownership: these Persons belong to this Company.

company-model.xmi
1<?xml version="1.0" encoding="UTF-8"?>
2<xmi:XMI xmi:version="2.0"
3 xmlns:xmi="http://www.omg.org/XMI"
4 xmlns:company="http://www.example.org/company">
5
6 <company:Company xmi:id="company1" name="Tech Innovations Ltd">
7 <employees xmi:id="person1" name="Alice Johnson" email="alice@techinnovations.com"/>
8 <employees xmi:id="person2" name="Bob Smith" email="bob@techinnovations.com"/>
9 <employees xmi:id="person3" name="Carol Williams" email="carol@techinnovations.com"/>
10 </company:Company>
11
12</xmi:XMI>

Section 3

Bidirectional References

Bidirectional references link two classes in both directions using eOpposite. When you navigate from Company to Person via employees, you can also navigate back from Person to Company via employer.

The framework automatically maintains consistency—when you add a Person to a Company’s employees, the Person’s employer is automatically set.

Step 1

Add a bidirectional reference between Company and Person.

Person now has an employer reference pointing back to Company. The eOpposite attributes link the two references, making them bidirectional.

Company.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0"
3 xmlns:xmi="http://www.omg.org/XMI"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
6 name="company"
7 nsURI="http://www.example.org/company"
8 nsPrefix="company">
9
10 <eClassifiers xsi:type="ecore:EClass" name="Person">
11 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
12 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
13 <eStructuralFeatures xsi:type="ecore:EReference" name="employer" eType="#//Company" eOpposite="#//Company/employees"/>
14 </eClassifiers>
15
16 <eClassifiers xsi:type="ecore:EClass" name="Company">
17 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
18 <eStructuralFeatures xsi:type="ecore:EReference" name="employees" upperBound="-1" eType="#//Person" containment="true" eOpposite="#//Person/employer"/>
19 </eClassifiers>
20
21</ecore:EPackage>

Step 2

Inspect the metamodel to see the reference structure.

The --show-references flag displays all references, their types, cardinalities, and opposites, helping you verify your relationship structure.

Terminal
1swift-ecore inspect Company.ecore --detail full --show-references

Check Your Understanding

Question 1 of 4

What is the difference between EAttribute and EReference?

Question 2 of 4

What does containment="true" mean for an EReference?

Question 3 of 4

What does upperBound="-1" specify?

Question 4 of 4

What is the purpose of eOpposite in a bidirectional reference?

Advanced Metamodel Features

Learn how to use enumerations, inheritance, and abstract classes to create sophisticated metamodels.