【问题标题】:Code First two objects having other object代码前两个对象有另一个对象
【发布时间】:2013-10-17 06:56:24
【问题描述】:

首先使用实体​​框架代码我有这个:

public class Person {
    public int PersonId {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}
    public virtual List<Note> Notes {get;set;}
}

public class Product {
    public int ProductId {get;set;}
    public string Name {get;set;}
    public virtual List<Note> Notes {get;set;}
}

public class Note {
    public int NoteId {get;set;}
    public string Body {get;set;}
    public virtual User Author {get;set;}
    // what should be here ?
}

我如何知道注释是来自 Person 还是 Product ? 我应该为此需要不同的类(PersonNote、ProductNote)吗? 我可以使用接口方法吗?喜欢 INoteable 吗?

我不确定要建模的数据库或类的最佳策略是什么。 任何建议表示赞赏。

谢谢,

编辑(基于答案)

建议的答案意味着对于每个有注释的实体,表格都会有一个我不太喜欢的新列。有没有办法使用鉴别器?我不喜欢每次新实体可以有注释时都修改表的想法,如果我有 10 个支持注释的实体,则可能最终得到 10 个 FK(9 个始终为空)。

理想情况下,我希望拥有类似接口(或其他)的东西,这样我就可以在代码中拥有 Product : INoteable ,这意味着可以使用此 id 创建便笺。 也许有一个鉴别器列。这有可能吗?

抱歉第一次没看清楚。

在现实生活场景中,我喜欢:产品、人员、采购订单、销售、付款、付款订单以及其他一些我需要实现注释的实体。

编辑 2:

这个数据库结构怎么样:

TABLE: Note
int PK NoteId
int FK NoteDataId
string Body

TABLE: Person
int PK PersonId 
int FK NoteDataId

TABLE: Product
int PK ProductId
int FK NoteDataId

TABLE: NoteData
int PK NoteDataId

使用这种数据结构,所有想要实现 Notes 的实体我只需添加一个导航属性 NoteDataId 并且在创建便笺时,我只需给出 NoteDataId 值。如果 NoteDataId 行不存在,我认为 EF 会负责创建它。

编辑 3:

示例数据:

Person:
PersonId 1
Name Bart
NoteDataId 1

PersonId 2
Name Alex
NoteDataId 2

Product:
ProductId 1
Name "Dulce de Leche"
NoteDataId 3

NoteData:
NoteDataId 1
NoteDataId 2
NoteDataId 3

Note:
NoteId 1
NoteDataId 1
Body "first note"

NoteId 2
NoteDataId 1
Body "second note of Bart"

NoteId 3
NoteDataId 2
Body "first note of Alex"

NoteId 4
NoteDataId 3
Body "Note about Dulce de Leche"

如何获取某人的笔记?

SELECT * FROM Note
JOIN NoteData USING (NoteDataId)
JOIN Person USING (NoteDataId)
WHERE PersonId = 1

向后?

SELECT * FROM Note
JOIN NoteData USING (NoteDataId)
LEFT JOIN Person USING (NoteDataId) 'can be null, only one type exists'
LEFT JOIN Product using (NoteDataId) 'can be null, only one type exists'
WHERE NoteId = 2

【问题讨论】:

  • 你是先做代码,所以你是从代码的角度思考。但我认为您应该问问自己,您希望如何在数据库中表示它。考虑架构,然后回到代码中,看看应该如何完成。
  • @AKhudairy 我更新了一个可能的数据结构。不知道好不好。
  • 每个人可以有一个笔记?。我不知道什么适合您的业务需求。我会添加两个连接表,一个是 Prodcut_Note,一个是 Person_Note,以启用表之间的多对多关系。我们在另一个站点有类似的情况,其中 dba 选择将一个列映射到两个表,但在这种情况下,您将无法拥有外键约束,我不喜欢这种方法
  • 每个人可以有n条笔记。看起来你没有很好地阅读结构。
  • 哦,我现在明白了(我没有看到您使用 NoteData 的去向)...好的,请查看我的回复帖子,并使用下面的示例代码

标签: c# asp.net-mvc database entity-framework ef-code-first


【解决方案1】:

创建可为空的外键:

public class Note {
    public int NoteId {get;set;}
    public string Body {get;set;}
    public virtual User Author {get;set;}

    public int? PersonId { get; set; }
    public virtual Person Person { get; set; }

    public int? ProductId { get; set; }
    public virtual Product Product { get; set; }
}

您必须将属性映射为可选。

Note 的配置:

Property(n => n.PersonId).IsOptional();
Property(n => n.ProductId).IsOptional();

Person 和 Product 的配置:

Property(p => p.Note).IsOptional();


更新:

您也可以使用table-per-hierarchy 解决方案。

让Note 保持不变(不妨使其抽象化)并创建派生类型。例如PersonNote:

public clas PersonNote : Note 
{   
    public int PersonId { get; set; }
    public virtual Person Person { get; set; }
}

Person 获取导航属性的位置:

public virtual PersonNote Note { get; set; }

还有ProductNote:

public clas ProductNote : Note 
{   
    public int ProductId { get; set; }
    public virtual Product Product { get; set; }
}

Product 获取导航属性的位置:

public virtual ProductNote Note { get; set; }

Entity Framework 将创建一个Note 表,其中包含所有派生类型的属性和一个discriminator 列。但这会导致你的代码中有很多类,但在我看来这并不是一件坏事。


更新 2

如果不需要,您也可以将导航属性从 Note 释放到 Product 或 Person。这将使您的代码更简单。您可以保留 Note 原样,并将导航和外键属性添加到您的其他实体:

public class Person
{
    // properties..

    public int NoteId { get; set; }

    public virtual Note Note { get; set; }
}

【讨论】:

  • 用更多问题编辑了我的问题:D 如果您介意看一看,我将不胜感激。
  • 我明白了,这更像是我正在寻找的。谢谢。
  • 我更新了可能针对数据库结构的问题。让我知道 EF 是否可以这样做,以及这种方法是否有任何缺点。
  • @Bart 使用您建议的数据结构,您没有 Note 到 Product 或 Person 的导航属性。如果您不需要它,没关系,并且使您的代码更简单。但它并不代表我在答案的第二部分中提出的按层次结构表类型。
  • @Bart 我再次更新了我的答案。这代表您建议的数据结构。
【解决方案2】:

已编辑:全面考虑。这并没有解决第一个问题,我们只是添加了另一个表。为什么不添加一个映射到枚举的注释类型列。


根据您上次的编辑,这就是我将如何编写代码

public class Person {
    public int PersonId {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}

    public int NoteData NoteDataId{get;set;}

    [ForeignKey("NoteDataId")]
    public virtual NoteData NoteData{get;set;}
}

public class Product {
    public int ProductId {get;set;}
    public string Name {get;set;}

    public int NoteData NoteDataId{get;set;}

    [ForeignKey("NoteDataId")]
    public virtual NoteData NoteData{get;set;}
}

public class NoteData {
    public int NoteDataID {get;set;}

    public virtual List<Note> Notes {get;set;}
}    

public class Note {
    public int NoteId {get;set;}
    public string Body {get;set;}

    public int NoteData NoteDataId{get;set;}

    [ForeignKey("NoteDataId")]
    public virtual NoteData NoteData{get;set;}
}

【讨论】:

  • 我明白你关于不解决第一个问题的观点。我询问了有关使用鉴别器(枚举节点类型)的问题。我不知道如何在 EF 中使用它进行导航。我认为这是解决所有这些混乱的可行解决方案 :D 想要感谢您的所有时间!
  • 是的,我不认为枚举会给你导航特权
  • 这是一个高质量的答案
【解决方案3】:

我不确定它如何用于 EF,但在我们的模型中,我们使用了一个通用的“父”属性来返回一个对象。这仅适用于您只有一个对象拥有该笔记且引用该笔记的对象不多时。

public virtual object Parent { get; set; }

但是,我认为 EF 不会自动为您填充这些内容...

【讨论】:

  • 有趣的方法,不要以为我可以用 EF 设置这个场景。您如何处理导航属性?
  • 抱歉,我无法提供 EF 导航属性。
猜你喜欢
  • 1970-01-01
  • 2012-07-18
  • 2018-10-10
  • 2015-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多