【问题标题】:Entity Framework - Inheritance - Zero to one Relationship to child object, how to map? (Fluent API)实体框架 - 继承 - 与子对象的零对一关系,如何映射? (流畅的 API)
【发布时间】:2012-01-07 08:27:54
【问题描述】:

我有一个继承层次结构,其中 Action 是 ActionCompleted 和 ActionCancelled 的父级。 Order 类有一个从零到一的 ActionCompleted 和 ActionCancelled。我已经尝试过 TPH 和 TPT(甚至尝试过 edmx)但无法让实体理解订单和子操作之间的这种关系,请建议我如何映射?

//Classes
    public class Order
    {
        public   int OrderId { get; set; }
        public string Name { get; set; }
        public   ActionCompleted ACO { get; set; }
        public ActionCancelled ACA { get; set; }

    }

public class Action
{
    public   int ActionID { get; set; }
    public   DateTime ActionDT { get; set; }
    public   Order Order { get; set; }
}

public class ActionCompleted : Action
{

}
public class ActionCancelled : Action
{
    public int CancelledByPhysician { get; set; }
}

//映射

public class EDISContext:DbContext
{
    public EDISContext()
        : base("EDISContext")
    { }


    public DbSet<Order> Orders { get; set; }

    public DbSet<Action> Actions { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>().ToTable("Orders");

        modelBuilder.Entity<Action>().HasKey(a => a.ActionID);

        modelBuilder.Entity<Action>().Map(m => m.ToTable("Actions"))
                                              .Map<ActionCompleted>(m => m.ToTable("ActionCompleted"))
                                              .Map<ActionCancelled>(m => m.ToTable("ActionCancelled"));

        //modelBuilder.Entity<Action>().HasRequired(a => a.Order).WithOptional().Map(m => m.MapKey("DiagOrderId"));
        modelBuilder.Entity<ActionCompleted>().HasRequired(a => a.Order).WithOptional().Map(m => m.MapKey("DiagOrderId"));
        modelBuilder.Entity<ActionCancelled>().HasRequired(a => a.Order).WithOptional().Map(m => m.MapKey("DiagOrderId"));

    }

}

用于保存数据的代码:

EDISContext db = new EDISContext();
var ord1 = db.Orders.FirstOrDefault(o => o.OrderId == 1);
ActionCompleted ac= new ActionCompleted();
ac.ActionDT = DateTime.Now;
ac.Order = ord1;
db.Actions.Add(ac);
db.SaveChanges();

数据库表脚本:

CREATE TABLE [dbo].[Orders](
    [OrderId] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](50) NULL,
) ON [PRIMARY]

CREATE TABLE [dbo].[Actions](
    [ActionID] [int] IDENTITY(1,1) NOT NULL,
    [ActionDT] [datetime] NOT NULL,
    [DiagOrderID] [int] NULL,
) ON [PRIMARY]

CREATE TABLE [dbo].[ActionCompleted](
    [ACID] [int] IDENTITY(1,1) NOT NULL,
    [ActionID] [int] NOT NULL,
) ON [PRIMARY]

CREATE TABLE [dbo].[ActionCancelled](
    [ACAID] [int] IDENTITY(1,1) NOT NULL,
    [ActionID] [int] NOT NULL,
    [CancelledByPhysician] [int] NULL,
 ) ON [PRIMARY]

【问题讨论】:

  • 我知道我越来越不耐烦了,但我想知道是否正在使用实体框架?可能我需要开始研究 Nhibernate。
  • 您能否阐明“无法让实体理解订单和子操作之间的这种关系”的意思?如果您创建订单和操作并尝试将它们保存到数据库中,会发生什么?我将您的代码复制并粘贴到控制台应用程序中,它运行良好。
  • 我认为它会抛出类似于下面的异常:Invalid column name 'ActionCompleted_ActionId'。\r\nInvalid column name 'ActionCompleted_ActionId ---- 只是要清楚 Orders 表与 Actions 表相关,与OrderId 作为外键存储在 Action 表中。 (orders 表中没有引用键,这是您创建表的方式吗?)----如果您不介意,请发布您创建的表的脚本,我将再次尝试我的代码。我会尽快发布我的表格脚本。
  • 另一件事:我在添加操作时使用现有订单,我设置了他们的订单属性,见下文: EDISContext db = new EDISContext(); var ord1 = db.Orders.FirstOrDefault(o => o.OrderId == 1); OrderActionEntered ae1 = new OrderActionEntered(); ae1.ActionDoneBy=1; ae1.ActionDT =日期时间。现在; ae1.Order = ord1; db.OrderActions.Add(ae1); db.SaveChanges();

标签: inheritance entity-framework-4 entity-relationship fluent-interface


【解决方案1】:

如果您坚持自己创建和维护数据库,我建议您尝试使用 Database First 工作流程,而不是 Code First。从您的数据库创建实体数据模型,然后生成 POCO。它会让您的生活更轻松,尤其是在您更改数据库架构的情况下。

至于EDM要理解表关系,必须在数据库中设置。我不知道你是否已经这样做了,因为你没有粘贴任何创建外键的脚本,但是 VS 在理解正确定义的模式时应该没有任何问题。

【讨论】:

  • 我想分别创建我的 POCOS 和 DB,然后使用 ORM 映射它们。但我会先尝试做 DB,看看是否创建了正确的 POCOS。顺便说一句,即使在我的另一篇文章中的 Nhibernate 中,我也无法得到这个问题的答案。
【解决方案2】:

首先使用代码时,至少尝试让 EF 为您创建数据库总是有用的 - 这是默认行为(将查找名为 .\SQLEXPRESS 的本地 SQL Server 实例)。如果您手动创建数据库并想要控制列名,这似乎正在发生,您需要使用 fluent API 指定列名。请参阅 http://msdn.microsoft.com/en-us/library/hh295845(v=vs.103).aspxhttp://msdn.microsoft.com/en-us/library/hh295847(v=VS.103).aspx 了解一些应该涵盖您遇到的问题的示例。

仅供参考,您创建的数据库将具有以下表和列名:

CREATE TABLE [dbo].[ActionCancelled](
    [ActionID] [int] NOT NULL,
    [CancelledByPhysician] [int] NOT NULL,
PRIMARY KEY CLUSTERED ( [ActionID] ASC )
)

CREATE TABLE [dbo].[ActionCompleted](
    [ActionID] [int] NOT NULL,
PRIMARY KEY CLUSTERED ( [ActionID] ASC )
)

CREATE TABLE [dbo].[Actions](
    [ActionID] [int] IDENTITY(1,1) NOT NULL,
    [ActionDT] [datetime] NOT NULL,
    [Order_OrderId] [int] NULL,
PRIMARY KEY CLUSTERED ( [ActionID] ASC )
)

CREATE TABLE [dbo].[Orders](
    [OrderId] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](max) NULL,
    [ACO_ActionID] [int] NULL,
    [ACA_ActionID] [int] NULL,
PRIMARY KEY CLUSTERED ( [OrderId] ASC )
)

【讨论】:

  • 我不明白为什么我需要提供这些列。如果我正在寻找 ActionCompleted 属性,可以通过转到 Actions 表并加入 ActionCompleted 来访问这些属性。我什至尝试了 TPH 映射,但它也没有工作,同样的错误。 link
  • 我看到的所有示例最终都以某种形式的集合属性与父类建立关系。但对我来说,我正在寻找的解决方案看起来很自然,并且使用自定义数据库编码很容易实现。现在我将尝试使用 Nhibernate,看看我是否会有一些运气。当我在订单表中有 ActionId 时,为什么要将 OrderId 作为引用键存储在操作表中,似乎对象世界的双向关系正在被推入关系世界。
  • 第二次查看我的数据库脚本并没有错。它在每个表中都有冗余的唯一 ID。在列名与属性名不同的地方,我使用 fluent api 来表示相同。我会尝试在没有顺序的情况下再次测试这段代码,看看它是否仍然有效。
  • 如果我在订单类中有 15 个类型为 action 的属性会怎样。并且 Order 应该有零到一个,我知道我可以使用一组动作从数据库中获取并在 getter 中编写一些逻辑来获取这些单独的属性。
  • 不确定我是否理解您的所有问题。回复:为什么要将 OrderId 存储在操作表中;您不会必须,但如果您不这样做,您将需要在 Orders 中的 *ActionId 列上建立索引,否则您将进行表扫描以查找给定 Action 的 Order。回复:15 处房产;在这种情况下,Order 将有 15 个可为空的 *ActionId 列。当然还有其他有效的相同模式表示,但是用于定义数据库模式的算法经过了很好的测试。 EF 生成的数据库的性能与我能识别的任何替代方案相似。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多