Home > Net >  Entity Framework setting foreign key, primary key violation
Entity Framework setting foreign key, primary key violation

Time:12-06

When trying to create a new database entry of type TestForm2 I include the related object Unit Type's ID as a foreign key, except when I perform context.SaveChanges() after adding the new model I get the following SQL exception:

SqlException: Violation of PRIMARY KEY constraint 'PK_dbo.UnitTypes'. Cannot insert duplicate key in object 'dbo.UnitTypes'. The duplicate key value is (2d911331-6083-4bba-a3ad-e50341a7b128). The statement has been terminated.

What this means to me is that it thinks that the foreign entry I'm trying to relate to the new model is instead a new object that it's attempting to insert into the UnitTypes table and failing because it sees an existing entry with the same primary key.

For context (pun not intended), this is my data context, the database model, and the erroring "Create" function.

public class DataContext : IdentityDbContext<ApplicationUser>
    {
        public DataContext() : base("DefaultConnection")
        {
        }
        public static DataContext Create()
        {
            return new DataContext();
        }
        public DbSet<SafetyIncident> SafetyIncidents { get; set; }
        public DbSet<ProductionLine> ProductionLines { get; set; }
        public DbSet<ProductionOrder> ProductionOrders { get; set; }
        public DbSet<SerialOrder> SerialOrder { get; set; }
        public DbSet<QualityError> QualityErrors { get; set; }
        public DbSet<PSA> PSAs { get; set; }
        public DbSet<TestStation> TestStations { get; set; }
        public DbSet<ProductionGoal> ProductionGoals { get; set; }
        public DbSet<DailyWorkStationCheck> DailyWorkStationChecks { get; set; }
        public DbSet<TestForm> TestForms { get; set; }
        public DbSet<User> AppUsers { get; set; }
        public DbSet<Options> Options { get; set; }
        public DbSet<DriveList> DriveSerials { get; set; }
        public DbSet<MRPController> MRPControllers { get; set; }
        public DbSet<TestOption> TestOptions { get; set; }
        public DbSet<UnitType> UnitTypes { get; set; }
        public DbSet<UnitTypeMap> UnitTypeMaps { get; set; }
        public DbSet<TestForm2> TestForm2s { get; set; }
        public DbSet<TestFormSection> TestFormSections { get; set; }
        public DbSet<TestFormSectionStep> TestFormSectionSteps { get; set; }
    }
public class TestForm2 : BaseEntity
    {
        public string SerialNumber { get; set; }
        public string MaterialNumber { get; set; }
        public string UnitTypeId { get; set; }
        public UnitType UnitType { get; set; }
        public bool UsesStandardOptions { get; set; }
        public bool OptionsVerified { get; set; } // This will only be used when UsesStandardOptions is true, otherwise its value doesn't matter
        public ICollection<TestOption> AllOptions { get; set; } // List of all options (at time of form creation)
        public ICollection<TestOption> Options { get; set; } // The options on a unit
        public ICollection<TestFormSection> Sections { get; set; }
    }
public FormViewModel Create(FormViewModel vm)
        {
            using (var context = new DataContext())
            {
                List<string> optionListStrings = GetOptionListForModelNumber(vm.MaterialNumber); // returns list of option codes
                List<TestOption> matchingOptions = context.TestOptions
                    .Where(optionInDb =>
                        optionListStrings.Any(trimOption => trimOption == optionInDb.OptionCode)).ToList();

                var unitType = context.UnitTypes.FirstOrDefault(x => x.Name == vm.UnitType);
                string unitTypeId = unitType.Id;

                TestForm2 newForm = new TestForm2
                {
                    // ID & CreatedAt instantiated by Base Entity constructor
                    SerialNumber = vm.SerialNumber,
                    MaterialNumber = vm.MaterialNumber,
                    UnitTypeId = unitType.Id,
                    UsesStandardOptions = vm.UsesStandardOptions,
                    OptionsVerified = vm.OptionsVerified,
                    //AllOptions = context.TestOptions.ToList(),
                    //Options = matchingOptions,
                    Sections = vm.Sections,
                };

                context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
                context.TestForm2s.Add(newForm);
                context.SaveChanges(); // THIS IS WHERE THE SQL EXCEPTION IS HAPPENING
 
                return vm;
            }

            return null;
        }

Lastly, I'm not sure if it's relevant, but a full copy of the related UnitType is viewable as part of newForm only after context.TestForm2s.add(newForm) resolves. This is weird to me since I don't think it should be automatically relating the data object like that.

I haven't been able to try much since everything looks properly configured to me. Please let me know if this is not the case or if I should include any other info.

CodePudding user response:

Found the issue. The vm.Sections was not using viewmodels to contain the section data, so the vm.Sections contained UnitType database models. Since this was instantiated in the controller (before opening the data context in the TestForm2 Create method) EF assumed that these data were new and needed to be added to the UnitType table.

Hope this thread helps someone else running into similar issues.

  • Related