实体框架 - 一对一 - ReferentialConstraint 映射到存储生成的列 [英] Entity Framework - One-to-One - ReferentialConstraint is mapped to a store-generated column

查看:19
本文介绍了实体框架 - 一对一 - ReferentialConstraint 映射到存储生成的列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我应该在 EF 中创建一个简单的一对一关系.但是当我尝试插入时收到以下错误:

I have what should be a simple one-to-one relationship to create within EF. But I'm receiving the following error when I try to insert:

ReferentialConstraint 映射到存储生成的列.柱子:'ACCOUNT_ID'.

ReferentialConstraint is mapped to a store-generated column. Column: 'ACCOUNT_ID'.

控制台应用示例:

namespace EF_ConsoleApp_Test
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var account = new Account
            {
                AccountNumber = "00123456",
                CustomerValue = new Customer { FirstName = "Joe" }
            };

            using (var db = new MainContext())
            {
                db.Accounts.Add(account);
                db.SaveChanges();
            }
        }
    }

    [Serializable]
    [Table("CUSTOMERS")]
    public class Customer
    {
        [Key]
        [Column("CUSTOMER_ID")]
        public int? Id { get; set; }

        [Required]
        [Column("FIRST_NAME")]
        [StringLength(45)]
        public string FirstName { get; set; }

        public virtual Account Account { get; set; }

        public Customer() { }
    }

    [Serializable]
    [Table("ACCOUNTS")]
    public class Account
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        [Column("ACCOUNT_ID")]
        public int? Id { get; set; }

        [Required]
        [Column("ACCOUNT_NUMBER")]
        [Display(Name = "Account Number")]
        [StringLength(16)]
        public string AccountNumber { get; set; }

        [Column("CUSTOMER_ID")]
        public int? CustomerId { get; set; }

        public virtual Customer CustomerValue { get; set; }

        /// <summary>
        /// Default Constructor
        /// </summary>
        public Account() { }
    }

    internal class MainContext : DbContext
    {
        internal MainContext() : base("name=DB.Context")
        {
            Database.SetInitializer<MainContext>(null);
        }

        public virtual DbSet<Account> Accounts { get; set; }

        public virtual DbSet<Customer> Customers { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Configure FK
            modelBuilder.Entity<Customer>()
                .HasRequired(c => c.Account)
                .WithRequiredPrincipal(a => a.CustomerValue);

            base.OnModelCreating(modelBuilder);
        }
    }
}

数据库表创建语句:

CREATE TABLE [dbo].[CUSTOMERS](
    [CUSTOMER_ID] [INT] IDENTITY(1,1) NOT NULL,
    [FIRST_NAME] [varchar](45) NOT NULL,
 CONSTRAINT [PK_CUSTOMERS] PRIMARY KEY CLUSTERED 
(
    [CUSTOMER_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[ACCOUNTS](
    [ACCOUNT_ID] [INT] IDENTITY(1,1) NOT NULL,
    [CUSTOMER_ID] [int] NOT NULL,
    [ACCOUNT_NUMBER] [varchar](16) NOT NULL,
 CONSTRAINT [PK_ACCOUNTS] PRIMARY KEY CLUSTERED 
(
    [ACCOUNT_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [dbo].[ACCOUNTS]  WITH CHECK ADD  CONSTRAINT [FK_ACCOUNTS_CUSTOMERS] FOREIGN KEY([CUSTOMER_ID])
REFERENCES [dbo].[CUSTOMERS] ([CUSTOMER_ID])
GO

App.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
  </startup>

  <connectionStrings>
    <add name="DB.Context"
         connectionString="data source=localdb;initial catalog=EF_TEST;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework;Connection Timeout=30;encrypt=true;trustServerCertificate=true;"
         providerName="System.Data.SqlClient" />
  </connectionStrings>

  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="mssqllocaldb" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>

</configuration>

我需要进行哪些更改才能使其正常工作?

What do I need to change to make this work?

注意事项:

  • 我使用的是 EF 6.2 和 .NET 4.7.1.
  • 我继承了这个数据库架构,无法更改一对一的关系.
  • 我试图通过仅调用 SaveChanges() 一次来避免显式创建事务,而不是包装一个单独的调用以首先创建客户,然后是帐户.

推荐答案

按照约定,EF6 使用所谓的 共享主键关联,其中依赖实体的PK也作为FK 到主要实体.

By convention EF6 represents the one-to-one relationships using the so called Shared Primary Key Association, where the PK of the dependent entity also serves as FK to the principal entity.

在您的情况下,它认为 Account.IdCustomer 的 FK,并且由于它是自动生成的,因此您会遇到有问题的异常.

In your case, it considers Account.Id to be the FK to Customer, and since it's auto-generated, you get the exception in question.

另外一个问题是 EF6 不支持具有显式 FK 属性的一对一关系(没有类似于一对多关系的 HasForeignKey fluent API).

The additional problem is that EF6 does not support one-to-one relationship with explicit FK property (there is no HasForeignKey fluent API similar to one-to-many relationships).

所以你需要从模型中移除 AccountId 属性,只留下导航属性.此外,虽然不是非常必要,但最好遵循命名约定并将其命名为 Account 而不是 AccountValue.

So you need to remove the AccountId property from the model and leave only the navigation property. Also, although not strongly necessary, it would be good to follow the naming conventions and just call it Account rather than AccountValue.

换句话说,替换

[Column("CUSTOMER_ID")]
public int? CustomerId { get; set; }

public virtual Customer CustomerValue { get; set; }

public virtual Customer Customer { get; set; }

FK 列名可以使用 MapKey fluent API 指定:

The FK column name can be specified using the MapKey fluent API:

modelBuilder.Entity<Customer>()
    .HasRequired(c => c.Account)
    .WithRequiredPrincipal(a => a.Customer)
    .Map(m => m.MapKey("CUSTOMER_ID")); // <--

你就完成了.

现在下面正确地首先插入一个新的 Customer,然后是一个引用它的新 Account:

Now the following correctly inserts first a new Customer and then a new Account referencing it:

var account = new Account
{
    AccountNumber = "00123456",
    Customer = new Customer { FirstName = "Joe" }
};
db.Accounts.Add(account);
db.SaveChanges();

这篇关于实体框架 - 一对一 - ReferentialConstraint 映射到存储生成的列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆