Using RA how do I see if a folder exist, if not create it
Question
Question
Answer
Bert's example will work, but it has a few code patterns that should be avoided:
- Depending on Exceptions for expected behavior. The Try-Catch around Folder.GetFolderInfo is not the best way to check if an entry exists. Exceptions are expensive to throw, so if you aren't sure that the item should exist, and you don't require that it exists, you should test for its existence without an Exception. In this case, the best way to do this is to use Entry.TryGetEntryInfo and check for a null return value instead of Folder.GetFolderInfo and expect an exception.
- Using recursion to create the parent folders. This works, but function calls are not free, and in edge cases you can get problems like stack overflow. Instead, you can unwind the recursion with a stack.
- Returning -1 for errors instead of throwing an Exception. You can't tell what went wrong when you return -1, and if later you decide that getting -1 should merit throwing an exception you won't get the full stack trace on your problem. When a method encounters a problem that it cannot recover from, it should throw an exception. If you want to stick with this design pattern, you should name the method "TryGetFolderID" to make it clear that you won't get Exceptions.
- Returning an int instead of a FolderInfo means that there is time in between creating the folder and acquiring a lock on it, which can lead to problems in some cases, and is harder on the server (see Michael Allen's repsonse here).
Again, Cliff's suggestion looks like it will work. That said, here is a rework of the method, addressing some of the above issues in C#:
private static FolderInfo CreateHelper(string path, Session s) { EntryInfo entry = null; Stack<string> toCreate = new Stack<string>(); if (!path.StartsWith("\\")) path = "\\" + path; // This is equivalent to the recursion in the other // solution, but with an explicit stack instead of relying // on the call stack for state. while (entry == null) { toCreate.Push(path.Split('\\').Last()); path = path.Substring(0, path.LastIndexOf('\\')); if (path == "") path = "\\"; entry = Entry.TryGetEntryInfo(path, s); } FolderInfo folder = entry as FolderInfo; if (folder == null) { throw new DuplicateObjectException( "A parent of the requested folder is a non-folder entry."); } // Walk back up the stack as if returning from recursive // calls. FolderInfo parent = null; try { while (toCreate.Count > 0) { if (parent != null) parent.Dispose(); parent = folder; folder = new FolderInfo(s); path = toCreate.Pop(); folder.Create(parent, path, EntryNameOption.None); } } catch (Exception) { // Guarantee that folder does not leak if an Exception // is thrown. if (folder != null) folder.Dispose(); throw; } finally { // Guarantee that parent does not leak. if (parent != null) parent.Dispose(); } return folder; } public static FolderInfo GetOrCreateFolderInfo(string path, Session s) { if (path == null) throw new ArgumentNullException("Path is null."); if (s == null) throw new ArgumentNullException("Session is null."); if (path == "") throw new ArgumentException("Path in an empty string."); // Will return null instead of throwing an Exception if the // entry does not exist. EntryInfo entry = Entry.TryGetEntryInfo(path, s); if (entry != null) { FolderInfo folder = entry as FolderInfo; if (folder != null) { return folder; } else { throw new DuplicateObjectException("Object already exists"); } } else { return CreateHelper(path, s); } }
To use it, call GetOrCreateFolderInfo with a path and a Session argument. It will make a best effort to create the folder. Note that this will throw Exceptions in cases similar to those when Folder.Create will throw Exceptions; invalid arguments, non-folder object already at the location that it is trying to put a folder. Also, the returned FolderInfo object must be disposed in order to avoid leaks!
The logic is almost identical to Bert's, but it unrolls the recursion, uses a different method to check for existence of the folder, and uses a different method to create the FolderInfo object. Its not perfectly optimized, and the string manipulations especially could use some work for clarity, but it appears to work in the limited test cases that I've tried it in.
EDIT 4/29/14: Added some comments to the code, fixed one or two wording issues in the text. Updated the helper method to fix a possible leak.
This is a very useful block of code, thank you!
However I do think there is a FolderInfo lock leak in CreateHelper(); I had LF Admin open, and viewed Entry Locks, and I noticed that the leaf folder remained locked, and the locks accumulated for each new leaf folder created.
To resolve this, I modifed the bottom half of CreateHelper() as follows:
// Walk back up the stack as if returning from recursive // calls. FolderInfo parent = null; FolderInfo child = null; // Added try { while (toCreate.Count > 0) { if (parent != null) parent.Dispose(); parent = folder; folder = new FolderInfo(s); child = folder; // Added path = toCreate.Pop(); folder.Create(parent, path, EntryNameOption.None); } } catch (Exception) { // Guarantee that folder does not leak if an Exception // is thrown. if (folder != null) folder.Dispose(); throw; } finally { // Guarantee that parent does not leak. if (parent != null) parent.Dispose(); if (child != null) // Added child.Dispose(); }
Replies
here is a function I use:
Private Function GetFolderID(ByVal sFolderPath As String, ByVal bCreateMissing As Boolean) As Integer Dim iReturn As Integer = -1 Try ' Only try if session object is not null If mySession IsNot Nothing Then 'Try to get folder by path Try Dim myFolder As FolderInfo = Folder.GetFolderInfo(sFolderPath, mySession) ' Save folder ID iReturn = myFolder.Id ' Release folder myFolder.Dispose() ' Return Folder ID Return iReturn Catch 'Folder not found, but do nothing here End Try ' Only create missing folder if bCreateMissing is True If bCreateMissing Then ' Get Parent path Dim sParentPath As String = sFolderPath.Substring(0, sFolderPath.LastIndexOf("\")) ' Get folder name Dim sFolderName As String = sFolderPath.Substring(sFolderPath.LastIndexOf("\") + 1) ' Get Parent ID Dim iParentID As Integer = GetFolderID(sParentPath, bCreateMissing) Try ' Get Parent folder object Dim ParentFolder As FolderInfo = Folder.GetFolderInfo(iParentID, mySession) ' Create missing folder iReturn = Folder.Create(ParentFolder, sFolderName, EntryNameOption.AutoRename, mySession) ' Release parent folder ParentFolder.Dispose() Catch ex As Exception ' Log error here ' Make sure return value is -1 iReturn = -1 End Try End If End If Catch ex As Exception ' Log error here ' Make sure return value is -1 iReturn = -1 End Try ' Return Folder ID Return iReturn End Function
Requirements of this function:
- Path does not include Repository name and starts with "\"
- Path does not end with "\"
- mySession is a valid Session object with a valid connection to the repository with rights to create folders in path if missing
This function can be used like this:
' Get Folder ID Dim iFolderID As Integer = GetFolderID("\FolderPath", True) ' must be greater then -1 to be a folder ID If iFolderID > -1 Then 'Folder exists MessageBox.Show("Folder ID: " & iFolderID.ToString) End If
Bert,
Seeing your code helped get my program to work. I needed to see the Dim myFolder As FolderInfo = Folder.GetFolderInfo(sFolderPath, mySession) once I had that I was able to complete the rest of my code.
Thank you for your help!
Gary Schreader