ASP.NET MVC 5 with EF 6 - Working With Files

4.39 (59 votes)

This tutorial explores how to upload files in an ASP.NET MVC application and how to use Entity Framework to store them in a database. It builds on a series of 12 featuring the fictitious Contoso University that teach you how to build MVC 5 applications using Entity Framework for data access. The original tutorial series was produced in C# by Tom Dykstra and Rick Anderson ( @RickAndMSFT ) of Microsoft. This tutorial is coded in C#. As with the previous tutorials, I have produced a version that uses the Visual Basic language. It is available here.

Download the code

The code for this section is available here. Save the .zip file to a convenient location and then extract the contents. Make sure you have an edition of Visual Studio 2013 installed (Express for Web, Community, Professional, Premium or Ultimate) and double click the .sln file. Once the project is opened in your IDE, press Shift+Ctrl+B to build the solution. This will ensure that all packages are restored from Nuget and may take a while depending on your Internet connection speed.

 

Should I store files in the database or file system?

One of the most often asked question in developer forums, the answer to this conundrum is "it depends". There are pros and cons to both approaches and this tutorial will not seek to add to the existing debates, but will cover how to approach both options. You will modify the Student CRUD methods and views to store avatar images in the database. Then you will modify the Instructor CRUD methods and views to store images in the field system and their file name in the database.

Create FileType Enumeration for File Types

The application will cater for files that will be used for a variety of purposes, some of which aren't known at the moment. The two types of file that you will start with will be images that serve as an Avatar for a student, and a photo for an instructor. One approach to this might be to add a new property to the User entity for each file. However, it is in the nature of software development that one day, a stakeholder will ask for the application to be modified to cater for personal reports, grades, meeting notes and all sorts of other files. Every time you need to cater for a new file type, you will have to alter the User class and add a new migration. Instead, you will create an enumeration to denote the purpose of each file, and add one property to the user entity to hold a collection of files, regardless of their purpose.

Add a new class file to the Models folder and name it FileType.cs. Replace the existing code with the following:

namespace ContosoUniversity.Models
{
    public enum FileType
    {
        Avatar = 1, Photo
    }
}

Storing Files in the Database

The first example will cover storing files as binary data in the database table. If you have already decided to store files in the file system and want to skip this part, feel free to navigate to the second part of the tutorial.

Create File Entity

Newer versions of SQL Server offer the FileStream data type for storing file data. Existing versions of Entity Framework (6.1.2 as of the publication date of this tutorial) do not support the FileStream data type via Code First. When you store file content in a database, the content itself is stored in binary format. This is represented as an array of bytes in .NET which Entity Framework (currently) maps to the SQL Server varbinary(max) data type.

  1. Right click on the Models folder and choose Add New Item. Add a class file, name it File.cs and replace the existing content with the following:

    using System.ComponentModel.DataAnnotations;
    
    namespace ContosoUniversity.Models
    {
        public class File 
        {
            public int FileId { get; set; }
            [StringLength(255)]
            public string FileName { get; set; }
            [StringLength(100)]
            public string ContentType { get; set; }
            public byte[] Content { get; set; }
            public FileType FileType { get; set; }
            public int PersonId { get; set; }
            public virtual Person Person { get; set; }
        }
    }

    The File entity has an virtual Person property, which is one part of establishing a one-to-many relationship with the Person class.

    MVC5 With EF6

  2. Complete this association by adding a using directive for System.Collections.Generic and a collection of files as a property to the Person class (highlighted below):

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Collections.Generic;
    
    namespace ContosoUniversity.Models
    {
        public abstract class Person
        {
            public int ID { get; set; }
    
            [Required]
            [StringLength(50)]
            [Display(Name = "Last Name")]
            public string LastName { get; set; }
            [Required]
            [StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
            [Column("FirstName")]
            [Display(Name = "First Name")]
            public string FirstMidName { get; set; }
    
            [Display(Name = "Full Name")]
            public string FullName
            {
                get
                {
                    return LastName + ", " + FirstMidName;
                }
            }
            public virtual ICollection<File> Files { get; set; }
        }
    }
  3. Now add a DbSet to the SchoolContext class:

    public DbSet<File> Files { get; set; }
  4. Add a new migration by typing the following into the Package Manager Console(PMC):

    add-migration Files
  5. Run the migration by typing the following into the PMC.

    update-database

Change the Student Create Form and Controller

Make the highlighted changes to the Create.vbhtml file in the Views\Student folder:

@using (Html.BeginForm("Create", "Student", null, FormMethod.Post, new {enctype = "multipart/form-data"})) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <h4>Student</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.FirstMidName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.FirstMidName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.FirstMidName, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.EnrollmentDate, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.EnrollmentDate, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.EnrollmentDate, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.Label("Avatar", new {@class = "control-label col-md-2"})
            <div class="col-md-10">
                <input type="file" id="Avatar" name="upload" />
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

You have added a file upload to the form:

MVC5 With EF6

You did this by specifying the type as "file" on an input element. However, this is not enough to ensure that uploaded file data is accessible on the server. You also used one of the longer overloads of the Html.BeginForm helper to add an enctype attribute to the form and sets its value to multipart/form-data. This is an essential step to getting file uploading to work.

Now add the highlighted code below to the HttpPost Create method in the StudentsController:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "LastName, FirstMidName, EnrollmentDate")]Student student, HttpPostedFileBase upload)
{
    try
    {
        if (ModelState.IsValid)
        {
            if (upload != null && upload.ContentLength > 0)
            {
                var avatar = new File
                {
                    FileName = System.IO.Path.GetFileName(upload.FileName),
                    FileType = FileType.Avatar,
                    ContentType = upload.ContentType
                };
                using (var reader = new System.IO.BinaryReader(upload.InputStream))
                {
                    avatar.Content = reader.ReadBytes(upload.ContentLength);
                }
                student.Files = new List<File> { avatar };
            }
            db.Students.Add(student);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
    }
    catch (RetryLimitExceededException /* dex */)
    {
        //Log the error (uncomment dex variable name and add a line here to write a log.
        ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
    }
    return View(student);
}

The first change adds a new parameter to the Create method's signature - upload, which is an instance of the HttpPostedFileBase type. The parameter name matches the name attribute's value of the input type="file" in the Create form so that the MVC model binder can bind the uploaded file to the parameter that you added to the Create method.

The highlighted code block that you added is responsible for extracting the binary data from the request and populating a new File entity instance ready to be added to the student's Files collection. If the user adds a new student without uploading a file, the upload parameter will equate to null, which is why that condition is checked before any attempt to access the HttpPostedFileBase ContentLength property. If you do not perform this check, and then reference the ContentLength property when no file has been uploaded, an exception will be raised. You then check the ContentLength because it is perfectly possible to upload an empty file. If the user does that, the ContentLength property will return 0, and there is no point in storing a file with no data in it. If the upload passes both tests, you create a new File object and assign the relevant properties with values taken from the upload parameter. The binary data is obtained from the InputStream property of the uploaded file, and a BinaryReader object is used to read that into the Content property of the File object. Finally, you add the new File object to the student object.

Change the Details method and View

Having stored the image in the database, you need to make some alterations to obtain the image and display it in the Details view.

  1. First, you will use the Include method in the LINQ query that fetches the student from the database to bring back releated files. The Include method does not support filtering, so the highlighted line below fetches all files associated with the student regardless of type.

    public ActionResult Details(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Student student = db.Students.Include(s => s.Files).SingleOrDefault(s => s.ID == id);
        if (student == null)
        {
            return HttpNotFound();
        }
        return View(student);
    }
  2. Add the highlighted line to the Views\web.config file. Make sure you add this to the web.config file in the Views directory - not the one in the root folder.

    <namespaces>
      <add namespace="System.Web.Mvc" />
      <add namespace="System.Web.Mvc.Ajax" />
      <add namespace="System.Web.Mvc.Html" />
      <add namespace="System.Web.Optimization"/>
      <add namespace="System.Web.Routing" />
      <add namespace="ContosoUniversity" />
      <add namespace="ContosoUniversity.Models" />
    </namespaces>
  3. Add the highlighted section of code to the Views\Student\Details.cshtml file.

    <h4>Student</h4>
    <hr />
    <dl class="dl-horizontal">
        @if(Model.Files.Any(f => f.FileType == FileType.Avatar)){
            <dt>
                Avatar
            </dt>
            <dd>
                <img src="~/File?id=@Model.Files.First(f => f.FileType == FileType.Avatar).FileId" alt="avatar" />
            </dd>
        }
        <dt>
            @Html.DisplayNameFor(model => model.LastName)
        </dt>

The change that you made to the Views\web.config file makes the ContosoUniversity.Model namespace available to your views. It results in you not having to use the fully qualified name to reference items in that namespace, such as the FileType enumeration. If you had not made that change, you would have to do a bit more typing in the view whenever you want to reference types in the Model namespace:

@if(Model.Files.Any(f => f.FileType == ContosoUniversity.Model.FileType.Avatar)){

You returned all of the files associated with the student (if there are any), so you must check to see if there are any files matching the Avatar file type. If there are, you render the first of these files. The image is rendered using a standard HTML img tag.

By convention, the url of the image maps to the Index method of a controller called File, passing in the id of the file as a query string value.

Adding the File Controller

  1. Right click on the Controllers folder and choose Add.. Controller

  2. Choose MVC 5 Controller - Empty from the selection.

    MVC5 With EF6

  3. Name it FileController.

    MVC5 With EF6

  4. Replace the templated code with the following:

    using ContosoUniversity.DAL;
    using System.Web.Mvc;
    
    namespace ContosoUniversity.Controllers
    {
        public class FileController : Controller
        {
            private SchoolContext db = new SchoolContext();
            //
            // GET: /File/
            public ActionResult Index(int id)
            {
                 var fileToRetrieve = db.Files.Find(id);
                 return File(fileToRetrieve.Content, fileToRetrieve.ContentType);
            }
        }
    }

    The code obtains the correct file based on the id value passed in the query string. Then it returns the image to the browser as a FileResult.

  5. Create a new student, making sure that you choose an image file to upload, then navigate to their details to check the result.

    MVC5 With EF6

You have completed the functionality to add an image to be stored in the database to a new student and display it as part of their details. The next section covers how to provide functionality to allow users to edit an existing student's avatar image.

Customise the Edit Methods and View

  1. Alter the code in the HttpGet Edit method in the StudentController to include the retrieval of files as in the highlighted code below.

    // GET: Student/Edit/5
    public ActionResult Edit(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Student student = db.Students.Include(s => s.Files).SingleOrDefault(s => s.ID == id);
        if (student == null)
        {
            return HttpNotFound();
        }
        return View(student);
    }
  2. Alter the Views\Student\Edit.cshtml file to include the correct enctype attribute in the form, and to both display the existing image and to provide a file upload should the user wish to provide a replacement:

    @using (Html.BeginForm("Edit", "Student", null, FormMethod.Post, new {enctype = "multipart/form-data"}))
    {
        @Html.AntiForgeryToken()
        
        <div class="form-horizontal">
            <h4>Student</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            @Html.HiddenFor(model => model.ID)
    
            <div class="form-group">
                @Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
                </div>
            </div>
    
            <div class="form-group">
                @Html.LabelFor(model => model.FirstMidName, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.FirstMidName, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.FirstMidName, "", new { @class = "text-danger" })
                </div>
            </div>
    
            <div class="form-group">
                @Html.LabelFor(model => model.EnrollmentDate, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.EnrollmentDate, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.EnrollmentDate, "", new { @class = "text-danger" })
                </div>
            </div>
    
            @if(Model.Files.Any(f => f.FileType == FileType.Avatar)) {
                <div class="form-group">
                    <span class="control-label col-md-2"><strong>Current Avatar</strong></span>
                    <div class="col-md-10">
                        <img src="~/File?id=@Model.Files.First(f => f.FileType == FileType.Avatar).FileId" alt="avatar" />
                    </div>
                </div>
            }
    
            <div class="form-group">
                @Html.Label("Avatar", new {@class = "control-label col-md-2"})
                <div class="col-md-10">
                    <input type="file" id="Avatar" name="upload" />
                </div>
            </div>
    
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Save" class="btn btn-default" />
                </div>
            </div>
        </div>
    }
  3. Make the highlighted changes to the HttpPost Edit method as shown below.

    [HttpPost, ActionName("Edit")]
    [ValidateAntiForgeryToken]
    public ActionResult EditPost(int? id, HttpPostedFileBase upload)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        var studentToUpdate = db.Students.Find(id);
        if (TryUpdateModel(studentToUpdate, "",
            new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
        {
            try
            {
                if (upload != null && upload.ContentLength > 0)
                {
                    if (studentToUpdate.Files.Any(f => f.FileType == FileType.Avatar))
                    {
                        db.Files.Remove(studentToUpdate.Files.First(f => f.FileType == FileType.Avatar));
                    }
                    var avatar = new File
                    {
                        FileName = System.IO.Path.GetFileName(upload.FileName),
                        FileType = FileType.Avatar,
                        ContentType = upload.ContentType
                    };
                    using (var reader = new System.IO.BinaryReader(upload.InputStream))
                    {
                        avatar.Content = reader.ReadBytes(upload.ContentLength);
                    }
                    studentToUpdate.Files = new List<File> { avatar };
                }
                db.Entry(studentToUpdate).State = EntityState.Modified;
                db.SaveChanges();
    
                return RedirectToAction("Index");
            }
            catch (RetryLimitExceededException /* dex */)
            {
                //Log the error (uncomment dex variable name and add a line here to write a log.
                ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
            }
        }
        return View(studentToUpdate);
    }

You altered the signature of the HttpPost Edit method to accept an instance of the HttpPostedFile. As in the Create method, this represents the uploaded file. The highlighted code block checks to see if a file was uploaded, and if one was, that it contains data. These are the same checks as you made in the Create method. If the file contains data, you find any existing files associated with the current student that are Avatar file types. If found, you mark the file for deletion. Then you add the uploaded file as a replacement.

Working with the file system

The next section covers how to store uploaded files in the file system instead of in the database. You will create a FilePath entity to represent a single file.

  1. Add a new class to the Models folder called FilePath.cs and replace the templated code with the following:

    namespace ContosoUniversity.Models
    {
      using System.ComponentModel.DataAnnotations;  
        public class FilePath
        {
            public int FilePathId {get;set;}
            [StringLength(255)]
            public string FileName {get;set;}
            public FileType FileType {get;set;}
            public int PersonID {get;set;}
            public virtual Person Person {get;set;}
        }
    }
  2. Add a DbSet to the SchoolContext for the new entity:

    public DbSet<FilePath> FilePaths { get; set; }
  3. Add a new navigational property to the Person class to accommodate a collection of FilePath objects:

    public virtual ICollection<FilePath> FilePaths { get; set; }

    This creates the one-to-many relationship between Person and FilePath:

    MVC5 With EF6

  4. Press Ctrl+Shift+B to ensure that your project builds successfully, then add a new migration to the project by typing the following into the PMC:

    add-migration FilePaths
  5. Apply the changes to the database by typing update-database into the PMC.

Amending the Instructor Create and Details Views and Methods

In this example, you will only alter the Create and Details methods and views. These alterations cover the core concepts that you need to understand when saving files to the file system.

  1. Add a new folder to the root directory called "images".

  2. Alter the Html.BeginForm helper in Views\Instructor\Create.cshtml to add the correct enctype to the form:

    @using (Html.BeginForm("Create", "Instructor", null, FormMethod.Post, new {enctype = "multipart/form-data"})){
  3. Now add a file input to the form itself, just after the Office Location input and before the div that houses the assigned courses checkboxes:

    <div class="form-group">
         @Html.Label("Photo", new {@class = "control-label col-md-2"})
         <div class="col-md-10">
             <input type="file" id="Photo" name="upload" />
         </div>
    </div>
  4. Make the highlighted changes to the HttpPost Create method in InstructorController.cs:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "LastName,FirstMidName,HireDate,OfficeAssignment")]Instructor instructor, string[] selectedCourses, HttpPostedFileBase upload)
    {
        if (selectedCourses != null)
        {
            instructor.Courses = new List<Course>();
            foreach (var course in selectedCourses)
            {
                var courseToAdd = db.Courses.Find(int.Parse(course));
                instructor.Courses.Add(courseToAdd);
            }
        }
        
        if (ModelState.IsValid)
        {
            if (upload != null && upload.ContentLength > 0)
            {
                var photo = new FilePath
                {
                    FileName = System.IO.Path.GetFileName(upload.FileName),
                    FileType = FileType.Photo
                };
                instructor.FilePaths = new List<FilePath>();
                instructor.FilePaths.Add(photo);
            }
            db.Instructors.Add(instructor);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        PopulateAssignedCourseData(instructor);
        return View(instructor);
    }

You added an additional parameter to the Create method's signature - upload of type HttpPostedFileBase. This is mapped to the file upload based on the parameter's name matching the name attribute on the input type=file that you added in the Create view, and provides access to the properties of the uploaded file. The added code checks to ensure that the upload is not null and then checks its ContentLength property to ensure that an empty file as not uploaded. If both of those checks succeed, a new FilePath object is created and its FileName property is assigned the value of the uploaded file's FileName, which is obtained by the System.IO.Path.GetFileName method.

Note: Some browsers provide more than just the file name when files are uploaded. Versions of Internet Explorer will provide the full client file path when you use it locally, i.e in development and test. Other browsers may or may not prepend the file name with values such as C:\FakePath\. In any event, you cannot rely on a browser providing just the file's name. Equally, you cannot rely on the browser to provide the original path of the uploaded file. Only some versions of Internet Explorer (currently) do this, and even then, only when your client and server are the same machine.

The file itself is saved to the images folder using the value extracted from the upload as the file name.

Note: it is not always a good idea to use the file's original name when you store uploads in the file system. Two files with the same name cannot exist in the same location so if a new file is uploaded and saved with the same name as an existing one, the original file will be overwritten when the new one is saved. A common resolution to this problem is to rename the file prior to saving it. Typically, a Guid is used as a file name on the basis that it can pretty much guarantee uniqueness. If you need to adopt this strategy, you can replace the existing line that assigns the FilePath object's FileName property with the following highlighted line of code:

var photo = new FilePath
{
    FileName = Guid.NewGuid().ToString() + System.IO.Path.GetExtension(upload.FileName),
    FileType = FileType.Photo
};

Amend the Instructor Details Method and View

  1. Make the highlighted amendment to the Details method in InstructorController.cs:

    public ActionResult Details(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Instructor instructor = db.Instructors.Include(i => i.FilePaths).SingleOrDefault(i => i.ID == id);
        if (instructor == null)
        {
            return HttpNotFound();
        }
        return View(instructor);
    }
  2. Finally, add the following code to the Views\Instructor\Details.cshtml file just before the closing </dl> tag at the bottom

    @if(Model.FilePaths.Any(f => f.FileType == FileType.Photo))
    {
        <dt>
            Photo
        </dt>
        <dd>
            <img src="~/images/@Model.FilePaths.First(f => f.FileType == FileType.Photo)).FileName" alt="" />
        </dd>
    }

The alteration that you made to the Details method in the controller ensures that any filepath objects belonging to the instructor are retrieved along with the rest of their details. The code that you added to the View checks to see if there are any filepath objects matching the FileType.Photo type, and if there are, the first one is displayed using an img element. The src attribute value is generated by concatenating the location where files are saved with the name that was given to this particular file. Notice that you didn't include the folder name as part of the value that you used for the FileName property. Doing so may make things more diffcult if you ever needed to change the file storage location.

Summary

In this tutorial, you saw how to use Entity Framwork to work with files in an MVC application. You explored two approaches to persisting file information: storing the file data in the database; and storing the file in the file system.

 

You might also like...

Date Posted:
Last Updated:
Posted by:
Total Views to date: 270070

47 Comments

- Stephen kenny

Hi, great article.
My issue is with the create view.
@using (Html.BeginForm("Create", "Reptiles", null, FormMethod.Post, new {enctype = "multipart/form-data"}))
This is my version and it is saying im missing a brace any ideas ?

- Mike

@Stephen,

You might be missing one after the closing )

- stephen

Everything has been ok up until this part, here's a snippet of the entire file but i removed lines that are irrelevant.

@model ReptileManager.Models.Reptile
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm("Create", "Reptiles", null, FormMethod.Post, new {enctype = "multipart/form-data"}))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Reptile</h4>
<hr />
***removed other fields for clarity***
<!--Images----------------------------------------------------------------------------------------------------------------------------------------------->
<div class="form-group">
@Html.Label("image", new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" id="image" name="upload" />
</div>
</div>

**removed for clarity ***

<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>

</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

- stephen

Hi,
Sorry for wasting your time, I had a comment <!--Images--> inside the form this stopped it from working.

- Mike

@Stephen,

Good that you've resolved it.

- hans

Hi there, tHis was almost exactly what I was looking for, but could you provide a example on how to set it up with a database first example too ?

- Abolfazl RoshanZamir

Hi, this article realy good and nice explain..thanks .

- Mog0

Probably a silly question but why did you disable cascade delete and implement it yourself?

- Mike

@Mog0,

It's actually a very good question to which I don't have a sensible answer. I have modified the article and downloads to work with cascade deletes now.

- hosein ey

tnx for this article
do you think next version of entityframework support's the sql server filestream and filetable ?

- Mike

@hosein ey

I have no idea. At the moment, the next version won't be ready for quite a while. The ADO.NET team are under a fair amount of pressure to deliver in line with the planned ASP.NET 5 RTM, and have already said that the version of EF7 that ships then won't actually be production-ready.

- Ro

Thanks, great article. Helped me a lot... I can add something that you miss in HttpPost Create method in InstructorController.cs:

instructor.FilePaths = new List<FilePath>(); instructor.FilePaths.Add(photo);
upload.SaveAs(Path.Combine(Server.MapPath("~/images"), photo.FileName));

Now the file is saved to the images folder...

- Abdirahman

Thank you very much.

- Rakesh Panchal

this is v v good web site for help

- Sean

Thank you for the tutorial. Saving to the database works, but the save FilePath is not working. It saves the GUID in the table, but the photo doesn't save to the /images/ folder. Is there some code missing?

Here is the HTML for the image:
<img src="/images/System.Data.Entity.DynamicProxies.FilePath_EB579B86026F81ABDEAE7F56AC5094383B3B0706E198E94E5D758B6772852F10).FileName" alt="">

Thanks

- saurabh rao

Hey Mike ! Great article ...the code worked flawlessly for me. Didn't have any issues executing it in the least .

I had another idea. How about using a folder to store a specific number of files ( which would be uploaded by a single student ) to ensure that files are stored by the student who has uploaded them. Any ideas \ thoughts on why this a good \ bad idea ? And if so , how would you proceed ? Thanks for your time !

- Joseph Casey

Probably should have read the title. Couldn't use it with Visual Studios 2013. =[

- Joseph Casey

Can you explain or provide a resource as to what is happening here?

public enum FileType
{
Avatar = 1, Photo
}

- Mike

@Joseph,

Those are enums. By default, they are assigned sequential numbers starting with 0, unless you specify another starting point as I have done in this case.

- Mike

@Sean,

I think you need to re-check your code. You have a mistake there somewhere. If you kind find it, post a question including the code you are using to the ASP.NET forums or Stackoverflow.

- Paul O'Dell

Hi Mike,

I am coming from classic ASP and just learning .NET and MVC. This article was fantastic at showing me how to upload and store files in the database. 15 minutes job done. Brilliant!

However, I want to upload a file using Ajax.BeginForm so that can update a portion of the screen that is a partial view (list of files). I just wondered if you had a similarly simple example for that scenario. I believe it is blocked for security reasons but a lot of the examples seem very convoluted.

Thanks again

- Jimmy

I used de database first appraoch. When I add a class Filteype I get the following error:
The property 'FileType' on the type 'ApplicationDossier.Models.Dossier' has a property type of 'ApplicationDossier.Models.FileType' which cannot be mapped to a primitive type.
Filetype is a string with max 50 chr. What am I doing wrong?

- Alfred Shaker

Hello,
Great tutorial! Thank you! I do have a question thought: I am doing this using a file system, and when I go to add the code to the details.cshtml page i get an error regarding the FileType and it says that it doesnt exist in the current context. Can someone please help me solve this? Thank you!

- Rohan

Very good and helpful tutorial. Thanks. Just wanted to know what would be the max file size limit we have here? Can I upload a file with say 50MB data? If yes, what config changes i'll have to do?

- Mike

@Rohan

The default maximum request size is 4MB. You can alter that in your web.config:

maxRequestLength="51200" />
but you also need to configure IIS to allow that size upload because it imposes a ~30MB limit. Here's one article that discusses how to change that: IIS7 File Upload Size Limits.

- Babak

Thanks Buddy , it was great article.

- Kamil

Hello Mike,

Any reason why the download link to the source code doesn't work anymore. I'd like to download the sample code. Where can I find it? Thanks.

Br.,
Kamil.

- Mike

@Kamil,

Sorry about that. I moved servers and forgot to copy that directory across. It's there now.

- Bew

hello mike, i need multiple upload file.

thx for help.

- Mike

@Bew,

I might do an article on multiple file uploading at some point.

- AKH

Thank you for a very well written and interesting article.

- saeed

hi i am new to asp.net mvc
i did every thing you told
with first approach
but when i want create a product and send a photo nothing will saved

- Mike

@saeed,

You must have made a mistake somewhere. I'm not sure how you expect me to be able to guess where. If you can't work out where it is, you should post a question to a forum.

- M

This article was incredibly helpful for me. Thank you!

- JR

Hello, Where is the location of the database? Thanks

- eamon

thank you so much, excelent tutorial

- J.

AWESOME ARTICLE! For anyone that wants file uploads to a database with MVC 5, this article is absolutely perfect. Worked like a charm for me after a few modifications to make it work for docx, xlsx files rather than images.

- Ad Gerrits

Thanks Mike. I'm rather new to MVC and EF and this is one of the best tutorials I've read. BTW: source code is still missing the previous mentioned 'upload.SaveAs(Path.Combine(Server.MapPath("~/images"), photo.FileName)); ' line...

- Bevan

Thanks Mike, your article helped me a lot!

- david sanchez

Thanks!

This line " " contains an extra ")"

- sajib

it was very helpful

- Bill Barbour

Wonderful example. I have it all working. I would like to add the image to each row of the index view. Would you provide an example of that? Thanks.

- Kelum Priyadarshana

Can I use this solution with entity framework also

- Vladimir Mrykin

Thanks for article.
I have 1 question for filesystem part.

Where is code for actual saving uploaded file to images directory? We a saving filepath, but not saving uploaded file.

- Francisco

The post is very good, thanks

- Micheal

Thanks for the code. its pretty straightforward. worked for me on my first trial. Perfect!

- wanyi

Great work. A little error needs to be revised: The "save as " line is missing in "InstructorController.cs", after "instructor.FilePaths.Add(photo);"

upload.SaveAs(Path.Combine(Server.MapPath("~/images"), photo.FileName))

Recent Comments

Pam 30/08/2017 11:30
In response to Sending Email in Razor Pages
Mike, RazorPages sound like a nice choice for somebody still working in ASP classic who wants to to...

Robby Robson 15/08/2017 00:43
In response to Routing in Razor Pages
Mike: great stuff. Now that .Core Standard 2.0 is formally out, how soon will you rewrite your book...

Satyabrata Mohapatra 28/07/2017 08:59
In response to Sending Email in Razor Pages
Bit off topic, but congratulation sir for your MVP award. You deserve it !!!...

Satyabrata Mohapatra 23/07/2017 16:43
In response to Razor Pages - The Elevator Pitch
@Dale Severin You can continue to build apps using asp.net web pages....

Satyabrata Mohapatra 23/07/2017 16:40
In response to Sending Email in Razor Pages
Thanks for sharing...learned a lot...

Gfw 22/07/2017 11:53
In response to Sending Email in Razor Pages
Question... Does System.Net.Mail support SSL?...

Dale Severin 20/07/2017 03:38
In response to Razor Pages - The Elevator Pitch
I work with razor web pages extensively. I appreciate the rapid development it permits me to I am as...

Obinna Okafor 14/07/2017 01:19
In response to Routing in Razor Pages
Thank you, Mike. Good post....

Satyabrata Mohapatra 11/07/2017 16:02
In response to Routing in Razor Pages
Very powerful routing system!!...

Cyrus 05/07/2017 03:41
In response to Razor Pages - Getting Started With The Preview
How can I trim packages and services as much as possible to use just razor pages? I don’t want to to...