gpt4 book ai didi

entity-framework - EF Code First 4.1 - 如何配置默认的一对多关系

转载 作者:行者123 更新时间:2023-12-04 14:51:24 24 4
gpt4 key购买 nike

我有一个引用地址集合的客户实体。这里的复杂之处在于我希望能够将特定地址识别为默认地址。

如果可能,我想在 Customer 表中保存默认地址的 FK。这似乎比在地址表中有一列来标识默认值更优雅。

在定义这种关系方面,我在使用 fluent API 时遇到了困难。当我运行以下代码时,我收到一个异常,内容为:
“保存未为其关系公开外键属性的实体时发生错误。EntityEntries 属性将返回 null,因为无法将单个实体识别为异常源。通过公开外键可以更轻松地处理保存时的异常实体类型中的关键属性。有关详细信息,请参阅 InnerException。”
“无法确定相关操作的有效顺序。由于外键约束、模型要求或存储生成的值,可能存在依赖关系。”

我创建了一个控制台应用程序来显示确切的问题。在这个测试应用程序中,我有一个客户实体、一个地址和灵活的 api 配置。

任何帮助将非常感激:

using System;
using System.Collections.Generic;
using System.Data.Entity.ModelConfiguration;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;

namespace OneToManyWithDefault
{

public class Customer
{
private ICollection<Address> m_Addresses;

public Customer()
{
Addresses = new List<Address>();
}

public int Id { get; set; }
public string CompanyName { get; set; }
public virtual ICollection<Address> Addresses
{
get
{
if (m_Addresses == null)
{
m_Addresses = new List<Address>();
}
return m_Addresses;
}
set
{
m_Addresses = value;
}
}
public Address DefaultAddress { get; set; }
public int DefaultAddressId { get; set; }

}

public class Address
{
public int Id { get; set; }
public string Town { get; set; }
public Customer Customer { get; set; }
}

public class MyContext
: DbContext
{
public DbSet<Customer> Customers { get; set; }

public MyContext(string connectionString)
: base(connectionString)
{

}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new CustomerConfiguration());
modelBuilder.Configurations.Add(new AddressConfiguration());
base.OnModelCreating(modelBuilder);
}
}

public class CustomerConfiguration
: EntityTypeConfiguration<Customer>
{
public CustomerConfiguration()
: base()
{
HasKey(p => p.Id);
Property(p => p.Id)
.HasColumnName("Id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.IsRequired();
Property(p => p.CompanyName)
.HasColumnName("Name")
.IsRequired();

// Configure the mapping for the Default Address (this is likely to be wrong!):
HasRequired(p => p.DefaultAddress).WithMany()
.Map(x => x.MapKey("DefaultAddressId"))
.WillCascadeOnDelete(false);
HasRequired(p => p.DefaultAddress)
.WithMany()
.HasForeignKey(x => x.DefaultAddressId);

ToTable("Customers");
}
}

public class AddressConfiguration
: EntityTypeConfiguration<Address>
{
public AddressConfiguration()
: base()
{
HasKey(p => p.Id);
Property(p => p.Id)
.HasColumnName("Id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.IsRequired();
Property(p => p.Town)
.HasColumnName("Town")
.IsRequired();

HasRequired(p => p.Customer)
.WithMany(c => c.Addresses)
.Map(x => x.MapKey("CustomerId"));

ToTable("Addresses");
}
}

class Program
{
private const string ConnectionString =
@"Server=.\sql2005;Database=OneToManyWithDefault;integrated security=SSPI;";

static void Main(string[] args)
{
Customer headOffice = new Customer();
headOffice.CompanyName = "C1";

Address address = new Address();
address.Town = "Colchester";
headOffice.Addresses.Add(address);

address = new Address();
address.Town = "Norwich";
headOffice.Addresses.Add(address);
headOffice.DefaultAddress = address;

MyContext context = new MyContext(ConnectionString);
context.Customers.Add(headOffice);
context.SaveChanges();

Console.WriteLine("Done.");
Console.ReadLine();
}
}
}

非常感谢,

保罗。

最佳答案

我不明白 EF 在那里谈论异常中的“未公开外键”是什么意思。我认为内部异常是重要的部分:

Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.



我认为您的模型中的问题在于您在 Customer 之间存在相互依赖关系。和 Address :一个地址需要一个客户(您在映射代码中将其标记为 required ),另一方面,客户需要一个地址(默认地址是 required 两者都是由于不可为空的外键,并且由于您的映射代码)。因此,EF 不知道在您的示例代码中首先保存哪个实体 - 默认地址还是客户?两个实体都需要使用有效的 FK 约束保存另一个实体的主键。

我能看到的最简单的解决方案是在您的模型中将默认地址设为可选,然后保存两次(我省略了按惯例工作的映射):
public class Customer
{
private ICollection<Address> m_Addresses;

public Customer() { Addresses = new List<Address>(); }

public int Id { get; set; }
public string CompanyName { get; set; }
public virtual ICollection<Address> Addresses { get { ... } set { ... } }
public Address DefaultAddress { get; set; }
public int? DefaultAddressId { get; set; } // FK for optional relationship
}

public class Address
{
public int Id { get; set; }
public string Town { get; set; }
public Customer Customer { get; set; }
}

// ...

public class CustomerConfiguration : EntityTypeConfiguration<Customer>
{
public CustomerConfiguration() : base()
{
Property(p => p.CompanyName)
.HasColumnName("Name")
.IsRequired();

HasMany(c => c.Addresses)
.WithRequired(a => a.Customer)
.Map(x => x.MapKey("CustomerId"));
}
}

public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration() : base()
{
Property(p => p.Town)
.HasColumnName("Town")
.IsRequired();
}
}

然后你的程序看起来像这样:
static void Main(string[] args)
{
Customer headOffice = new Customer();
headOffice.CompanyName = "C1";

Address address = new Address();
address.Town = "Colchester";
headOffice.Addresses.Add(address);

address = new Address();
address.Town = "Norwich";
headOffice.Addresses.Add(address);

//headOffice.DefaultAddress = address;
//We don't set the default address here as SaveChanges would throw an
//exception. But because it is optional now we are allowed to leave it null.

MyContext context = new MyContext(ConnectionString);
context.Customers.Add(headOffice);
context.SaveChanges();

headOffice.DefaultAddress = address; // headoffice and address have now PKs
context.SaveChanges(); // Updates headoffice in the DB with default address
}

这双 SaveChanges是丑陋的,但我没有看到另一种方式。

关于entity-framework - EF Code First 4.1 - 如何配置默认的一对多关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5936880/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com