Question

I have two models : Category and Picture which refers to two tables, Categories and Pictures respectively. The Category model has a navigation property to Picture model.

Now, I created a controller using Scaffolding feature with CRUD operations for Category. Following is the code :-

public ActionResult Create()
{
    ViewBag.ParentCategoryId = new SelectList(db.Categories, "Id", "Name");
    ViewBag.PictureId = new SelectList(db.Pictures, "Id", "PictureUrl");
    return View();
}

The automatically generated controller actions uses SelectList for listing the available Picture entries in the database and passes it down to dropdownlist for selection. This is not the ideal scenario since what I want is to unable the user to upload the Picture and then the reference is added to Category model. Later, the entries are saved to Categories and Pictures table.

Was it helpful?

Solution 2

First of all, I would like to thank @NickLarsen for making me believe that my understanding is good and i can achieve the task myself.

The problem was not that too tough but since i was new to Asp.net MVC, things were bit baffling. From the very start, I had the notion that i will be needing a ViewModel merging Category and Price classes and then a picture uploading API. But, somehow I wasn't able to fit the pieces in right place. Therefore after various regression and research over the internet, I achieved the task in following manner :-

  • First of all, I created a ViewModel

    public class CatPicView
    {
        public Category Category { get; set; }
        public Picture Picture { get; set; }
    }
    
  • Second, I added the Uploadify javascript API

    <script type="text/javascript">
        $('#file_upload').uploadify({
            'uploader': '@Url.Content("~/uploadify/uploadify.swf")',
            'script': '@Url.Action("Upload", "Category")',
            'cancelImg': '@Url.Content("~/uploadify/cancel.png")',
            'buttonText': 'Upload',
            'folder': '@Url.Content("~/content/images")',
            'fileDesc': 'Image Files',
            'fileExt': '*.jpg;*.jpeg;*.gif;*.png',
            'auto': true,
            'onComplete': function (event, ID, fileObj, response, data) {
                    var json = jQuery.parseJSON(response);
                    $("#pictureImage").html("<img src='"+json+"' alt='"+json+"' height='100px' width='100px'/>");
                    $("#Picture_PictureUrl").val(json);
                    $("#pictureRemove").show();
                }
            });
    </script>
    
  • Hooked the API to following Server Function for renaming and saving to folder

    [HttpPost]
    public ActionResult Upload(HttpPostedFileBase fileData)
    {
        if (fileData != null && fileData.ContentLength > 0)
        {
            //var fileName = Server.MapPath("~/Content/Images/" + Path.GetFileName(fileData.FileName));
            int pictureCount = 800000;
            pictureCount += db.Pictures.Count();
            string extension = Path.GetExtension(fileData.FileName);
            string renamedImage = Server.MapPath("~/Content/Images/Categories/cat" + pictureCount + extension);
            fileData.SaveAs(renamedImage);
            return Json("/Content/Images/Categories/" + Path.GetFileName(renamedImage));
        }
        return Json(false);
    }
    
  • And at last, rewrote the Category create Function as below for saving changes to DB

    [HttpPost]
    public ActionResult Create(CatPicView catPic)
    {
        if (ModelState.IsValid)
        {
            if (!String.IsNullOrEmpty(catPic.Picture.PictureUrl))
            {
                Picture picture = new Picture();
                picture.PictureUrl = catPic.Picture.PictureUrl;
                db.Pictures.Add(picture);
                catPic.Category.PictureId = picture.Id;
            }
            db.Categories.Add(catPic.Category);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View();
    }
    

OTHER TIPS

Create model like this:

public class FullCategoryModel
{
    public HttpPostedFileBase Picture { get; set; }
    public Category CategoryModel {get; set;}
}

In view:

@using (Html.BeginForm("Create", "Category", FormMethod.Post, 
    new { enctype = "multipart/form-data" }))
{  
  @Html.TextBoxFor(model => model.Category.Name)      // example, put there all category details 
  <input type="file" name="Picture" id="Picture" />      
  <input type="submit" value="Upload" />    

}

Then create action:

[ActionName("Create")]
[HttpPost]
public ActionResult Create(FullCategoryModel model)
{
// here you can get image in bytes and save it in db, 
// also all category detail are avalliable here

MemoryStream ms = new MemoryStream();
model.Picture.InputStream.CopyTo(ms);
Image picture = System.Drawing.Image.FromStream(ms);

// save in db as separate objects, than redirect
return RedirectToAction("Index", "Category");
}

I think MVC scaffolding feature see the relation of two models as "Many to Many". That's why it created two drop down list for you. According to your scenario, you could do "Category" create page without "Picture" model data because "Picture" is the main entity here. So In the picture create action.

[HttpPost]
    public ActionResult Create(Picture picture)
    {
        if (ModelState.IsValid)
        {
            databaseContext.Pictures.Add(picture);
            databaseContext.SaveChanges();
            return RedirectToAction("Index");  
        }

        return View(picture);
    }

In the view page of create picture

@model YourProjectName.Models.Picture
<h2>Create</h2>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
    <legend>Picture</legend>
    <div class="editor-label">
        @Html.LabelFor(model => model.Url)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Url)
        @Html.ValidationMessageFor(model => model.Url)
    </div>
    <div class="editor-label">
        @Html.LabelFor(model => model.Categories.CategoryID, "Category")
    </div>
    <div class="editor-field">
        @Html.DropDownList("CategoryID", "Choose Category")
        @Html.ValidationMessageFor(model => model.Categories.CategoryID)
    </div>
    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top