I have a List<Category>
public partial class Category
{
public int CategoryId { get; set; }
public string? Name { get; set; }
public int? ImageId { get; set; }
public virtual ProductImage? Image { get;
set; }
public virtual ICollection<Product> Products { get; } = new List<Product>();
}
and I want to create from this a List<(string, Image)>
by using a single linq expression. How can I do this?
Doing it with multiple instructions would look like this:
List<Category> CategoriesWithImages = Context.Categories.Include(c => c.Image).ToList();
List<(string, Image)> values = new List<(string, Image)>();
CategoriesWithImages.ForEach(c => values.Add((c.Name, Image.FromStream(new MemoryStream(c.Image.Image)))));
Is there a better way to do this?
Edit: Unfortunately I have to use Include() method for this to load images from another table
CodePudding user response:
You could do it like this:
List<(string, Image)> values = CategoriesWithImages
.Select(c => new ValueTuple<string, Image>(c.Name, Image.FromStream(new MemoryStream(c.Image.Image))))
.ToList();
Using records is another option (requires C# 9 ):
List<ImageData> values = CategoriesWithImages
.Select(c => new ImageData(c.Name, Image.FromStream(new MemoryStream(c.Image.Image))))
.ToList();
record struct ImageData(string Name, Image Image);
Note: structy records require C# 10
CodePudding user response:
Right now Context.Categories.Include(c => c.Image)
loads the entire Categories
and probably Images
tables in memory. That's wasteful
A better way would be to retrieve only the necessary fields from the database and not use Image
. This class, like all types in the System.Drawing
namespace, is only meant for drawing on a desktop screen using GDI and uses limited OS-wide resources. That's why there's no Image.FromBytes
- the image is expected to be stored on the local disk or get loaded from the application's resources.
From the comments, it seems there are ~20 thumbnails, 15KB each, displayed in a Windows Forms application.
A LINQ query can load just the name and images as byte[]
arrays, in a dictionary or List, depending on usage:
record MyImage(string Name,byte[] Content);
...
var categoryThumbnails=await Context.Categories
.ToListAsync(new MyImage(c.Name,c.Image.Image));
or
Dictionary<string,byte[]> categoryThumbnails=await Context.Categories
.ToDictionaryAsync(c=>c.Name,c=>c.Image.Image));
EF Core will generate the necessary JOINs between Categories and the Image table and load only the Name
and Image
properties.
At this point, a good idea would be to store the images locally, eg in a temporary folder, and load them from disk with the Image-derived Bitmap class and the Bitmap(string)
constructor.