【问题标题】:How to cast base class to derived class using Entity Framework如何使用实体框架将基类转换为派生类
【发布时间】:2019-04-15 08:49:31
【问题描述】:

我正在尝试使用实体框架将基对象转换为派生类对象。

我尝试使用复制构造函数。构造函数本身确实可以工作,但框架会抛出错误。

模型类:

public class Member
{
    [Key]
    public string SRU { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public virtual Address Address { get; set; }

}

public class Player : Member
{
    public virtual PlayerPosition Position { get; set; }
    public virtual Doctor Doctor { get; set; }
    public virtual HealthIssue HealthIssue { get; set; }

    public Player()
    {

    }
    public Player(Member m)
    {
        SRU = m.SRU;
        Name = m.Name;
        Email = m.Email;
        Address = m.Address;
    }
}

我的尝试:

DatabaseModel db = new DatabaseModel();

var val = db.Members
            .FirstOrDefault(b => b.Name == "Frank");

val = new Player(val);
db.Members.Update(val);

// Save changes in database
db.SaveChanges();

我希望更新此实体,但它会引发错误:

System.InvalidOperationException: '无法跟踪实体类型'Player'的实例,因为另一个具有相同键值的实例 {'SRU'} 已被跟踪。

附加现有实体时,请确保仅附加一个具有给定键值的实体实例。

编辑:

我试过了,但我改了名字,但类型还是一样。

DatabaseModel db = new DatabaseModel();

var val = db.Members
            .FirstOrDefault(b => b.Name == "Yordan");
db.Entry(val).State = EntityState.Detached;

var val2 = new Player(val);
val2.Name = "Frank";

db.Members.Update(val2);
db.SaveChanges();

所以我得到了会员和播放器扩展会员。在这种情况下,我想将 Member 转换为 Player。

【问题讨论】:

  • 首先,这似乎是 EF Core,所以请更新标签。其次,这听起来像是设计不当或 XY 问题。 val 已经是 Player 并且不清楚您要更新什么,或者是另一个 Member 派生类型,在这种情况下它无法转换为 Player。请描述一下你到底想做什么。
  • 需要将成员更改(扩展)为播放器,通常在编程中使用复制构造函数,但 EF 不允许我这样做。
  • 这不是正常的编程,因为您选择将所有 Member 派生类型存储在一个表 (TPH database inheritance strategy) 中,SRU 是主键,所以它是唯一的。这就是 EF 错误消息试图告诉您的内容。
  • 好的,我明白了。这个数据库首先是由代码完成的。知道如何重新设计它吗?或如何更改Discriminator

标签: asp.net-mvc entity-framework


【解决方案1】:

val 您尝试更新的对象是新对象,它与您从数据库中检索到的键具有相同的键。由于密钥必须是唯一的,因此您不能使用相同的密钥跟踪两个对象。 先尝试分离旧对象:

DatabaseModel db = new DatabaseModel();
var val = db.Members
        .FirstOrDefault(b => b.Name == "Frank");

var val2 = new Player(val);
db.Entry(val).State = EntityState.Detached;
db.Members.Update(val2);

db.SaveChanges();

【讨论】:

  • 它停止抛出错误但类型仍然相同,即使 VS 显示我通过 player 类型。
【解决方案2】:

我从另一边接近这个主题并创建了我自己的数据库并使用从现有数据库中生成上下文。

结果,我收到了这样一个上下文模式

        public virtual DbSet<Member> Members { get; set; }
        public virtual DbSet<Player> Players { get; set; }
        public virtual DbSet<Senior> Seniors { get; set; }

以及三个生成的模型

public partial class Member
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public virtual Player Player { get; set; }
    }
public partial class Player
    {
        public int Id { get; set; }
        public string Position { get; set; }

        public virtual Member Member { get; set; }
        public virtual Senior Senior { get; set; }
    }
public partial class Senior
    {
        public int Id { get; set; }
        public string Kin { get; set; }

        public virtual Player Player { get; set; }
    }

这是解决此问题的唯一方法,还是有任何替代方案或经过验证的结构。我不知道这个解决方案是否好,因为我是凭直觉做到的。一方面它是有意义的,因为所有部分都有一个共同的密钥,并且关系是 1-1,就像在继承中一样。

编辑:

总结:我发现来自EF Core的信息不支持这种解决方案,唯一的选择是使用存储过程或手动更改鉴别器,如果我们使用一个表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-08
    • 2013-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-18
    相关资源
    最近更新 更多