Jack
Jack

Reputation: 1

Cannot save multiple files to database with Foreign Key

In my MVC application I have two tables called Ticket and Attachment and I want to save attachments for per ticket. The problem is that: I need to save multiple attachments with the TicketID when creating a new ticket. So, I think I should create a new ticket in the Ticket table and then get its ID and save all the attachments with this TicketID to the Attachment table in a loop. I have look at many web sites and stackoverflow, but there is not such a kind of problem or solution on that pages. Any idea?

Note: I use Entity Framework Code First, but I can also use Stored Procedure or SQL command for this operation.

Here are these two models:

public class Ticket
{
    [Key] 
    public int ID { get; set; }

    public string Description { get; set; }

    //... removed for clarifty
}


public class Attachment
{
    [Key]
    public int ID { get; set; }

    //Foreign key for Ticket
    public int TicketID { get; set; }

    public byte[] FileData { get; set; }

    public string FileMimeType { get; set; }    
}


And the controller method:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude = null)] TicketViewModel viewModel 
/* contains both model: Ticket and Attachment */, IEnumerable<HttpPostedFileBase> files)
{
    if (ModelState.IsValid)
    {
        //??? First I need to save Ticket
        repository.SaveTicket(viewModel.Ticket);  

        foreach(var f in files)
        {
            viewModel.Attachment.FileMimeType = f.ContentType;
            viewModel.Attachment.FileData = new byte[f.ContentLength];
            f.InputStream.Read(viewModel.Attachment.FileData , 0, f.ContentLength);

            //??? Then save all attachment. But no idea how to get TicketID      
            repository.SaveAttachment(viewModel.Attachment); 
        }
    }   
    return View();
}

Upvotes: 1

Views: 255

Answers (1)

Rob Tillie
Rob Tillie

Reputation: 1147

The ID property will be automatically filled by EF after a SaveChanges. Your code can then use it. I assume that the viewModel.Ticket object is the actual object saved to the database. If not, please also post the SaveTicket method.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude = null)] TicketViewModel viewModel 
/* contains both model: Ticket and Attachment */, IEnumerable<HttpPostedFileBase> files)
{
    if (ModelState.IsValid)
    {
        // assumes viewModel.Ticket is the actual entity saved, and SaveChanges is called.  
        repository.SaveTicket(viewModel.Ticket);  

        foreach(var f in files)
        {
            viewModel.Attachment.FileMimeType = f.ContentType;
            viewModel.Attachment.FileData = new byte[f.ContentLength];
            f.InputStream.Read(viewModel.Attachment.FileData , 0, f.ContentLength);

            // fill ticket id
            viewModel.Attachment.TicketID = viewModel.Ticket.ID;
            repository.SaveAttachment(viewModel.Attachment); 
        }
    }   
    return View();
}

If you want to do everything in one transaction, you can add the childs immediatly, and SaveChanges will save all objects:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude = null)] TicketViewModel viewModel 
/* contains both model: Ticket and Attachment */, IEnumerable<HttpPostedFileBase> files)
{
    if (ModelState.IsValid)
    {
        var ticket = viewModel.Ticket;

        foreach(var f in files)
        {
            var attachment = new Attachment();
            attachment.FileMimeType = f.ContentType;
            attachment.FileData = new byte[f.ContentLength];
            f.InputStream.Read(attachment.FileData , 0, f.ContentLength);

            ticket.Attachments.Add(attachment);
        }

        // this will save the ticket and attachments
        repository.SaveTicket(ticket);
    }   
    return View();
}

Your Ticket class will have to look like this:

public class Ticket
{
    [Key] 
    public int ID { get; set; }

    public string Description { get; set; }

    // EF will now to use the foreign key to the attachment table
    public virtual ICollection<Attachment> Attachments { get; set; }
}

Upvotes: 1

Related Questions