Adding Primary and Gallery Images

To add a primary image and multiple gallery images to both Movie and Person records, need to start with a migration:

  1. using FluentMigrator;
  2. namespace MovieTutorial.Migrations.DefaultDB
  3. {
  4. [Migration(20160603205900)]
  5. public class DefaultDB_20160603_205900_PersonMovieImages : Migration
  6. {
  7. public override void Up()
  8. {
  9. Alter.Table("Person").InSchema("mov")
  10. .AddColumn("PrimaryImage").AsString(100).Nullable()
  11. .AddColumn("GalleryImages").AsString(int.MaxValue).Nullable();
  12. Alter.Table("Movie").InSchema("mov")
  13. .AddColumn("PrimaryImage").AsString(100).Nullable()
  14. .AddColumn("GalleryImages").AsString(int.MaxValue).Nullable();
  15. }
  16. public override void Down()
  17. {
  18. }
  19. }
  20. }

Then modify MovieRow.cs and PersonRow.cs:

  1. namespace MovieTutorial.MovieDB.Entities
  2. {
  3. // ...
  4. public sealed class PersonRow : Row, IIdRow, INameRow
  5. {
  6. [DisplayName("Primary Image"), Size(100),
  7. ImageUploadEditor(FilenameFormat = "Person/PrimaryImage/~")]
  8. public string PrimaryImage
  9. {
  10. get { return Fields.PrimaryImage[this]; }
  11. set { Fields.PrimaryImage[this] = value; }
  12. }
  13. [DisplayName("Gallery Images"),
  14. MultipleImageUploadEditor(FilenameFormat = "Person/GalleryImages/~")]
  15. public string GalleryImages
  16. {
  17. get { return Fields.GalleryImages[this]; }
  18. set { Fields.GalleryImages[this] = value; }
  19. }
  20. // ...
  21. public class RowFields : RowFieldsBase
  22. {
  23. // ...
  24. public readonly StringField PrimaryImage;
  25. public readonly StringField GalleryImages;
  26. // ...
  27. }
  28. }
  29. }
  1. namespace MovieTutorial.MovieDB.Entities
  2. {
  3. // ...
  4. public sealed class MovieRow : Row, IIdRow, INameRow
  5. {
  6. [DisplayName("Primary Image"), Size(100),
  7. ImageUploadEditor(FilenameFormat = "Movie/PrimaryImage/~")]
  8. public string PrimaryImage
  9. {
  10. get { return Fields.PrimaryImage[this]; }
  11. set { Fields.PrimaryImage[this] = value; }
  12. }
  13. [DisplayName("Gallery Images"),
  14. MultipleImageUploadEditor(FilenameFormat = "Movie/GalleryImages/~")]
  15. public string GalleryImages
  16. {
  17. get { return Fields.GalleryImages[this]; }
  18. set { Fields.GalleryImages[this] = value; }
  19. }
  20. // ...
  21. public class RowFields : RowFieldsBase
  22. {
  23. // ...
  24. public readonly StringField PrimaryImage;
  25. public readonly StringField GalleryImages;
  26. // ...
  27. }
  28. }
  29. }

Here we specify that these fields will be handled by ImageUploadEditor and MultipleImageUploadEditor types.

FilenameFormat specifies the naming of uploaded files. For example, Person primary image will be uploaded to a folder under App_Data/upload/Person/PrimaryImage/.

You may change upload root (App_Data/upload) to anything you like by modifying UploadSettings appSettings key in web.config.

~ at the end of FilenameFormat is a shortcut for the automatic naming scheme {1:00000}/{0:00000000}_{2}.

Here, parameter {0} is replaced with identity of the record, e.g. PersonID.

Parameter {1} is identity / 1000. This is useful to limit number of files that is stored in one directory.

Parameter {2} is a unique string like 6l55nk6v2tiyi, which is used to generate a new file name on every upload. This helps to avoid problems caused by caching on client side.

It also provides some security so file names can’t be known without having a link.

Thus, a file we upload for person primary image will be located at a path like this:

  1. > App_Data\upload\Person\PrimaryImage\00000\00000001_6l55nk6v2tiyi.jpg

You don’t have to follow this naming scheme. You can specify your own format like PersonPrimaryImage_{0}_{2}.

Next step is to add these fields to forms (MovieForm.cs and PersonForm.cs):

  1. namespace MovieTutorial.MovieDB.Forms
  2. {
  3. //...
  4. public class PersonForm
  5. {
  6. public String Firstname { get; set; }
  7. public String Lastname { get; set; }
  8. public String PrimaryImage { get; set; }
  9. public String GalleryImages { get; set; }
  10. public DateTime BirthDate { get; set; }
  11. public String BirthPlace { get; set; }
  12. public Gender Gender { get; set; }
  13. public Int32 Height { get; set; }
  14. }
  15. }
  1. namespace MovieTutorial.MovieDB.Forms
  2. {
  3. //...
  4. public class MovieForm
  5. {
  6. public String Title { get; set; }
  7. [TextAreaEditor(Rows = 3)]
  8. public String Description { get; set; }
  9. [MovieCastEditor]
  10. public List<Entities.MovieCastRow> CastList { get; set; }
  11. public String PrimaryImage { get; set; }
  12. public String GalleryImages { get; set; }
  13. [TextAreaEditor(Rows = 8)]
  14. public String Storyline { get; set; }
  15. public Int32 Year { get; set; }
  16. public DateTime ReleaseDate { get; set; }
  17. public Int32 Runtime { get; set; }
  18. public Int32 GenreId { get; set; }
  19. public MovieKind Kind { get; set; }
  20. }
  21. }

I also modified Person dialog css a bit to have more space:

  1. .s-MovieDB-PersonDialog {
  2. > .size { width: 700px; height: 600px; }
  3. .caption { width: 150px; }
  4. .s-PersonMovieGrid > .grid-container { height: 500px; }
  5. }

This is what we get now:

Person with Images

ImageUploadEditor stores file name directly in a string field, while MultipleImageUpload editor stores file names in a string field with JSON array format.

Removing Northwind and Other Samples

As i think our project has reached a good state, i’m now going to remove Northwind and other samples from MovieTutorial project.

See following how-to topic:

How To: Removing Northwind and Other Samples