diff --git a/README.md b/README.md
index 50c0e113322aa09bcc636e6db0aadb4d7ba1a4db..a0ca3cd0f2e36d028bb6075401184c149ba94d44 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,5 @@
-## Tree
+# Tree API
 
 The TreeApi handles the retrieving or storing metadata to a certain path.
   
 Check out the [docs](https://coscine.rwth-aachen.de/coscine/api/Coscine.Api.Tree/swagger). 
- 
diff --git a/src/Tree/Controllers/TreeController.cs b/src/Tree/Controllers/TreeController.cs
index b01e84cf1e9e34f19183d0b032835841b8882429..511933da1f2acd52374acd2f17826bbfed61f4ad 100644
--- a/src/Tree/Controllers/TreeController.cs
+++ b/src/Tree/Controllers/TreeController.cs
@@ -1,4 +1,3 @@
-using Coscine.Api.Tree.Util;
 using Coscine.ApiCommons;
 using Coscine.ApiCommons.Factories;
 using Coscine.Configuration;
@@ -36,6 +35,7 @@ namespace Coscine.Api.Tree.Controllers
         private readonly Authenticator _authenticator;
         private readonly ResourceModel _resourceModel;
         private readonly RdfStoreConnector _rdfStoreConnector;
+        private readonly CoscineLDPHelper _coscineLDPHelper;
         private readonly ProjectRoleModel _projectRoleModel;
         private readonly ProjectResourceModel _projectResourceModel;
         private readonly CoscineLogger _coscineLogger;
@@ -64,22 +64,7 @@ namespace Coscine.Api.Tree.Controllers
             var path = rule[rule.IndexOf("/")..];
             _blobApiLink = $"https://{host}{path}/blob/";
             _prefix = _configuration.GetStringAndWait("coscine/global/epic/prefix");
-        }
-
-        /// <summary>
-        /// Generates Id
-        /// </summary>
-        /// <param name="resourceId">Id of the resource</param>
-        /// <param name="path"> Path to file</param>
-        /// <returns> Uri </returns>
-        public Uri GenerateId(string resourceId, string path)
-        {
-            if (!path.StartsWith("/"))
-            {
-                path = "/" + path;
-            }
-
-            return new CustomUri($"https://hdl.handle.net/{_prefix}/{resourceId}@path={Uri.EscapeDataString(path)}");
+            _coscineLDPHelper = new CoscineLDPHelper(_rdfStoreConnector, _prefix);
         }
 
         /// <summary>
@@ -159,10 +144,12 @@ namespace Coscine.Api.Tree.Controllers
                 var resourceTypeInformation = resourceTypeDefinition.GetResourceTypeInformation().Result;
                 var metadataInfos = new List<ResourceEntry>(fileInfos);
 
+                var metadataIds = _rdfStoreConnector.ListMetadata(resourceId);
+
                 // Add to metadata infos, if no "physical" file for it exists
                 if (!metadataInfos.Any((metadataInfo) => metadataInfo.Key == path))
                 {
-                    metadataInfos.Add(new ResourceEntry(path, true, 0, GenerateId(resourceId, path).AbsoluteUri, null, DateTime.Now, DateTime.Now));
+                    metadataInfos.Add(new ResourceEntry(path, true, 0, _coscineLDPHelper.GetId(resourceId, path, true, false, "metadata", metadataIds).AbsoluteUri, null, DateTime.Now, DateTime.Now));
                 }
 
                 var applicationProfileGraph = _rdfStoreConnector.GetGraph(resource.ApplicationProfile);
@@ -173,7 +160,7 @@ namespace Coscine.Api.Tree.Controllers
                 int metadataCount = 0;
                 foreach (var info in metadataInfos)
                 {
-                    var id = GenerateId(resourceId, info.Key);
+                    var id = _coscineLDPHelper.GetId(resourceId, info.Key, true, false, "metadata", metadataIds);
                     if (_rdfStoreConnector.HasGraph(id.AbsoluteUri))
                     {
                         var graph = _rdfStoreConnector.GetGraph(id);
@@ -297,9 +284,9 @@ namespace Coscine.Api.Tree.Controllers
         /// <returns>If OK status code 204, otherwise status code 400 or 401</returns>
         [HttpPut("[controller]/{resourceId}/{*path}")]
         [ApiExplorerSettings(IgnoreApi = true)]
-        public IActionResult StoreMetadataForFileWithPath(string resourceId, string path)
+        public async Task<IActionResult> StoreMetadataForFileWithPath(string resourceId, string path)
         {
-            return StoreMetadataForFile(resourceId, path);
+            return await StoreMetadataForFile(resourceId, path);
         }
 
         /// <summary>
@@ -310,7 +297,7 @@ namespace Coscine.Api.Tree.Controllers
         /// <param name="mimeType">Requested MimeType of the metadata</param>
         /// <returns>If OK status code 204, otherwise status code 400 or 401</returns>
         [HttpPut("[controller]/{resourceId}/")]
-        public IActionResult StoreMetadataForFileWithParameter(string resourceId, [FromQuery] string path = "", [FromQuery] string mimeType = "application/rdf+json")
+        public async Task<IActionResult> StoreMetadataForFileWithParameter(string resourceId, [FromQuery] string path = "", [FromQuery] string mimeType = "application/rdf+json")
         {
             // Strip the first slash, to reuse the previous implementation.
             if (path.StartsWith("/"))
@@ -318,7 +305,7 @@ namespace Coscine.Api.Tree.Controllers
                 path = path[1..];
             }
 
-            return StoreMetadataForFile(resourceId, path, mimeType);
+            return await StoreMetadataForFile(resourceId, path, mimeType);
         }
 
         /// <summary>
@@ -328,7 +315,7 @@ namespace Coscine.Api.Tree.Controllers
         /// <param name="path">Path to the file</param>
         /// <param name="mimeType">Requested MimeType of the metadata</param>
         /// <returns>If OK status code 204, otherwise status code 400 or 401</returns>
-        public IActionResult StoreMetadataForFile(string resourceId, string path, string mimeType = "application/rdf+json")
+        public async Task<IActionResult> StoreMetadataForFile(string resourceId, string path, string mimeType = "application/rdf+json")
         {
             path = $"/{path}";
             if (path.Contains("%2F") || path.Contains("%2f"))
@@ -339,7 +326,7 @@ namespace Coscine.Api.Tree.Controllers
             // Ducktape solution for supporting multiple mimetypes
             var metadataObject = ObjectFactory<JToken>.DeserializeFromStream(Request.Body);
 
-            var graphNameUri = GenerateId(resourceId, path);
+            var graphNameUri = _coscineLDPHelper.GetId(resourceId, path, false, true);
 
             JObject json;
 
@@ -443,38 +430,15 @@ namespace Coscine.Api.Tree.Controllers
                 return BadRequest("Data has the wrong format!");
             }
 
-            // store the data
-            if (_rdfStoreConnector.HasGraph(graphNameUri))
-            {
-                _rdfStoreConnector.ClearGraph(graphNameUri);
-
-                if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics))
-                {
-                    LogAnalyticsUpdateMd(_projectResourceModel.GetProjectForResource(resource.Id).Value, resource.Id, path, user, GetMetadataCompleteness(graph.Triples.Count, resource));
-                }
-            }
-            else
+            if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics))
             {
-                try
-                {
-                    _rdfStoreConnector.CreateNamedGraph(graphNameUri);
-                }
-#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception.
-                catch (Exception)
-#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception.
-                {
-                    // Graph creation failed because it has been created before, skip this for now
-                }
-
-                if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics))
-                {
-                    LogAnalyticsUploadMd(_projectResourceModel.GetProjectForResource(resource.Id).Value, resource.Id, path, user, GetMetadataCompleteness(graph.Triples.Count, resource));
-                }
+                LogAnalyticsUploadMd(_projectResourceModel.GetProjectForResource(resource.Id).Value, resource.Id, path, user, GetMetadataCompleteness(graph.Triples.Count, resource));
             }
 
             // BaseUri must be set for the sparql query
             graph.BaseUri = graphNameUri;
-            _rdfStoreConnector.AddGraph(graph);
+
+            await _rdfStoreConnector.AddMetadataAsync(graph);
 
             return NoContent();
         }
diff --git a/src/Tree/Util/CustomUri.cs b/src/Tree/Util/CustomUri.cs
deleted file mode 100644
index 2eaa9416949fc9e94eab1b9db666599b7c140a98..0000000000000000000000000000000000000000
--- a/src/Tree/Util/CustomUri.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-
-namespace Coscine.Api.Tree.Util
-{
-    /// <summary>
-    /// Adapts the returned ToString of a Uri
-    /// </summary>
-    public class CustomUri : Uri
-    {
-        /// <summary>
-        /// Constructs a Custom Uri
-        /// </summary>
-        /// <param name="uri"></param>
-        public CustomUri(string uri) : base(uri)
-        {
-
-        }
-
-        /// <summary>
-        /// Overwrites the ToString for returning the AbsoluteUri (for dotNetRDF)
-        /// </summary>
-        /// <returns></returns>
-        public override string ToString()
-        {
-            return AbsoluteUri;
-        }
-    }
-}