3
\$\begingroup\$

I have an ASP.NET MVC Controller, the relevant parts of which appear here:

public class IncidentController : Controller
{
    private IncidentDBContext db = new IncidentDBContext();

    public ActionResult Edit(int id = 0)
    {
        Incident incident = db.Incidents
            .Include(i => i.Images)
            .Where(i => i.IncidentID == id)
            .Single();
        if (incident == null)
        {
            return HttpNotFound();
        }
        return View(incident);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(Incident incident, int[] SelectedImages)
    {
        if (ModelState.IsValid)
        {
            UpdateRelatedImages(incident, SelectedImages);

            db.Entry(incident).State = EntityState.Modified;
            db.SaveChanges();

            return RedirectToAction("Index");
        }
        return View(incident);
    }

    private void UpdateRelatedImages(Incident incident, int[] selectedImageIDs)
    {
        incident.Images = new List<IncidentImage>();
        foreach (int imageID in selectedImageIDs)
        {
            IncidentImage image = db.IncidentImages.Where(i => i.IncidentImageID == imageID).Single();
            if (image.IncidentID == incident.IncidentID)
            {
                incident.Images.Add(image);
            }
            else if (image.IncidentID == null)
            {
                incident.Images.Add(image);
                image.IncidentID = incident.IncidentID;
                db.Entry(image).State = EntityState.Modified;
            }
            else
            {
                throw new InvalidOperationException(String.Format("Cannot assign image {0} to incident {1}.  Current Incident ID of image: {2}.", imageID, incident.IncidentID, image.IncidentID));
            }
        }
    }

    [HttpPost]
    public ActionResult UploadImages()
    {
        var images = new List<object>();

        foreach (string imageName in Request.Files.AllKeys)
        {
            HttpPostedFileBase file = Request.Files[imageName];
            string fileName = Guid.NewGuid().ToString() + Path.GetExtension(imageName);
            string filePath = Server.MapPath("~/UploadedImages/" + fileName);
            file.SaveAs(filePath);
            int insertedID = CreateImage(imageName, fileName);
            images.Add(new { ImageName = imageName, FileName = fileName, ID = insertedID });
        }

        return Json(images);
    }

    private int CreateImage(string imageName, string fileName)
    {
        string CreateImageQuery =
        @"insert into
            INCIDENT_IMAGE_TEST (IMAGE_NAME, [FILE_NAME])
        output
            inserted.INCIDENT_IMAGE_ID
        values
            (@imageName, @fileName)";

        int returnThis = db.Database.SqlQuery<int>(
            CreateImageQuery,
            new SqlParameter("imageName", imageName),
            new SqlParameter("fileName", fileName)
        ).Single();

        return returnThis;
    }
}

And two code-first models:

[Table("INCIDENT_TEST")]
public class Incident
{
    [Column("INCIDENT_ID")]
    public int IncidentID { get; set; }

    [InverseProperty("IncidentID")]
    public List<IncidentImage> Images { get; set; }

    // ...
}

[Table("INCIDENT_IMAGE_TEST")]
public class IncidentImage
{
    [Column("INCIDENT_IMAGE_ID")]
    public int IncidentImageID { get; set; }

    [Column("INCIDENT_ID")]
    public int? IncidentID { get; set; }

    // ...
}

The code works, but I feel like I'm fighting the framework instead of using it. Specifically, I'm concerned with two things:

The UploadImages action is called via AJAX in the view, creates an entry for the image in my DB, and sends back the information that the view needs to update its list via JSON. Works fine, but I don't like the call to SqlQuery() that executes manually-written SQL to insert the row.

Is there any way to do that by working with an IncidentImage model after the file is saved?

The HTTP POST version of the Edit action binds most of the incoming parameters directly to the Incident model, but the set of check boxes indicating which images should be related to the incident get bound to an int[] and then handled manually by UpdateRelatedImages(). After binding, incident.Images remains null, so I create a new list for it and add all related images.

Is this the "proper" way to do this, or is there some way for me to populate incident.Images during binding, and only deal with changes to the list, rather than remaking the whole thing?

\$\endgroup\$
  • \$\begingroup\$ Your code has a bug in Edit(int id = 0) when you say .Single(); you mean SingleOrDefault(); \$\endgroup\$ – Snowbody Aug 12 '14 at 17:41
  • \$\begingroup\$ Similar bug in UpdateRelatedImages(Incident incident, int[] selectedImageIDs) \$\endgroup\$ – Snowbody Aug 12 '14 at 17:43
3
\$\begingroup\$

I am a bit late and I don't know if I understand very well the second part of your question but maybe I can help you with the first one about CreateImage method. If you are using DB generated Ids for your IncidentImage entity, EF follows every INSERT with SELECT SCOPE_IDENTITY(), hence you already have the id

private int CreateImage(string imageName, string fileName)
{
    IncidentImage image = new IncidentImage
    { 
        // set image and file name 
    }       

    db.IncidentImages.AddObject(image);
    db.SaveChanges();

    return  image.IncidentImageID;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.