Search code examples
c#imagesharp

Object disposed exception after passing Image as paramter


I'm trying get Image(ImageSharp) from method, then pass it as parameter to another method and sent it by email, but I'm getting Object Disposed Exception.

Here is how my code looks like:

public async Task<Unit> Handle( CancellationToken cancellationToken)
    {
        ...
        
        var ticketImage = await ticket.GetTicketWithQRCode(cancellationToken);

        _emailService.Send(ticketImage);
        
        return new Unit();
    }
    
async public Task<Image> GetTicketWithQRCode(CancellationToken cancellationToken)
    {

        var qrCode = QrCode.EncodeText($@"
            {{
            Id:""{this.TickedGuid}"",
            UserData:""{this.TickedUserData.Name} {this.TickedUserData.Surname}""
            }}
            ", QrCode.Ecc.Quartile);

        using var imageBase = await Image.LoadAsync<Rgba32>($"/var/opt/clubapp/images/{this.Ticket.Image.ImageSrc}", cancellationToken);

        using var imageWithQRCode = new Image<Rgba32>(imageBase.Width, imageBase.Height);

        imageWithQRCode.CombineWithBaseImageAndQrCode(imageBase, qrCode, new Size() { Width = 390, Height = 495 }, 250);

        await imageWithQRCode.ResizeToHasMaximumFileSize(1.5, cancellationToken);

        var imageRatio = imageWithQRCode.Width / imageWithQRCode.Height;
        imageWithQRCode.Mutate(x => x.Resize(new ResizeOptions() { Mode = ResizeMode.Max, Size = new Size() { Height = 100, Width = 100 * imageRatio } }));

        return imageWithQRCode;
    }
    
public string Send(Image image)
    {
        var message = new MimeMessage();

        [...]
    
        var body = new TextPart("Html")
        {
            Text = @"test"
        };

        using var memoryStream = new MemoryStream();
        image.SaveAsPng(memoryStream)  <<------------exception on this line

        var attachment = new MimePart("image", "png")
        {
            Content = new MimeContent(memoryStream),
            ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
            ContentTransferEncoding = ContentEncoding.Base64,
        };
        
        ...
    }

and this is log:

Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HMLNLCMVSL21", Request id "0HMLNLCMVSL21:00000002": An unhandled exception was thrown by the application.
      System.ObjectDisposedException: Cannot access a disposed object.
      Object name: 'Image`1'.
         at SixLabors.ImageSharp.Image.ThrowObjectDisposedException(Type type)
         at SixLabors.ImageSharp.Image.EnsureNotDisposed()
         at SixLabors.ImageSharp.Image.Save(Stream stream, IImageEncoder encoder)
         at SixLabors.ImageSharp.ImageExtensions.SaveAsPng(Image source, Stream stream, PngEncoder encoder)
         at SixLabors.ImageSharp.ImageExtensions.SaveAsPng(Image source, Stream stream)
         at YaClubApp.Infrastructure.Services.EmailService.Send(Image image) in /src/src/Infrastructure/Services/EmailService.cs:line 73

Should I change this methods and use memoryStream on top (in Handle) and then pass it to GetTicketWithQRCode and after that to Send?

Or what is a proper way to do that?


Solution

  • Your line

    using var imageWithQRCode = new Image<Rgba32>(imageBase.Width, imageBase.Height);
    

    is disposing of your result image at the end of your method... if you want a caller to use the image object after this method completes remove the using from that line... don't forget that its now the callsite that is responsible for calling dispose.

    so basically remove the using from the above line and instead add it infront of

    var ticketImage = await ticket.GetTicketWithQRCode(cancellationToken);
    

    as thats the place that now need to own the lifetime of the image.