4

When I try to save to my database I get the error

SqlException: Cannot insert explicit value for identity column in table 'Photo' when IDENTITY_INSERT is set to OFF.

I am using asp.net 5 MVC 6 with EF 7 on Visual Studio 2015 There are a lot of similar questions. Most have solutions that are not supported by asp.net 5 MVC 6 or EF 7(One said to use a Data Annotation that would fix the problem in EF 6). The others have not worked. I try not to ask except as a last resort.

My design is that every user will have many folders, and a folder will have many photos.

I added public ICollection<UserFolder> UserFolders { get; set; } to ApplicationUser

The Model:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations.Schema;

namespace FamPhotos.Models
{
    public class UserFolder
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public ICollection<Photo> Photo { get; set; }

        public string ApplicationUserId { get; set; }
        public virtual ApplicationUser ApplicationUser { get; set; }
    }

    public class Photo
    {
        public int ID { get; set; }
        public string Description { get; set; }
        public DateTime UploadDate { get; set; }
        public string Url { get; set; }

        public int UserFolderId { get; set; }
        public UserFolder UserFolder { get; set; }

    }
}

The Controller method

// POST: Photos/Create
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create(Photo photo, IFormFile files, int id)
    {
        if (files == null)
        {
            ModelState.AddModelError(string.Empty, "Please select a file to upload.");
        }
        else if (ModelState.IsValid)
        {
            photo.UploadDate = DateTime.Now;
            photo.UserFolderId = id;

            var folderName = _context.UserFolder.Where(q => q.ID == id).Single().Name; 

            //TODO:  Check for image types
            var fileName = photo.ID.ToString() + ContentDispositionHeaderValue.Parse(files.ContentDisposition).FileName.Trim('"');
            var filePath = Path.Combine(_applicationEnvironment.ApplicationBasePath, "Photos", User.GetUserName(), folderName, fileName);
            await files.SaveAsAsync(filePath);

            photo.UserFolder = _context.UserFolder.Where(q => q.ID == id).Single();
            photo.Url = "~/Photos/" + fileName;

            _context.Add(photo);
            _context.SaveChanges();


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

The View:

    @model FamPhotos.Models.Photo

@{
    ViewData["Title"] = "Create";
}

<h2>Create</h2>

<form asp-action="Create" asp-controller="Photos" method="post" enctype="multipart/form-data">
    <div class="form-horizontal">
        <h4>Photo</h4>
        <hr />
        <div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
        <input type="file" name="files" />
        <label asp-for="Description" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="Description" class="form-control" />
            <span asp-validation-for="Description" class="text-danger" />
        </div>
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
</form>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
}

My DbContext:

   namespace FamPhotos.Models
{
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<UserFolder>()
                .HasMany(q => q.Photo)
                .WithOne(c => c.UserFolder)
                .HasForeignKey(c => c.UserFolderId);



            base.OnModelCreating(builder);
            // Customize the ASP.NET Identity model and override the defaults if needed.
            // For example, you can rename the ASP.NET Identity table names and more.
            // Add your customizations after calling base.OnModelCreating(builder);
        }
        public DbSet<Photo> Photo { get; set; }
        public DbSet<ApplicationUser> ApplicationUser { get; set; }
        public DbSet<UserFolder> UserFolder { get; set; }
    }
}

Thanks.

Dave D
  • 307
  • 1
  • 3
  • 8
  • 2
    You are confused by the error. IDENTITY_INSERT OFF means that the database does not allow you to insert and identity value (this is the normal condition when you have an identity column). You turn IDENTITY_INSERT ON in cases where you are, for instance, restoring a bunch of records that include identity keys and you wish to overwrite the key value in an update or specify it in an insert. ON means ALLOW you to insert an identity. – Erik Funkenbusch Jan 19 '16 at 21:17
  • Your OnModelCreating is incomplete. Look at the accepted answer of this question. http://stackoverflow.com/questions/15258571/performing-an-identity-insert-using-ef5-code-first-migrations – granadaCoder Jan 19 '16 at 21:32

2 Answers2

11

If you do not want an ID to be database generated, then you should use the DatabaseGenerated attribute on your model, as in

public class MyModel
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int ID {get;set;}
    ...
}

This attribute is in fact supported in EF7.

See https://learn.microsoft.com/en-us/ef/core/modeling/generated-properties

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • Is there another way to temporarily allow writing PK identity during a seeding of a table? – Adam Cox Apr 17 '18 at 21:08
  • @AdamCox - During your seed operation you can issue the command `context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT ON");` and then make sure to issue the OFF command when you are done (remember "ON" means you CAN insert, "OFF" means you can't)
    – Erik Funkenbusch Apr 17 '18 at 21:22
  • I did attempt this and was unable to find methods for `Database.ExecuteSqlCommand` on my EF Core 2.0.2 DbContext. Am I missing some extension? – Adam Cox Apr 17 '18 at 21:41
  • @AdamCox - Database is a property, not a method. https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.database?view=efcore-2.0#Microsoft_EntityFrameworkCore_DbContext_Database - ExecuteSqlCommand is an extension method in the RelationalDatabaseFacadeExtensions class. – Erik Funkenbusch Apr 17 '18 at 22:07
6

If you want to insert a primary key into the database don't have an Identity on the database Column.

The error message is stating that you are trying to choose a primary key, and the database wants to choose one for you.

Either turn the identity off or allow the database to choose the primary key.

Joshua Duxbury
  • 4,892
  • 4
  • 32
  • 51
  • Changed the ID to PhotoID and UserFolderID. I thought I had read that either one would do what I wanted. – Dave D Jan 19 '16 at 21:17