📜 ⬆️ ⬇️

ASP.NET MVC: Binding data to models that contain images

Data binding (binding) is a fairly convenient means of ASP.NET MVC. It is convenient, first of all, in that it allows you to hide the implementation of data conversion between data from the model and HTTP request data.
In my projects I often encounter the need to save various data like blob and image. In this article, I would like to show how you can easily organize and use data binding from a model that contain different images. For example, I took the MVC Music Store training project and decided to correct it - to add the ability to change the image of the cover of a music album. When writing this article, I used the APS.NET version of MVC 3 and Razor.


Implement binding


In general, data binding is designed to load and save model data. We implement a class that customizes the default data binding.
public class ImageModelBinder : DefaultModelBinder { private string _fieldName; public ImageModelBinder(string fieldName) { _fieldName = fieldName; } public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var obj = base.BindModel(controllerContext, bindingContext); ValueProviderResult valueResult; valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + "." + _fieldName); if (valueResult == null) { valueResult = bindingContext.ValueProvider.GetValue(_fieldName); } if (valueResult != null) { HttpPostedFileBase file = (HttpPostedFileBase)valueResult.ConvertTo(typeof(HttpPostedFileBase)); if (file != null) { byte[] tempImage = new byte[file.ContentLength]; file.InputStream.Read(tempImage, 0, file.ContentLength); PropertyInfo imagePoperty = bindingContext.ModelType.GetProperty(_fieldName); imagePoperty.SetValue(obj, tempImage, null); } } return obj; } } 

In this implementation, we redefined the main BindModel method, first called the base implementation to bind all data, and then implemented the conversion and writing to the image data field from an HTTP request form field of the HttpPostedFileBase type.
Also, it would be nice to create your own attribute:
 public class ImageBindAttribute : CustomModelBinderAttribute { private IModelBinder _binder; public ImageBindAttribute(string fieldName) { _binder = new ImageModelBinder(fieldName); } public override IModelBinder GetBinder() { return _binder; } } 


To create a view, we also need a simple file-loading field helper:
 public static IHtmlString ImageUpload(this HtmlHelper helper, string name) { return ImageUpload(helper, name, null); } public static IHtmlString ImageUpload(this HtmlHelper helper, string name, object htmlAttributes) { var tagBuilder = new TagBuilder("input"); tagBuilder.GenerateId(name); UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext); tagBuilder.Attributes["name"] = name; tagBuilder.Attributes["type"] = "file"; tagBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); return MvcHtmlString.Create(tagBuilder.ToString()); } 

')

Using


Now we modify the main project.
In our project there is a model - class Album. Add a new Image field of type byte []. Also exclude this new field from the standard binding.
 [Bind(Exclude = "AlbumId, Image")] public class Album { ... public string AlbumArtUrl { get; set; } [ScaffoldColumn(false)] public byte[] Image { get; set; } ... } 

And do not forget to add a new field to the database.

Next, we need to somehow declare our binding. This can be done by adding an attribute to the appropriate Action, or by adding a data binding globally.
 public ActionResult Create([ImageBind("Image")] Album album) { if (ModelState.IsValid) { storeDb.Albums.Add(album); 


Unfortunately, this is not suitable for the Edit method, where the FormCollection type is used.
in arguments. Then you can simply add a new binding to the collection, for example:
  public ActionResult Edit(int id, FormCollection collection) { var album = storeDb.Albums.Find(id); if (Binders[typeof(Album)] == null) Binders.Add(typeof(Album), new ImageModelBinder("Image")); ... 


Or do it globally, in the Global.asax file:
 ModelBinders.Binders.Add(typeof(Album), new ImageModelBinder("Image")); 


To load images, modify the view and add a field to the form:
 <div class="editor-field"> @Html.ImageUpload("Image") </div> 

and change the call to the form helper by adding the new HTML attribute enctype = “multipart / form-data” to enable applauding of binary data:
 (Html.BeginForm("Edit", "StoreManager", FormMethod.Post, new { enctype = "multipart/form-data" })) 

That's about all about loading and saving.

To display, we need to create a new Action.
 [OutputCache(Duration = 0)] public ActionResult Image(int id) { var album = storeDb.Albums.Find(id); return new FileStreamResult(new MemoryStream(album.Image), "image/png"); } 

In this example, we restrict ourselves to using the image in the png format. You also need to take care of caching by specifying the corresponding attribute.

To create a view, we need a simple helper:
 public static IHtmlString Image(this HtmlHelper helper, string name, string id) { return Image(helper, name, id, null); } public static IHtmlString Image(this HtmlHelper helper, string name, string id, object htmlAttributes) { var tagBuilder = new TagBuilder("img"); UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext); tagBuilder.Attributes["src"] = urlHelper.Action(name, null, new { id = id }); tagBuilder.Attributes["alt"] = string.Format("{0} of {1}", name, id); tagBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); return MvcHtmlString.Create(tagBuilder.ToString()); } 


And in the form we add.
 <div class="field"> @Html.Image("Image", @Model.AlbumId.ToString()) </div> 


Conclusion


In this example, I wanted to show how you can quickly and easily organize data binding in MVC. As you can see, as a result of using the binding, the implementation code of the view, model and controller is quite simple and concise. And the most important thing is that once we have implemented the binding, we can use it anywhere else.

What can be improved.
Add a BeginForm helper is an abbreviated version in order not to write controller and action names. The implementation of Action for display is rather primitive. In an amicable way, you need to save the image type and return it correctly and implement the validation of the banary data. And instead of the helpers that I cited in this article, a more correct method is to use the editing and viewing templates. You can also use such a powerful tool as filters.

Sources


mvcmusicstore.codeplex.com
www.highoncoding.com/Articles/689_Uploading_and_Displaying_Files_Using_ASP_NET_MVC_Framework.aspx
www.hanselman.com/blog/SplittingDateTimeUnitTestingASPNETMVCCustomModelBinders.aspx
odetocode.com/Blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx
odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx

Source: https://habr.com/ru/post/113964/


All Articles