c# - Entity Framework - 一对一 - ReferentialConstraint 映射到存储生成的列

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

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

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>

我需要更改什么才能使这项工作正常进行?

注意事项:

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

最佳答案

按照惯例,EF6 使用所谓的 Shared Primary Key Association 表示一对一关系,其中依赖实体的 PK 也用作主体实体的 FK。

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

另一个问题是 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();

关于c# - Entity Framework - 一对一 - ReferentialConstraint 映射到存储生成的列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50011305/

相关文章:

c# - 如何对嵌套的 LINQ 提供的集合中的项目进行排序

c# - Entity Framework :Using Transaction Scope how to check whether DbContext has transaction?

c# - 使用弹性数据库客户端库、EF 和 Identity 时找不到 DbContext

sql - 帮助用sql比较两个列表

sql-server - 如何返回具有最小和最大列值的行

c# - 为什么我们不能在没有装箱的情况下覆盖值类型中的 Equals()?

c# - 使用 .Net 应用程序连接到 HIVE

c# - 播放声音 channel

sql - 从一个表中选择行并插入到另一表中

c# - 将大文件上传到 sitecore 媒体库