c# - Entity Framework/code first/table-per-type inheritance——实现派生类和具体类之间的一对多关联

标签 c# entity-framework inheritance ef-code-first table-per-type

我在 VS2012 中使用 MVC4,并且我遵循一种表类型继承方法。我正在尝试使用 Seed() 方法将数据添加到我的数据库。

我有以下类(class):

房东

[Table("Landlord")]
public class Landlord : UserProfile
{
    // A Landlord can have many ResidentialProperties
    [ForeignKey("ResidentialPropertyId")]
    public virtual ICollection<ResidentialProperty> ResidentialProperties { get; set; }
}

住宅属性(property)

[Table("ResidentialProperty")]
public class ResidentialProperty
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int ResidentialPropertyId { get; set; }

    // A ResidentialProperty has 1 Landlord
    public virtual int UserId { get; set; }
    public virtual UserProfile UserProfile { get; set; } 

}

因此Landlord继承自UserProfile,因此Landlord中没有PK属性。

我要实现的关系如下:

  • 一个房东可以拥有多个ResidentialProperties (1:n)
  • ResidentialProperty 可以(在应用程序的范围内)有一个 Landlord (1:1)

我认为我配置模型的方式足以从包管理器成功运行 update-database 命令,并添加我的 Seed() 我的数据库的方法。事实并非如此,我收到以下错误:

Unable to determine the principal end of the 'LetLord.Models.ResidentialProperty_UserProfile' relationship. Multiple added entities may have the same primary key.

在阅读之后我意识到我想要创建的关系需要使用 Fluent API 来实现,所以我尝试了以下方法:

        modelBuilder.Entity<Landlord>()
            .HasMany(x => x.ResidentialProperties)
            .WithOptional()
            .HasForeignKey(x => x.ResidentialPropertyId);

        modelBuilder.Entity<ResidentialProperty>()
            .HasRequired(x => x.UserProfile);

当我尝试通过包管理器执行 update-database 命令时,出现以下错误:

\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'Landlord_ResidentialProperties_Target' in relationship 'Landlord_ResidentialProperties'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.

我完全不知道如何解决我的问题。我花了很多时间在这上面,这很令人沮丧,因为我觉得这应该很容易完成。如果有人有解决方案,请与我分享,我将不胜感激。

编辑 - 上述第一个错误抛出的异常输出:

System.Data.Entity.Infrastructure.DbUpdateException: Unable to determine the principal end of the 'LetLord.Models.ResidentialProperty_Landlord' relationship. Multiple added entities may have the same primary key. ---> System.Data.UpdateException: Unable to determine the principal end of the 'LetLord.Models.ResidentialProperty_Landlord' relationship. Multiple added entities may have the same primary key. at System.Data.Mapping.Update.Internal.UpdateTranslator.RegisterEntityReferentialConstraints(IEntityStateEntry stateEntry, Boolean currentValues) at System.Data.Mapping.Update.Internal.UpdateTranslator.RegisterReferentialConstraints(IEntityStateEntry stateEntry) at System.Data.Mapping.Update.Internal.UpdateTranslator.PullModifiedEntriesFromStateManager() at System.Data.Mapping.Update.Internal.UpdateTranslator.ProduceCommands() at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache) at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options) at System.Data.Entity.Internal.InternalContext.SaveChanges() --- End of inner exception stack trace --- at System.Data.Entity.Internal.InternalContext.SaveChanges() at System.Data.Entity.Internal.LazyInternalContext.SaveChanges() at System.Data.Entity.DbContext.SaveChanges() at LetLord.Migrations.Configuration.Seed(LetLordContext context) in c:\Users\Home\Desktop\LetLord\LetLord\Migrations\Configuration.cs:line 83

编辑 - 解决方案:

实体应该按照下面 Ladislav 的回答,我的错误是在基类的多边设置导航属性,而不是派生类。

我的下一个错误是在我的 Seed() 方法中对实体进行排序。我在下面添加了它们应该是什么样的,这尤其适用于添加具有关联的实体。

    protected override void Seed(LetLord.Models.LetLordContext context)
    {

        try
        {

            #region Landlords

            // create a list of landlords first - a landlord can have many residential properties, make this  = new List<ResidentialProperty>()
            var landlords = new List<Landlord>
        {
            // landlord 1
            new Landlord { UserName="Frank",     FirstName="Frank",   LastName="O'Sullivan", Email="a@a.com", AccountType=(int)AccountType.Landlord, ResidentialProperties = new List<ResidentialProperty>() }

            // ...
        };
            landlords.ForEach(l => context.UserProfile.AddOrUpdate(l));
            context.SaveChanges();

            #endregion

            #region ResidentialProperties

            // then create a list of properties
            var residentialProperties = new List<ResidentialProperty>
        {
            // Property 1 associated with LandLord 1
            new ResidentialProperty { PropTypeValue=(int)PropertyType.Detached, Description="Some description...", NumberOfBedrooms="4", NumberOfReceptionRooms="2", NumberOfBathrooms="2", HasBackGarden=true, HasFrontGarden=true, HasSecureParking=false, IsDisabledFriendly=false, DateAdded=DateTime.Now, Landlord = landlords.FirstOrDefault(u => u.UserId == 11) } // in my case, user 11 is landlord 1

            // ... 
        };
            residentialProperties.ForEach(rp => context.ResidentialProperty.AddOrUpdate(rp));
            context.SaveChanges(); 

            #endregion

            // then add our list of properties to our list of landlords
            landlords[0].ResidentialProperties.Add(residentialProperties[0]);
            landlords[1].ResidentialProperties.Add(residentialProperties[1]);
            landlords[2].ResidentialProperties.Add(residentialProperties[2]);
            landlords[3].ResidentialProperties.Add(residentialProperties[3]);
            landlords[4].ResidentialProperties.Add(residentialProperties[4]);
            landlords[5].ResidentialProperties.Add(residentialProperties[5]);
            landlords[6].ResidentialProperties.Add(residentialProperties[6]);
            landlords[7].ResidentialProperties.Add(residentialProperties[7]);
            landlords[8].ResidentialProperties.Add(residentialProperties[8]);
            landlords[9].ResidentialProperties.Add(residentialProperties[9]);
            context.SaveChanges();

        }
        catch (Exception ex)
        {
            string lines = ex.ToString();
            System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\Users\Home\Desktop\error.txt");
            file.WriteLine(lines);
            file.Close();
        }

当我在修复上述代码后尝试运行 update-database 时,我遇到了这个错误:

System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.ResidentialProperty_dbo.Landlord_UserId". The conflict occurred in database "C:\USERS\HOME\DESKTOP\LETLORD\LETLORD\APP_DATA\LETLORD.MDF", table "dbo.Landlord", column 'UserId'. The statement has been terminated.

快速搜索后,我发现这个错误是(我认为)造成的,因为我的数据库中已经有数据。删除并重新创建数据库解决了这个问题,当我再次运行 Seed() 方法时,数据已成功添加。

最佳答案

如果房东可以有多个属性并且属性可以有多个一个房东模型应该是这样的:

[Table("Landlord")]
public class Landlord : UserProfile
{
    // A Landlord can have many ResidentialProperties
    public virtual ICollection<ResidentialProperty> ResidentialProperties { get; set; }
}

[Table("ResidentialProperty")]
public class ResidentialProperty
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int ResidentialPropertyId { get; set; }

    // A ResidentialProperty has 1 Landlord
    // Foreign key is on many side and it contains value of primary key on one side
    // In your case FK must contain value of property's landlord
    public virtual int UserId { get; set; }
    // ForeignKey attribute pairs navigation property on many side with foreign key propety 
    [ForeignKey("UserId")]
    public virtual Landlord Landlord { get; set; } 
}

关于c# - Entity Framework/code first/table-per-type inheritance——实现派生类和具体类之间的一对多关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14940907/

相关文章:

php - 避免违反LSP

c++ - 以不止一种方式向上转换为作为基类的 C++ 类(无需使用虚拟继承)

c# - 为什么 SortedSet<T>.GetViewBetween 不是 O(log N)?

C#:存储对对象中数组位置的引用

c# - 对异常执行操作并返回到上次访问的页面

sql - 如何使用 Entity Framework 编写复杂的SQL查询?

Python 继承输出

c# - 无法从 pg_dump.exe 命令行退出

entity-framework - Entityframework 使用 join 方法和 lambda 进行连接

performance - 为什么DbSet.Add工作这么慢?