Attachment Class and Row.AddAttachment() method

265
2
Jump to solution
02-20-2024 03:13 AM
Asimov
by
New Contributor III

For my workflow I collect feature attributes in a custom pro window and update the feature with an edit operation when the user confirms the edits. Now I need to manage feature attachments too, and I would like to take advantage of the AddAttachment() method (and the other CRUD methods about attachments) exposed by the Row object.

The reason why it would be convenient to use this method instead of the EditOperation.AddAttachment() method is that I collect the attachments inside the ProWindow and I keep track of them in a model where I store the MemoryStream data of the attachments (that is the format I can read from the feature itself via the Attachment.GetData() method). By doing so, when the user "submits" the form, I already have got all my data binded to the model, and I don't have to deal with storing file paths and handle issues due to a file been removed in the meantime and such.

Now, calling the aformentioned Row.AddAttachment() method throws a "Objects in this object class cannot be updated outside of an edit session" exception, and I can understand that, but my question is: how can I use such method within an edit session? In the documentation nothing is told about edit sessions and the given code examples just build the Attachment object and the pass it to the AddAttachment method, as if it is supposed to work:

 

public async Task AddingAttachments()
{
  await ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(() =>
  {
    using (Geodatabase geodatabase = new Geodatabase(new DatabaseConnectionFile(new Uri("path\\to\\sde\\file\\sdefile.sde"))))
    using (FeatureClass parkFeatureClass = geodatabase.OpenDataset<FeatureClass>("LocalGovernment.GDB.Park"))
    {
      QueryFilter filter = new QueryFilter { WhereClause = "NUMPARKING > 0" };

      using (RowCursor parkingCursor = parkFeatureClass.Search(filter, false))
      {
        while (parkingCursor.MoveNext())
        {
          using (MemoryStream stream = CreateMemoryStreamFromContentsOf("Sample.xml"))
          {
            Attachment attachment = new Attachment("Sample.xml", "text/xml", stream);

            using (Row row = parkingCursor.Current)
            {
              long attachmentId = row.AddAttachment(attachment);
            }
          }
        }
      }
    }
  });
}

private MemoryStream CreateMemoryStreamFromContentsOf(String fileNameWithPath)
{
  MemoryStream memoryStream = new MemoryStream();

  using (FileStream file = new FileStream(fileNameWithPath, FileMode.Open, FileAccess.Read))
  {
    byte[] bytes = new byte[file.Length];
    file.Read(bytes, 0, (int)file.Length);
    memoryStream.Write(bytes, 0, (int)file.Length);
  }

  return memoryStream;
}

 

in which context this code is allowed? How can I link this with my EditOperation? Since the only way to add an attachment using the EditOperation.AddAttachment() method is to pass the file's path to it, I would need to either store new attachments paths (and handling connected issues like mentioned above) or save the MemoryStream I have in the model to a file "on the fly" (again, handling all the issues connected with that).

Is there a way to pass a MemoryStream to the EditOperation.AddAttachment() or use the Row.AddAttachment() method linking it to an EditOperation?

0 Kudos
1 Solution

Accepted Solutions
Aashis
by Esri Contributor
Esri Contributor

Hi @Asimov , You should wrap the editing code inside the apply edits. For more details please refer to the conceptual doc.

 

geodatabase.ApplyEdits(() =>{
	using (RowCursor parkingCursor = parkFeatureClass.Search(filter, false))
		  {
			while (parkingCursor.MoveNext())
			{
			  using (MemoryStream stream = CreateMemoryStreamFromContentsOf("Sample.xml"))
			  {
				Attachment attachment = new Attachment("Sample.xml", "text/xml", stream);

				using (Row row = parkingCursor.Current)
				{
				  long attachmentId = row.AddAttachment(attachment);
				}
			  }
			}
		  }
});

 

View solution in original post

0 Kudos
2 Replies
Aashis
by Esri Contributor
Esri Contributor

Hi @Asimov , You should wrap the editing code inside the apply edits. For more details please refer to the conceptual doc.

 

geodatabase.ApplyEdits(() =>{
	using (RowCursor parkingCursor = parkFeatureClass.Search(filter, false))
		  {
			while (parkingCursor.MoveNext())
			{
			  using (MemoryStream stream = CreateMemoryStreamFromContentsOf("Sample.xml"))
			  {
				Attachment attachment = new Attachment("Sample.xml", "text/xml", stream);

				using (Row row = parkingCursor.Current)
				{
				  long attachmentId = row.AddAttachment(attachment);
				}
			  }
			}
		  }
});

 

0 Kudos
Asimov
by
New Contributor III

Thanks @Aashis, it makes sense. Unfortunately this approach is not suitable for my workflow because I need to include attachment management in the same ongoing EditOperation I have, where I take care of the attributes. I also need to keep everything wrapped inside a single item on the undo stack. 

I managed to implement what I need using the EditOperation.AddAttachment()/RemoveAttachment() methods, saving MemoryStreams on the fly on disk and taking care of the cleaning after the operation completes: it seems to work well and it keeps everything on the same Edit Operation as I needed.

0 Kudos