Ayesha
Ayesha

Reputation: 31

exception on db.SaveChanges().Am i doing correct or not?Provide a solution with the correct code

I have taken primary key (Userid) as foreign key in 'Book' table.It is one to many relationship i.e a single user is able to upload multiple books. BookID is auto incremented.Whenever user clicks 'Upload Books' button, I need to store the name of the book and its path in database with userid as foreign key from User table and BookID to be auto increment.

I am having the following exception at db.SaveChanges():

An exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll but was not handled in user code Additional information: An error occurred while updating the entries. See the inner exception for details.

Inner exception is:

Cannot insert the value NULL into column 'Id', table 'BooksModule.dbo.Book'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated.

Below is my code:

Here is my model class: Book.cs

public partial class Book
{
    public string Id { get; set; }
    public int BookID { get; set; }
    public string BookName { get; set; }
    public string BookPath { get; set; }

    public virtual User User { get; set; }
}

Here is my model class of Users: User.cs

public partial class User
{
    public User()
    {
        this.Books = new HashSet<Book>();
    }

    public string Id { get; set; }
    public string UserName { get; set; }

    public virtual ICollection<Book> Books { get; set; }
}

here is a method:

[HttpGet]
public ActionResult FileUpload(string id)
{
    return View();
}

[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase file,Book bk)
{
        var filepath = "";
        var fname = "";
        if (ModelState.IsValid)
        {
            if (file.ContentLength > 0)
            {
                var fileName = Path.GetFileName(file.FileName);
                var path = Path.Combine(Server.MapPath("~/App_Data/Uploads"), fileName);
                filepath = path;
                fname = fileName;
                file.SaveAs(path);
            }
            bk.BookName = fname;
            bk.BookPath = filepath;
            db.Books.Add(bk);
            db.SaveChanges();
            return RedirectToAction("Index", "Home");
        }
        else
        {
            return View();
        }      
    }

Here is a view:

<div class="sidebar">
    @using (Html.BeginForm("FileUpload", "UploadBooks", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        @Html.ValidationSummary();
        <fieldset>
            <legend>Upload a file</legend>
            <div class="editor-field">
            @Html.TextBox("file", "", new { type = "file" })
            </div>

            <input type="submit" id="btnSubmit" class="btn btn-default2" value="Upload" />

        </fieldset>
    }

</div>

Upvotes: 0

Views: 3841

Answers (1)

DavidG
DavidG

Reputation: 118937

Entity Framework will pick Id as the primary key, and as yours is a string you need to supply it yourself (which you are not doing.)

It's preferable to have an int as your primary key so EF can make it an identity column in the database.

A few other observations too:

  1. The FileUpload post action is taking a Book object that isn't used other than as a variable in your action, instead just declare a new Book object inside the method.
  2. The fileName and path variables are not needed.
  3. Do you need both Id and BookId? I would remove BookId.

I would suggest your Book.cs looks like this:

public partial class Book
{
    public int Id { get; set; }

    public string BookName { get; set; }
    public string BookPath { get; set; }

    public virtual User User { get; set; }
}

And your post action:

[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase file)
{
    if (ModelState.IsValid)
    {
        if (file.ContentLength > 0)
        {
            fname = Path.GetFileName(file.FileName);
            filepath = Path.Combine(Server.MapPath("~/App_Data/Uploads"), fileName);
            file.SaveAs(fname);

            Book bk = new Book
            {
                BookName = fname,
                BookPath = filepath
            };

            db.Books.Add(bk);
            db.SaveChanges();

        }

        return RedirectToAction("Index", "Home");
    }
    else
    {
        return View();
    }      
}

Update

From the comments you made above, there is an additional change you need to make to the Book class. First you need to tell Entity Framework that your Id property if the key using the [Key] attribute. Secondly for the Id column you should do one of these:

  1. Remove Id (as Entity Framework will create one for you)
  2. Rename Id to UserId to allow Entity Framework to automatically link it to the User property.
  3. Add an attribute to the User property to tell it the name of the column to use, for example [ForeignKey("Id")]

I would suggest either 1 or 2 so it is more obvious what the column is when looking at the database:

public partial class Book
{
    [Key]
    public int BookId { get; set; }

    public string BookName { get; set; }
    public string BookPath { get; set; }

    public string UserId { get; set; }
    public virtual User User { get; set; }
}

Upvotes: 1

Related Questions