首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何打破实体之间的循环关联?

如何打破实体之间的循环关联?
EN

Stack Overflow用户
提问于 2009-03-05 18:27:54
回答 8查看 2.7K关注 0票数 13

我第一次在这个网站上,所以如果标签错误或在其他地方被回答了,我深表歉意…

我在目前的项目中不断遇到特殊的情况,我想知道你们会如何处理它。模式是:一个具有子级集合的父级,并且该父级具有对子集合中特定项的一个或多个引用,通常是“默认”子级。

一个更具体的例子:

代码语言:javascript
复制
public class SystemMenu 
{
    public IList<MenuItem> Items { get; private set; }
    public MenuItem DefaultItem { get; set; }
}

public class MenuItem
{
    public SystemMenu Parent { get; set; }
    public string Name { get; set; }
}

对我来说,这似乎是一种很好的清晰的关系建模方式,但由于循环关联,我无法在DB中强制执行关系,并且LINQ To SQL由于循环关联而崩溃。即使我可以绕过这个问题,这显然也不是一个好主意。

我目前唯一的想法是在MenuItem上有一个'IsDefault‘标志:

代码语言:javascript
复制
public class SystemMenu 
{
    public IList<MenuItem> Items { get; private set; }
    public MenuItem DefaultItem 
    {
        get 
        {
            return Items.Single(x => x.IsDefault);
        }
        set
        {
            DefaultItem.IsDefault = false;
            value.DefaultItem = true;
        }
    }
}

public class MenuItem
{
    public SystemMenu Parent { get; set; }
    public string Name { get; set; }
    public bool IsDefault { get; set; }
}

有没有人处理过类似的事情,并能提供一些建议?

干杯!

编辑:感谢到目前为止的回复,也许“菜单”的例子并不出色,但我正在努力想出一些有代表性的东西,这样我就不必进入我们不是很清楚的领域模型的细节了!也许一个更好的例子是公司/员工关系:

代码语言:javascript
复制
public class Company
{
    public string Name { get; set; }
    public IList<Employee> Employees { get; private set; }
    public Employee ContactPerson { get; set; }
}

public class Employee
{
    public Company EmployedBy { get; set; }
    public string FullName { get; set; }
}

员工肯定需要引用他们的公司,而且每个公司只能有一个ContactPerson。希望这能让我的观点更清晰一点!

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2009-03-09 14:15:54

感谢所有回答的人,一些非常有趣的方法!最后,我不得不匆忙地完成了一些事情,所以我想出了这个想法:

引入了名为WellKnownContact的第三个实体和相应的WellKnownContactType枚举:

代码语言:javascript
复制
public class Company
{
    public string Name { get; set; }
    public IList<Employee> Employees { get; private set; }
    private IList<WellKnownEmployee> WellKnownEmployees { get; private set; }
    public Employee ContactPerson
    {
        get
        {
            return WellKnownEmployees.SingleOrDefault(x => x.Type == WellKnownEmployeeType.ContactPerson);
        }
        set
        {                
            if (ContactPerson != null) 
            {
                // Remove existing WellKnownContact of type ContactPerson
            }

            // Add new WellKnownContact of type ContactPerson
        }
    }
}

public class Employee
{
    public Company EmployedBy { get; set; }
    public string FullName { get; set; }
}

public class WellKnownEmployee
{
    public Company Company { get; set; }
    public Employee Employee { get; set; }
    public WellKnownEmployeeType Type { get; set; }
}

public enum WellKnownEmployeeType
{
    Uninitialised,
    ContactPerson
}

它感觉有点麻烦,但绕过了循环引用的问题,并且干净地映射到DB上,这样就省去了试图让LINQ to SQL做任何太聪明的事情!还允许多种类型的“知名联系人”,这肯定会在下一个sprint中出现(所以不是真正的YAGNI!)。

有趣的是,一旦我提出了人为的Company/Employee示例,与我们真正处理的相当抽象的实体相比,它使思考变得更加容易。

票数 0
EN

Stack Overflow用户

发布于 2009-03-07 17:16:31

解决这个问题的诀窍是意识到父母不需要知道孩子的所有方法,孩子也不需要知道父母的所有方法。因此,您可以使用Interface Segregation Principle将它们解耦。

简而言之,您为父对象创建了一个接口,该接口只包含子对象所需的那些方法。您还可以为子对象创建一个接口,该接口只包含父对象所需的那些方法。然后让父接口包含子接口的列表,并且让子接口指向父接口。我称其为Flip Flob模式,因为UML图具有Eckles-Jordan触发器的几何形状(Sue me,我是一个老硬件工程师!)

代码语言:javascript
复制
  |ISystemMenu|<-+    +->|IMenuItem|
          A    1  \  / *     A
          |        \/        |
          |        /\        |
          |       /  \       |
          |      /    \      |
          |     /      \     |
    |SystemMenu|        |MenuItem|

请注意,此图中没有循环。您不能从一个类开始,然后沿着箭头回到您的起点。

有时,为了得到正确的分离,你必须移动一些方法。可能有一些你认为应该放在SystemMenu中的代码被转移到了MenuItem中,等等。但总的来说,这项技术运行得很好。

票数 25
EN

Stack Overflow用户

发布于 2009-03-05 18:42:58

你的解决方案看起来很合理。

另一件需要考虑的事情是,内存中的对象不必与数据库模式完全匹配。在数据库中,您可以使用具有子属性的更简单的模式,但在内存中,您可以进行优化,并使父对象具有对子对象的引用。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/615968

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档