Ticket T1279931
Visible to All Users

How to customizely handle object deletion in XAF Web Blazor application using Entity Framework?

created 5 days ago (modified 5 days ago)

Hello there,

I have a class responsible to upload, download and delete files to a cloud storage (Azure). To upload the files I can override the OnSaving() method in which I call the storageHelper Upload method to store the file in the cloud (see code below). However I can't find any method to override when the object is deleted, as I want to delete it as well from the cloud storage.

In XPO objects you have got the OnDeleting() method. Is there any similar method for EF objects? If not, do you have any suggestions for a solution?

This is the Class I'm talking about:

Code
public class FileDataReference : BaseObject, IFileData, IEmptyCheckable { private readonly AzureStorageHelper storageHelper; public virtual Guid FileId { get; set; } public virtual string? FileName { get; set; } public virtual int Size { get; set; } public virtual long SizeLong { get; set; } public virtual required string Container { get; set; } public virtual bool IsEmpty => Size == 0; string? fileToClear; byte[]? bytesToSave; public FileDataReference() { storageHelper = new AzureStorageHelper(); } public override void OnCreated() { base.OnCreated(); FileId = Guid.NewGuid(); Container = "images"; } public void Clear() { bytesToSave = null; fileToClear = FileName; FileName = null; Size = 0; SizeLong = 0; } public void LoadFromStream(string fileName, Stream stream) { using var ms = new MemoryStream(); stream.CopyTo(ms); FileName = fileName; Size = stream.Length <= long.MaxValue ? ((int)stream.Length) : int.MaxValue; SizeLong = stream.Length; bytesToSave = ms.ToArray(); fileToClear = null; } public void SaveToStream(Stream stream) { storageHelper.Download(Container, FileId, ref stream); } public override void OnSaving() { base.OnSaving(); storageHelper.Upload(Container, FileId, FileName, bytesToSave); bytesToSave = null; } public override void Deleting() ===> **This method does not exist!** { Clear(); storageHelper.Delete(Container, FileId); } }

Thank you in advance,
Kind regards,
Herman.

Answers approved by DevExpress Support

created 4 days ago

Hello Herman,

Thank you for the code snippet.

While our base classes and interfaces for EF Core business classes do not have an alternative to XPO's OnDeleting method, there are other ways to handle this usage scenario in XAF. Please refer to the following help topic for more information: Delete Objects from the Database.

If this information doesn't help or I misunderstood your usage scenario, please describe it in greater detail or create a small sample project that illustrates what you're trying to achieve.

Best regards,
Herman

    Show previous comments (3)
    Dennis Garavsky (DevExpress) 16 hours ago

      Hello, Herman.

      Thank you for your follow-up.

      Can you explain and/or give me an example how to override the handler of Delete Action of the Images ListView?
      But I'm still not successful in deleting those stored in local database.
      Also how can I identify which items have been checked for deletion?

      Please take a look at the OnSaving and RemoveOldFileFromStore methods in the https://devexpress.com/kb=e965#EFCore/FileSystemData/BusinessObjects/FileSystemStoreObject.cs file - they are pretty similar to your custom IFileData implementer (FileDataReference). We also have a generic code example here: GetObjectsToDelete(Boolean). Again, my colleague and I recommended these APIs earlier (see Delete Objects from the Database help topic).

      NOTE: these APIs do not deal with the Delete Action, selected ListView items, or anything UI-related - you just intercept a moment when objects are being committed and execute your custom logic depending on object states.

      My question is, how can I delete the metadata of the FileData of the selected Image(s), as the solution proposed by Herman above only resolves my cloud storage part?

      If you override the OnSaving method of your FileDataReference class similarly to what is shown in our examples, it should be sufficient in most cases. If you want, you can additionally override the OnSaving method of your Image and Angle classes to execute special logic for collection elements when the root object is deleted.

        Hello Dennis,

        I have followed your advice by extending my implementation based on your proposed approach, with a handler for ObjectSpace.Committing as also based on one of your suggestions

        Code
        public class AngleImageDeletionController : ObjectViewController<DetailView, Angle> { protected override void OnActivated() { base.OnActivated(); ObjectSpace.CustomDeleteObjects += ObjectSpace_CustomDeleteObjects; ObjectSpace.Committing += ObjectSpace_Committing; } private void ObjectSpace_CustomDeleteObjects(object? sender, CustomDeleteObjectsEventArgs e) { IList<Image> images = ViewCurrentObject.Images; foreach (object deletingObject in e.Objects) { if (deletingObject is Image image && images.Contains(image)) { image.FileData.DeleteFile(); } } e.Handled = true; } void ObjectSpace_Committing(object? sender, CancelEventArgs e) { if (sender is IObjectSpace os) { var objectsToDelete = os.GetObjectsToDelete(includeParent: true); foreach (var item in objectsToDelete) { if (item is Image image) { ObjectSpace.Delete(image.FileData); ===> **This Deletion doesn't work!** } } } } protected override void OnDeactivated() { ObjectSpace.CustomDeleteObjects -= ObjectSpace_CustomDeleteObjects; ObjectSpace.Committing -= ObjectSpace_Committing; base.OnDeactivated(); } }

        However the ObjectSpace.Delete(image.FileData) doesn't work. The GCRecord of the deleted FileData(s) remain 0. I don't understand why this is.

        As a temporary solution, I set the MetaData of the FileData to zero, indicating that its cloud counterpart has been deleted. I can remove the deleted FileData records from the DB later on manually by filtering not on the GCRecord but on the empty MetaData (see attachment).

        It would be nice if I only need to look at the GCRecord value. So I appreciate if you can advice me a solution. Otherwise I will leave it to this temporary solution.

        Dennis Garavsky (DevExpress) 13 hours ago

          Herman, just another reminder to avoid using CustomDeleteObjects at all (please take another moment to review my reply from March 3rd).

          The Committing event handler is OK, except for the ObjectSpace.Delete call - it is also redundant because your Image object is already within the list of deleted objects (since you have an aggregated collection in your Angle class). That means that the Image.OnSaving method will be normally called as well (along with your IsDeletedObject and other custom logic to delete FileData, as in our E965 example).

          I've not seen your latest edits to the FileDataReference and Image classes, but we can make a cursory review of a modified MainDemo.NET.EFCore app with your three classes once you have had an opportunity to review and implement our previous recommendations.

          Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

          Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.