Search code examples
c#sqlasp.net-mvcentity-frameworkef-database-first

primary key mvc entitityframework model error


This one's driving me nuts.

I have a table with several columns and a Primary Key (bigint, not null)

I have VS create a model of it

public long ParcelRangeID { get; set; }
..
..

Then I create Controller and View. When I use the Create View to create a new record, on db.SaveChanges the troubles start: (I have included the field in my view as HiddenFor)

  • With no further changes; I get a Required ID is missing error. (also when I use the DataAnnotations [Key], [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] and [HiddenInput(DisplayValue = false)]
  • When I use [Bind(Exclude="ParcelRangeID")] i get the DbUpdateConcurrencyException.

How can I insert a new record and have the required ID generated automatically without the error?

Edit

Here's the controller code:

    [HttpPost]
            [ValidateAntiForgeryToken]
            public ActionResult Create(ParcelRange parcelrange)
            {
                if (ModelState.IsValid)
                {
                    db.ParcelRange.Add(parcelrange);
                    db.SaveChanges();
                    return RedirectToAction("Index");
                }

                return View(parcelrange);
            }

And the model:

 public partial class ParcelRange
    {
        public ParcelRange()
        {
            this.PickupAddressID = -1;
            this.OrganizationID = -1;
            this.DepotID = -1;
            this.IsVTG = 0;
            this.IsInternal = 0;
            this.IsInterCompany = 0;
            this.UpdateCount = 0;
            this.IsActive = 1;
            this.Modified = DateTime.Now.Date;
            this.Created = DateTime.Now.Date;
            this.ValidFrom = DateTime.Now.Date;
            this.ValidTo = DateTime.MaxValue;
        }

        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        [Key]
        [HiddenInput(DisplayValue = false)]
        public long ParcelRangeID { get; set; }
        public long NdswCustomerID { get; set; }
        public long PickupAddressID { get; set; }
        public long OrganizationID { get; set; }
        public long DepotID { get; set; }
        public string ParcelRangePrefix { get; set; }
        public string ParcelNumberStart { get; set; }
        public string ParcelNumberEnd { get; set; }
        public byte IsVTG { get; set; }
        public byte IsInternal { get; set; }
        public byte IsInterCompany { get; set; }
        public byte[] RowVer { get; set; }
        public System.DateTime Created { get; set; }
        public System.DateTime Modified { get; set; }
        public int UpdateCount { get; set; }
        public System.DateTime ValidFrom { get; set; }
        public System.DateTime ValidTo { get; set; }
        public byte IsActive { get; set; }
    }

Edit (2)

And finally the (default) view autogenerated

@model MvcApplication1.ParcelRange

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
    @Html.HiddenFor(model => model.ParcelRangeID)

    <fieldset>
        <legend>ParcelRange</legend>

        <div class="editor-label">

            @Html.LabelFor(model => model.NdswCustomerID)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.NdswCustomerID)
            @Html.ValidationMessageFor(model => model.NdswCustomerID)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.PickupAddressID)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.PickupAddressID)
            @Html.ValidationMessageFor(model => model.PickupAddressID)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.OrganizationID)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.OrganizationID)
            @Html.ValidationMessageFor(model => model.OrganizationID)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.DepotID)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.DepotID)
            @Html.ValidationMessageFor(model => model.DepotID)
        </div>

@*        <div class="editor-label">
            @Html.LabelFor(model => model.ParcelRangePrefix)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ParcelRangePrefix)
            @Html.ValidationMessageFor(model => model.ParcelRangePrefix)
        </div>*@

        <div class="editor-label">
            @Html.LabelFor(model => model.ParcelNumberStart)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ParcelNumberStart)
            @Html.ValidationMessageFor(model => model.ParcelNumberStart)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ParcelNumberEnd)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ParcelNumberEnd)
            @Html.ValidationMessageFor(model => model.ParcelNumberEnd)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.IsVTG)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.IsVTG)
            @Html.ValidationMessageFor(model => model.IsVTG)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.IsInternal)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.IsInternal)
            @Html.ValidationMessageFor(model => model.IsInternal)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.IsInterCompany)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.IsInterCompany)
            @Html.ValidationMessageFor(model => model.IsInterCompany)
        </div>

@*        <div class="editor-label">
            @Html.LabelFor(model => model.Created)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Created)
            @Html.ValidationMessageFor(model => model.Created)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Modified)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Modified)
            @Html.ValidationMessageFor(model => model.Modified)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.UpdateCount)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.UpdateCount)
            @Html.ValidationMessageFor(model => model.UpdateCount)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ValidFrom)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ValidFrom)
            @Html.ValidationMessageFor(model => model.ValidFrom)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ValidTo)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ValidTo)
            @Html.ValidationMessageFor(model => model.ValidTo)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.IsActive)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.IsActive)
            @Html.ValidationMessageFor(model => model.IsActive)
        </div>*@

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

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

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

Edit (3) Database script for the table:

USE [DPDCDB_ODS]
GO

/****** Object:  Table [dbo].[ParcelRange]    Script Date: 16-6-2014 15:50:40 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[ParcelRange](
    [ParcelRangeID] [bigint] IDENTITY(1,1) NOT NULL,
    [NdswCustomerID] [bigint] NOT NULL,
    [PickupAddressID] [bigint] NOT NULL,
    [OrganizationID] [bigint] NOT NULL,
    [DepotID] [bigint] NOT NULL,
    [ParcelRangePrefix]  AS (left([ParcelNumberStart],(4))),
    [ParcelNumberStart] [nvarchar](50) NOT NULL,
    [ParcelNumberEnd] [nvarchar](50) NOT NULL,
    [IsVTG] [tinyint] NOT NULL,
    [IsInternal] [tinyint] NOT NULL,
    [IsInterCompany] [tinyint] NOT NULL,
    [RowVer] [timestamp] NOT NULL,
    [Created] [datetime] NOT NULL,
    [Modified] [datetime] NOT NULL,
    [UpdateCount] [int] NOT NULL,
    [ValidFrom] [date] NOT NULL,
    [ValidTo] [date] NOT NULL,
    [IsActive] [tinyint] NOT NULL,
 CONSTRAINT [PK_ParcelRange] PRIMARY KEY CLUSTERED 
(
    [ParcelRangeID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [DATA]
) ON [DATA]

GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT ((-1)) FOR [NdswCustomerID]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT ((-1)) FOR [PickupAddressID]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT ((-1)) FOR [OrganizationID]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT ((-1)) FOR [DepotID]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT ((0)) FOR [IsVTG]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT ((0)) FOR [IsInternal]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT ((0)) FOR [IsInterCompany]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT (getdate()) FOR [Created]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT (getdate()) FOR [Modified]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT ((0)) FOR [UpdateCount]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT (getdate()) FOR [ValidFrom]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT (CONVERT([date],'9999-12-31')) FOR [ValidTo]
GO

ALTER TABLE [dbo].[ParcelRange] ADD  DEFAULT ((1)) FOR [IsActive]
GO

Edit (4)

I'm using Entity Framework 5.0, will try to update to a higher version.

Edit (5) Updated to EF 6.1, same problem This is my connectionstring:

metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=XXX.XXX.XXX.XXX\DEV;initial catalog=DPDCDB_ODS;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient

Solution

  • "(I have included the field in my view as HiddenFor)"

    As it is a database-generated primary key, in a create view it has no initial value and you should not be using a HiddenFor to hold that property. Just delete that HiddenFor.

    If you use the scaffolding wizards to create EF controllers you should note the primary key is never present in the create views.

    Update: http://robjoosentest.azurewebsites.net/parcelranges

    I took the table script provided, created a database, exported it to Azure, created a VS 2013 MVC web application and then the EF model-based controller and views for the table. I then published it to Azure and it works just fine (well done Microsoft, let's see anyone do that with Amazon S3 in 5 mins flat!) :). I suspect it is the version of one of your libraries (maybe EF version). Otherwise it may be a side effect of other database activity on your web app.