2

像这样将存储库接口注入实体对象是否违反了持久性知识。通过不使用界面,我清楚地看到了问题,但是使用界面时真的有问题吗?下面的代码是好的还是坏的模式,为什么?

public class Contact
{
    private readonly IAddressRepository _addressRepository;

    public Contact(IAddressRepository addressRepository)
    {
        _addressRepository = addressRepository;
    }

    private IEnumerable<Address> _addressBook;
    public IEnumerable<Address> AddressBook
    {
        get
        {
            if(_addressBook == null)
            {
               _addressBook = _addressRepository.GetAddresses(this.Id);
            }
            return _addressBook;
        }
    }
}
4

4 回答 4

1

这不是一个好主意,但在某些有限的情况下可能没问题。我对您的模型有点困惑,因为我很难相信 Address 是您的聚合根,因此拥有一个完整的地址存储库并不常见。根据您的示例,您可能实际上使用的是表数据网关或 dao 而不是存储库。

我更喜欢使用数据映射器来解决这个问题(ORM 或类似的解决方案)。基本上,我会利用我的 ORM 将地址簿视为聚合根“联系人”的延迟加载属性。这样做的好处是,只要实体绑定到会话,就可以保存您的更改。

如果我没有使用 ORM,我仍然希望具体的联系人存储库实现设置 AddressBook 后备存储(列表或其他)的属性。我可能将存储库设置为一个代理对象,该对象确实知道其他数据存储,并按需加载它。

于 2010-07-15T18:00:44.980 回答
0

自从我提出这个问题以来已经有 2 年了,而且这个问题有点被误解了,我将尝试自己回答。

改写的问题:“业务实体类应该完全不了解持久性吗?”

我认为实体类应该完全不知道持久性,因为您将在代码库中的许多地方实例化它们,因此总是必须将 Repository 类注入实体构造函数很快就会变得混乱,而且看起来也不是很干净。如果您需要注入多个存储库,这将变得更加明显。因此,我总是使用单独的处理程序/服务类来为实体执行持久性工作。这些类的实例化频率要低得多,您通常可以更好地控制这种情况发生的地点和时间。实体类尽可能保持轻量级。

我现在总是有 1 个 Repository pr 聚合根,如果在从存储库中获取实体时需要一些额外的业务逻辑,我通常会为聚合根创建 1 个 ServiceClass。

通过对问题中的代码进行调整示例,因为这是一个不好的示例,我现在会这样做:

代替:

public class Contact
{
    private readonly IContactRepository _contactRepository;

    public Contact(IContactRepository contactRepository)
    {
        _contactRepository = contactRepository;
    }

    public void Save()
    {
        _contactRepository.Save(this);
    }
}

我这样做:

public class Contact
{

}

public class ContactService
{
    private readonly IContactRepository _contactRepository;

    public ContactService(IContactRepository contactRepository)
    {
        _contactRepository = contactRepository;
    }

    public void Save(Contact contact)
    {
        _contactRepository.Save(contact);
    }
}
于 2013-02-22T15:39:51.020 回答
0

您可以从外部注入加载功能。.NET 4.0 中的新Lazy<T>类型为此派上了用场:

    public Contact(Lazy<IEnumerable<Address>> addressBook)
    {
        _addressBook = addressBook;
    }

    private Lazy<IEnumerable<Address>> _addressBook;
    public IEnumerable<Address> AddressBook
    {
        get { return this._addressBook.Value; }
    }

另请注意,IEnumerable<T>当您从查询提供程序获取 s 时,它们可能本质上是惰性的。但是对于任何其他类型,您可以使用Lazy<T>.

于 2010-07-29T15:05:46.587 回答
0

通常,当您遵循 DDD 时,您总是使用整个聚合进行操作。存储库始终为您返回一个完全加载的聚合根。

像您的示例中那样编写代码没有多大意义(至少在 DDD 中)。Contact 聚合将始终包含所有地址(如果它的行为需要它们,我怀疑这是诚实的)。

因此,通常 ContactRepository 假设为您构建整个 Contact 聚合,其中 Address 是一个实体,或者很可能是该聚合内的一个值对象。

因为 Address 是一个实体/值对象,属于(因此由其管理)Contact 聚合,所以它不会有自己的存储库,因为您不应该管理属于此聚合之外的聚合的实体。

Resume:总是加载整个 Contact 并调用它的行为方法来处理它的状态。

于 2013-02-22T13:47:03.567 回答