简体   繁体   English

EF 6:插入多个-无法更改关系,因为一个或多个外键属性不可为空

[英]EF 6: inserting multiple - The relationship could not be changed because one or more of the foreign-key properties is non-nullable

I've run across this a few times in our system in the past, but haven't found the reasoning... 我过去在我们的系统中遇到过几次,但是没有找到理由……

In essence I have a Contact, and the contact has a nullable PhoneNumber. 本质上,我有一个联系人,并且该联系人具有可为空的PhoneNumber。

The code Loops through a list of View Models, maps them to the Data Models, then (after all models are mapped) calls SaveChanges(). 代码循环遍历视图模型列表,将它们映射到数据模型,然后(在映射所有模型之后)调用SaveChanges()。

The test data has the data for the phone number duplicated. 测试数据具有重复的电话号码数据。 When I alter the test data to have different phone numbers for each contact, everything saves fine. 当我将测试数据更改为每个联系人具有不同的电话号码时,一切都可以节省。

Here's a stripped down simple sample of what I'm doing that exhibits the behavior. 这是我正在做的事情的简化示例,展示了该行为。

class Program
{
    static void Main(string[] args)
    {
        TestWeirdEDMXIssue();
        Console.ReadLine();
    }

    static void TestWeirdEDMXIssue()
    {
        List<dynamic> works = new List<dynamic>
            {
                new { FirstName = "Fred", LastName = "Snider", PhoneNumber = "888-888-8881", CountryID = 1 },
                new { FirstName = "Ted", LastName = "Snider", PhoneNumber = "888-888-8882", CountryID = 1 },
                new { FirstName = "Mike", LastName = "Snider", PhoneNumber = "888-888-8883", CountryID = 1 },
                new { FirstName = "Tim", LastName = "Snider", PhoneNumber = "888-888-8884", CountryID = 1 },
                new { FirstName = "Todd", LastName = "Snider", PhoneNumber = "888-888-8885", CountryID = 1 },
                new { FirstName = "Terry", LastName = "Snider", PhoneNumber = "888-888-8886", CountryID = 1 }
            };


        List<dynamic> broken = new List<dynamic>
            {
                new { FirstName = "Fred", LastName = "Snider", PhoneNumber = "888-888-8888", CountryID = 1 },
                new { FirstName = "Ted", LastName = "Snider", PhoneNumber = "888-888-8888", CountryID = 1 },
                new { FirstName = "Mike", LastName = "Snider", PhoneNumber = "888-888-8888", CountryID = 1 },
                new { FirstName = "Tim", LastName = "Snider",  PhoneNumber = "888-888-8888", CountryID = 1 },
                new { FirstName = "Todd", LastName = "Snider", PhoneNumber = "888-888-8888", CountryID = 1 },
                new { FirstName = "Terry", LastName = "Snider", PhoneNumber = "888-888-8888", CountryID = 1 }
            };

        TestWeirdEDMXIssueSave(works); //Completes with "Success!"
        TestWeirdEDMXIssueSave(broken); //Throws Exception

    }

    static void TestWeirdEDMXIssueSave(List<dynamic> data)
    {
        try
        {

            using (var context = new DBEntities())
            {
                var creatorID = context.UserProfiles.FirstOrDefault(up =>  up.Contact.FirstName == "automationtestuser")?.ID  ?? Guid.Empty;
                foreach (var item in data)
                {
                    var contact = context.Contacts.Create();
                    context.Contacts.Add(contact);

                    contact.ID = Guid.NewGuid();
                    contact.FirstName = item.FirstName;
                    contact.LastName = item.LastName;
                    contact.CreationDate = DateTime.Now;
                    contact.CreatedBy = creatorID;


                    var phoneNumber = context.PhoneNumbers.Create();
                    context.PhoneNumbers.Add(phoneNumber);

                    //PhoneNumber ID is Identity
                    phoneNumber.CreatedBy = creatorID;
                    phoneNumber.CreationDate = DateTime.Now;
                    phoneNumber.TypeID = (int)PhoneNumberTypes.Office;
                    phoneNumber.Number = item.PhoneNumber;
                    phoneNumber.CountryID = item.CountryID;

                    contact.PhoneNumber = phoneNumber;
                }
                context.SaveChanges();
            }
            Console.WriteLine("Success!");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}

OUTPUT 输出值

Success!

System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
    at System.Data.Entity.Core.Objects.ObjectContext.PrepareToSaveChanges(SaveOptions options)
    at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
    at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(SaveOptions options)
    at System.Data.Entity.Internal.InternalContext.SaveChanges()
    at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
    at System.Data.Entity.DbContext.SaveChanges()
...(My Code)...

EDIT Here are the create scripts for the Tables (sql generated, so its a bit ugly): 编辑这里是表的创建脚本(生成的sql,因此有点难看):

Contact 联系

CREATE TABLE [Common].[Contacts](
    [ID] [uniqueidentifier] NOT NULL,
    [FirstName] [varchar](25) NOT NULL,
    [MiddleInitial] [char](1) NULL,
    [LastName] [varchar](50) NOT NULL,
    [Title] [varchar](50) NULL,
    [Description] [varchar](255) NULL,
    [ContactEmailAddress] [varchar](100) NULL,
    [ContactPhoneNumberID] [int] NULL,
    [ContactFaxNumberID] [int] NULL,
    [AddressID] [int] NULL,
    [ACHDate] [datetime] NULL,
    [CreatedBy] [uniqueidentifier] NOT NULL,
    [CreationDate] [datetime] NOT NULL,
    [IsMarkedAsDeleted] [bit] NOT NULL,
    [Position] [varchar](50) NULL,
    [MiddleName] [varchar](50) NULL,
    [LastThenFirstName]  AS (case when [LastName] IS NULL OR len(rtrim([LastName]))=(0) then [FirstName] when [FirstName] IS NULL OR len(rtrim([FirstName]))=(0) then [LastName] else (rtrim(ltrim([LastName]))+', ')+rtrim(ltrim([FirstName])) end) PERSISTED,
    [FirstThenLastName]  AS (rtrim(ltrim((isnull([FirstName],'')+' ')+isnull([LastName],'')))) PERSISTED,
    [AuthenticatorPin] [int] NULL,
    [AuthenticatorCode] [int] NULL,
 CONSTRAINT [PK_Contacts] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [Common].[Contacts] ADD  CONSTRAINT [DF_Contacts_ID]  DEFAULT (newid()) FOR [ID]
GO

ALTER TABLE [Common].[Contacts] ADD  CONSTRAINT [DF_Contacts_CreationDate]  DEFAULT (getdate()) FOR [CreationDate]
GO

ALTER TABLE [Common].[Contacts] ADD  CONSTRAINT [DF_Contacts_IsMarkedAsDeleted]  DEFAULT ((0)) FOR [IsMarkedAsDeleted]
GO

ALTER TABLE [Common].[Contacts]  WITH CHECK ADD  CONSTRAINT [FK_Contacts_AddressID_Addresses_ID] FOREIGN KEY([AddressID])
REFERENCES [Common].[Addresses] ([ID])
GO

ALTER TABLE [Common].[Contacts] CHECK CONSTRAINT [FK_Contacts_AddressID_Addresses_ID]
GO

ALTER TABLE [Common].[Contacts]  WITH CHECK ADD  CONSTRAINT [FK_Contacts_ContactFaxNumberID_PhoneNumbers_ID] FOREIGN KEY([ContactFaxNumberID])
REFERENCES [Common].[PhoneNumbers] ([ID])
GO

ALTER TABLE [Common].[Contacts] CHECK CONSTRAINT [FK_Contacts_ContactFaxNumberID_PhoneNumbers_ID]
GO

ALTER TABLE [Common].[Contacts]  WITH CHECK ADD  CONSTRAINT [FK_Contacts_ContactPhoneNumberID_PhoneNumbers_ID] FOREIGN KEY([ContactPhoneNumberID])
REFERENCES [Common].[PhoneNumbers] ([ID])
GO

ALTER TABLE [Common].[Contacts] CHECK CONSTRAINT [FK_Contacts_ContactPhoneNumberID_PhoneNumbers_ID]
GO

ALTER TABLE [Common].[Contacts]  WITH CHECK ADD  CONSTRAINT [FK_Contacts_CreatedBy_UserProfiles_ID] FOREIGN KEY([CreatedBy])
REFERENCES [Common].[UserProfiles] ([ID])
GO

ALTER TABLE [Common].[Contacts] CHECK CONSTRAINT [FK_Contacts_CreatedBy_UserProfiles_ID]
GO

Phone Numbers 电话号码

CREATE TABLE [Common].[PhoneNumbers](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Number] [varchar](20) NOT NULL,
    [Extension] [varchar](10) NULL,
    [TypeID] [int] NOT NULL,
    [CountryID] [int] NOT NULL,
    [CreatedBy] [uniqueidentifier] NOT NULL,
    [CreationDate] [datetime] NOT NULL,
    [IsMarkedAsDeleted] [bit] NOT NULL,
 CONSTRAINT [PK_PhoneNumbers] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [Common].[PhoneNumbers] ADD  CONSTRAINT [DF_PhoneNumbers_CreationDate]  DEFAULT (getdate()) FOR [CreationDate]
GO

ALTER TABLE [Common].[PhoneNumbers] ADD  CONSTRAINT [DF_PhoneNumbers_IsMarkedAsDeleted]  DEFAULT ((0)) FOR [IsMarkedAsDeleted]
GO

ALTER TABLE [Common].[PhoneNumbers]  WITH CHECK ADD  CONSTRAINT [FK_PhoneNumbers_CountryID_Countries_ID] FOREIGN KEY([CountryID])
REFERENCES [Common].[Countries] ([ID])
GO

ALTER TABLE [Common].[PhoneNumbers] CHECK CONSTRAINT [FK_PhoneNumbers_CountryID_Countries_ID]
GO

ALTER TABLE [Common].[PhoneNumbers]  WITH CHECK ADD  CONSTRAINT [FK_PhoneNumbers_CreatedBy_UserProfiles_ID] FOREIGN KEY([CreatedBy])
REFERENCES [Common].[UserProfiles] ([ID])
GO

ALTER TABLE [Common].[PhoneNumbers] CHECK CONSTRAINT [FK_PhoneNumbers_CreatedBy_UserProfiles_ID]
GO

ALTER TABLE [Common].[PhoneNumbers]  WITH CHECK ADD  CONSTRAINT [FK_PhoneNumbers_TypeID_PhoneNumberTypes_ID] FOREIGN KEY([TypeID])
REFERENCES [Common].[PhoneNumberTypes] ([ID])
GO

ALTER TABLE [Common].[PhoneNumbers] CHECK CONSTRAINT [FK_PhoneNumbers_TypeID_PhoneNumberTypes_ID]
GO

Depending on how your entities are mapped and the relationship between PhoneNumber and Contact, the data may not be persisting as you expect. 根据您的实体的映射方式以及PhoneNumber和Contact之间的关系,数据可能不会像您期望的那样持久。 Ideally you should use database generated IDs for all entities and use navigation properties to manage the related entities. 理想情况下,您应该为所有实体使用数据库生成的ID,并使用导航属性来管理相关实体。

Based on the examples, check that the schema has a PhoneNumberId column on Contact, and that it is a FK to the PhoneNumber table's PK. 根据示例,检查架构在Contact上是否具有PhoneNumberId列,并且是否为PhoneNumber表的PK的FK。 Also check that the relationship between contact and phone number is set up correctly. 还要检查联系人和电话号码之间的关系是否设置正确。 For instance either the entity type configuration or DbContext OnModelCreating you should have something like: 例如,实体类型配置或DbContext OnModelCreating您应该具有以下内容:

If your Contact entity declares a PhoneNumberId FK field: 如果您的联系人实体声明了PhoneNumberId FK字段:

HasRequired(x => x.PhoneNumber)
  .WithMany()
  .HasForeignKey(x => x.PhoneNumberId); 

or if the entity does not declare the FK field, but the table has the FK: 或如果实体未声明FK字段,但表具有FK:

HasRequired(x => x.PhoneNumber)
  .WithMany()
  .Map(x => x.MapKey("PhoneNumberId")); 

This forms a many-to-one relationship between contact and phone # which may not be intended since it is possible to use the same phone # record against multiple contacts. 这可能会在联系人和电话号码之间形成多对一关系,因为可能对多个联系人使用相同的电话号码记录,所以这可能是不希望的。 (2 contacts may share the same phone #, but changing the phone # on one should not necessarily update the # of the other.) For a 1-to-1 relationship the PhoneNumber table would best be served by using a ContactId as its PK. (2个联系人可以共享同一电话号码,但是在一个电话号码上更改电话号码不一定更新另一个电话号码。)对于一对一关系,最好通过使用ContactId作为其PK服务PhoneNumber表。 。

IF this relationship is not set up appropriately one way or the other, EF can end up inserting records in the wrong order, not recognizing the required FKs. 如果没有以一种或另一种方式适当地建立这种关系,则EF最终可能会以错误的顺序插入记录,而无法识别所需的FK。

In terms of populating your entities, you can be less deliberate with the entity creation and let EF manage the related data: 在填充实体方面,您可以减少实体创建的工作量,而让EF管理相关数据:

using (var context = new DBEntities())
{
  // A better way to select the ID rather than returning the entire entity:
  var creatorID = context.UserProfiles
    .Where(up =>  up.Contact.FirstName == "automationtestuser")
    .Select(up => up.ID)
    .SingleOrDefault();

  foreach (var item in data)
  {
    var contact = new Contact
    {
      //contact.ID = Guid.NewGuid(), Definitely recommend letting DB generate ID using NewSequentialId.
      FirstName = item.FirstName,
      LastName = item.LastName,
      CreationDate = DateTime.Now,
      CreatedBy = creatorID,

      PhoneNumber = new PhoneNumber 
      {
        CreatedBy = creatorID;
        CreationDate = DateTime.Now;
        TypeID = (int)PhoneNumberTypes.Office;
        Number = item.PhoneNumber;
        CountryID = item.CountryID;
      }
    }
    context.Contacts.Add(contact);
    context.SaveChanges(); 
  }
}

Note that we don't need to create or add phone #'s directly to the DbContext. 请注意,我们不需要直接将电话#创建或添加到DbContext。 EF will take care of that using the appropriate relationships and set the FKs for us. EF将使用适当的关系来解决此问题,并为我们设置FK。

This ensures that the related entities are inserted together and you can avoid adding a DbSet for every entity into your DbContext. 这样可以确保将相关实体插入在一起,并且可以避免将每个实体的DbSet添加到DbContext中。 (Do you need to search for PhoneNumbers as a top level entity?) (您是否需要搜索PhoneNumbers作为顶级实体?)

However, to fully determine what is going wrong you should also post any mapping declarations you have, whether code-first annotations, or schema first with entity type configurations or OnModelCreating overrides. 但是,要完全确定问题出在哪里,您还应该发布任何映射声明,无论是代码优先的注释,还是使用实体类型配置或OnModelCreating替代的模式优先。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 EF添加实体:由于一个或多个外键属性不可为空,因此无法更改关系 - EF adding entity: The relationship could not be changed because one or more of the foreign-key properties is non-nullable EF 6.1.1-无法更改关系,因为一个或多个外键属性不可为空 - EF 6.1.1 - The relationship could not be changed because one or more of the foreign-key properties is non-nullable 在EF6中,什么可能导致错误无法更改关系,因为一个或多个外键属性不可为空 - In EF6 what can cause the error The relationship could not be changed because one or more of the foreign-key properties is non-nullable EF SaveChanges方法抛出:由于一个或多个外键属性不可为空,因此无法更改关系 - EF SaveChanges method throws: The relationship could not be changed because one or more of the foreign-key properties is non-nullable 实体框架5无法更改该关系,因为一个或多个外键属性不可为空 - Entity Framework 5 The relationship could not be changed because one or more of the foreign-key properties is non-nullable 由于一个或多个外键属性不可为空,因此无法更改该关系 - The relationship could not be changed because one or more of the foreign-key properties is non-nullable Pocos-无法更改关系,因为一个或多个外键属性不可为空 - Pocos - The relationship could not be changed because one or more of the foreign-key properties is non-nullable 操作失败:由于一个或多个外键属性不可为空,因此无法更改该关系asdf - The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable, asdf 实体框架无法更改关系,因为一个或多个外键属性不可为空 - Entity Framework The relationship could not be changed because one or more of the foreign-key properties is non-nullable 操作失败:无法更改关系,因为一个或多个外键属性不可为空 - The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM