实体框架 - 一对一 - ReferentialConstraint映射到商店生成的列

时间:2018-04-24 22:01:58

标签: c# .net sql-server entity-framework entity-framework-6

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

  

ReferentialConstraint映射到存储生成的列。柱:   '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>

我需要做些什么来改变这项工作?

备注:

  • 我使用的是EF 6.2和.NET 4.7.1。
  • 我继承了此数据库架构,并且无法更改一对一关系。
  • 我试图避免显式创建一个事务,只需调用SaveChanges(),而不是先包装一个单独的调用来创建Customer,然后是Account。

1 个答案:

答案 0 :(得分:4)

按照惯例,EF6代表使用所谓的Shared Primary Key Association的一对一关系,其中从属实体的PK也作为主要实体的FK。

在您的情况下,它会将Account.Id视为FK到Customer,并且由于它是自动生成的,因此您会收到相关的异常。

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

因此,您需要从模型中删除AccountId属性,并仅保留导航属性。此外,尽管不是非常必要,但最好遵循命名约定,只需将其称为Account而不是AccountValue

换句话说,替换

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

public virtual Customer CustomerValue { get; set; }

public virtual Customer Customer { get; set; }

可以使用MapKey流畅的API指定FK列名称:

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

你已经完成了。

现在,以下内容正确插入新的Customer,然后引用新的Account

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