diff --git a/ECoreNetto.Tests/Resource/GetUriFragmentTestFixture.cs b/ECoreNetto.Tests/Resource/GetUriFragmentTestFixture.cs
new file mode 100644
index 0000000..4ece0a6
--- /dev/null
+++ b/ECoreNetto.Tests/Resource/GetUriFragmentTestFixture.cs
@@ -0,0 +1,119 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// Copyright 2017-2025 Starion Group S.A.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// ------------------------------------------------------------------------------------------------
+
+namespace ECoreNetto.Tests.Resource
+{
+ using System;
+ using System.IO;
+ using System.Linq;
+
+ using ECoreNetto.Resource;
+
+ using NUnit.Framework;
+
+ ///
+ /// Suite of tests that verify is the inverse of
+ /// and is appropriately guarded (see issue #76).
+ ///
+ [TestFixture]
+ public class GetUriFragmentTestFixture
+ {
+ private ResourceSet resourceSet = null!;
+
+ private Resource resource = null!;
+
+ private EPackage rootPackage = null!;
+
+ [SetUp]
+ public void SetUp()
+ {
+ var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "recipe.ecore");
+ var uri = new Uri(Path.GetFullPath(path));
+
+ this.resourceSet = new ResourceSet();
+ this.resource = this.resourceSet.CreateResource(uri);
+ this.rootPackage = this.resource.Load(null);
+ }
+
+ [Test]
+ public void Verify_that_the_root_package_round_trips()
+ {
+ var fragment = this.resource.GetURIFragment(this.rootPackage);
+
+ Assert.That(this.resource.GetEObject(fragment), Is.SameAs(this.rootPackage));
+ }
+
+ [Test]
+ public void Verify_that_a_class_round_trips()
+ {
+ var eClass = this.rootPackage.EClassifiers.OfType().First();
+
+ var fragment = this.resource.GetURIFragment(eClass);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(fragment, Is.EqualTo(eClass.Identifier));
+ Assert.That(this.resource.GetEObject(fragment), Is.SameAs(eClass));
+ });
+ }
+
+ [Test]
+ public void Verify_that_a_structural_feature_round_trips()
+ {
+ var feature = this.rootPackage.EClassifiers
+ .OfType()
+ .SelectMany(eClass => eClass.EStructuralFeatures)
+ .First();
+
+ var fragment = this.resource.GetURIFragment(feature);
+
+ Assert.That(this.resource.GetEObject(fragment), Is.SameAs(feature));
+ }
+
+ [Test]
+ public void Verify_that_an_enum_literal_round_trips()
+ {
+ // the recipe model defines the 'Unit' enumeration with a 'PIECE' literal
+ var literal = this.resource.GetEObject("recipe.ecore#//Unit/PIECE");
+ Assert.That(literal, Is.Not.Null);
+
+ var fragment = this.resource.GetURIFragment(literal!);
+
+ Assert.That(this.resource.GetEObject(fragment), Is.SameAs(literal));
+ }
+
+ [Test]
+ public void Verify_that_a_null_object_throws()
+ {
+ Assert.That(() => this.resource.GetURIFragment(null!), Throws.ArgumentNullException);
+ }
+
+ [Test]
+ public void Verify_that_an_object_not_contained_in_the_resource_throws()
+ {
+ var eClass = this.rootPackage.EClassifiers.OfType().First();
+
+ // a different resource does not contain the object, so a fragment cannot be produced
+ var otherResource = new Resource();
+
+ Assert.That(() => otherResource.GetURIFragment(eClass), Throws.TypeOf());
+ }
+ }
+}
diff --git a/ECoreNetto/Resource/Resource.cs b/ECoreNetto/Resource/Resource.cs
index 4084d99..4bbfd74 100644
--- a/ECoreNetto/Resource/Resource.cs
+++ b/ECoreNetto/Resource/Resource.cs
@@ -201,7 +201,7 @@ public IEnumerable AllContents()
}
///
- /// Returns the URI fragment that, when passed to getEObject will return the given object.
+ /// Returns the URI fragment that, when passed to , will return the given object.
///
///
/// The object to identify
@@ -209,9 +209,36 @@ public IEnumerable AllContents()
///
/// the URI fragment for the object.
///
+ ///
+ /// The returned fragment is the name-based Ecore reference under which the object is registered in this
+ /// resource (its ), for example recipe.ecore#//Recipe for a class or
+ /// EStructuralFeature::recipe.ecore#//Recipe/ingredients for a structural feature. This is exactly the
+ /// reference string consumed by , so the round-trip
+ /// GetEObject(GetURIFragment(eObject)) returns the same instance.
+ ///
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is not contained in this resource and therefore cannot be turned
+ /// into a resolvable URI fragment.
+ ///
public string GetURIFragment(EObject eObject)
{
- throw new NotImplementedException();
+ if (eObject == null)
+ {
+ throw new ArgumentNullException(nameof(eObject));
+ }
+
+ var fragment = eObject.Identifier;
+
+ if (string.IsNullOrEmpty(fragment) || !this.Cache.TryGetValue(fragment, out var cached) || !ReferenceEquals(cached, eObject))
+ {
+ throw new InvalidOperationException(
+ $"The provided '{eObject.GetType().Name}' is not contained in this resource and cannot be turned into a URI fragment.");
+ }
+
+ return fragment;
}
///