什么是正确的对象关系? (C#)

时间:2009-11-25 01:45:08

标签: c# oop data-modeling

我有一个关于我应该为这种情况设置的正确对象关系的快速问题:

我有一个带有相关参数的Customer对象和一个带有相关参数的depot对象。每个仓库为一组客户提供服务,客户需要访问各自仓库的特定信息。

我想知道我应该建立什么样的正确关系,以便一组客户对象都引用特定软件仓库对象的相同实例。我想确保它没有为每个客户创建重复的Depot对象。此外,我希望能够在不通过客户本身的情况下更改仓库的属性。

我知道这可能是一个相当基本的问题,但C#有许多不同的“功能”,它会不时混淆。

感谢您的帮助!

  • 查理

4 个答案:

答案 0 :(得分:1)

如果我正确理解您的问题,我认为您的问题的解决方案可能是OR映射器。 Microsoft目前提供两个OR映射器,LINQ to SQL和Entity Framework。如果您使用的是.NET 3.5,我建议使用LINQ to SQL,但如果您能够使用.NET 4.0进行试验,我强烈建议您查看实体框架。 (我不鼓励在.NET 3.5中使用Entity Framework,因为它过早发布并且有很多问题。)

这两个OR映射器都提供了可视化建模工具,允许您构建概念实体模型。使用LINQ to SQL,您可以从数据库生成模型,该模型将为您提供实体类,以及这些类之间的关联(表示来自数据库模式的外键)。 LINQ to SQL框架将处理为您生成SQL查询,并自动将数据库查询结果映射到对象图。您描述的关系,以及引用同一个部门的集合中的多个客户的关系将自动为您处理,您根本不需要担心它们。您还可以使用LINQ查询数据库,并且可以避免编写大量存储过程和管道/映射代码。

如果您使用.NET 4.0,实体框架实际上就类固醇而言是LINQ to SQL。它支持LINQ to SQL所做的一切,还有更多功能。它支持模型驱动设计,允许您构建生成代码和数据库模式的概念模型。它支持更广泛的映射,提供更灵活的平台。它还提供了Entity SQL(eSQL),它是一种基于文本的查询语言,除了LINQ to Entities之外,还可用于查询模型。将LINQ转换为SQL,它将解决您用作示例的场景,以及许多其他场景。

OR映射器可以节省大量时间,金钱和精力,大大减少了与关系数据库交互所需的工作量。它们提供动态查询以及具有冲突解决的动态,乐观更新/插入/删除。

答案 1 :(得分:1)

这听起来像你有很多对多的关系。 (客户知道他们的仓库,反之亦然)

理想情况下,这似乎最适合您定义弱实体表的数据库应用程序... 如果我们谈论的是10个客户和10个仓库,那么当然使用数据库是过度的......

假设数据库过度,可以使用某些Dictionarys在代码中建模。假设您使用int作为Depot和Customer的唯一标识符,您可以创建如下内容:

// creating a derived class for readability.
public class DepotIDToListOfCustomerIDs : Dictionary<int,List<int>> {}
public class CustomerIDToListOfDepotIDs : Dictionary<int,List<int>> {}
public class DepotIDToDepotObject : Dictionary<int,Depot>{}
public class CustomerIDToCustomerObject : Dictionary<int, Customer>{}
//...
// class scope for a class that manages all these objects...
DepotIDToListOfCustomerIDs _d2cl = new DepotIDToListOfCustomerIDs();
CustomerIDToListOfDepotIDs _c2dl = new CustomerIDToListOfDepotIDs();
DepotIDToDepotObject _d2do = new DepotIDToDepotObject();
CustomerIDToCustomerObject _c2co = new CustomerIDToCustomerObject();
//...
// Populate all the lists with the cross referenced info.
//...
// in a method that needs to build a list of depots for a given customer
// param: Customer c
if (_c2dl.ContainsKey(c.ID))
{
    List<int> dids=_c2dl[c.ID];
    List<Depot> ds=new List<Depot>();
    foreach(int did in dids)
    {
        if (_d2do.ContainsKey(did))
            ds.Add(_d2do[did]);
    }
}
// building the list of customers for a Depot would be similar to the above code.

编辑1:请注意,使用上面的代码,我已经制作了它以避免循环引用。让客户引用也引用同一客户的软件仓库将防止这些快速垃圾收集。如果这些对象将在整个应用程序生命周期中持续存在,则可以采用更简单的方法。在该方法中,您有两个列表,一个是Customer实例,另一个是Depot实例列表。客户和仓库将分别包含仓库和客户的列表。但是,您仍需要两个词典才能解析客户的库存ID,反之亦然。结果代码将与上述代码相同99%。

编辑2: 正如其他回复中所述,您可以(并且应该)拥有一个对象代理模型,该模型可以建立关系并回答有关关系的问题。 对于那些误读我的代码的人;它绝不是为这种情况设计绝对和完整对象模型。 但是,它旨在说明对象代理如何以防止循环引用的方式管理这些关系。你对第一次出现的混乱感到抱歉。我要感谢他们展示了一个很容易被其他人消费的优秀OO演示文稿。

答案 2 :(得分:1)

回复@Jason D,并且为了@Nitax:我真的在略读表面,因为虽然它基本上很容易,但它也会变得复杂。我不可能比Martin Fowler更好地重写它(当然不是在10分钟内)。

首先必须解决内存中只有一个引用特定软件仓库的对象的问题。我们将通过称为存储库的东西实现这一目标。 CustomerRepositoryGetCustomer()方法,DepotRepositoryGetDepot()方法。我要挥挥手,假装刚刚发生。

其次,您需要编写一些测试来指示您希望代码如何工作。我无法知道,但无论如何都要忍受我。

// sample code for how we access customers and depots
Customer customer = Repositories.CustomerRepository.GetCustomer("Bob");
Depot depot = Repositories.DepotRepository.GetDepot("Texas SW 17");

现在,困难的部分是:想如何建立关系模型?在OO系统中,您实际上不需要做任何事情。在C#中我可以执行以下操作。

客户保留他们所用的仓库列表

class Customer
{
    public IList<Depot> Depots { get { return _depotList; } }
}

或者,Depots会保留他们所在客户的列表

class Depot
{
    public IList<Customer> Customers { get { return _customerList; } }
}
// * code is very brief to illustrate.

在最基本的形式中,任意数量的客户都可以参考任意数量的仓库。 m:n已解决。参考文献在OO中很便宜。

请注意,我们遇到的问题是,虽然客户可以保留一份对其所关注的所有仓库的引用列表(第一个示例),但是仓库并不是一种简单的方法来枚举所有客户。

要获取仓库的所有客户列表(第一个示例),我们必须编写迭代所有客户的代码并检查客户.Depots属性:

List<Customer> CustomersForDepot(Depot depot)
{
    List<Customer> allCustomers = Repositories.CustomerRepository.AllCustomers();
    List<Customer> customersForDepot = new List<Customer>();

    foreach( Customer customer in allCustomers )
    {
        if( customer.Depots.Contains(depot) )
        {
            customersForDepot.Add(customer);
        }
    }
    return customersForDepot;
}

如果我们使用Linq,我们可以将其写为

var depotQuery = from o in allCustomers
                 where o.Depots.Contains(depot)
                 select o;

return query.ToList();

将10,000,000个客户存储在数据库中? 哎哟!每次仓库需要确定其客户时,您真的不想加载所有10,000,000个客户。另一方面,如果您只有10个Depot,那么查询加载所有Depots一次并不是什么大问题。 您应该始终考虑您的数据和数据访问策略。

我们可以CustomerDepot中都有列表。当我们这样做时,我们必须小心实施。添加或删除关联时,我们需要立即对两个列表进行更改。否则,我们让客户认为他们与仓库相关联,但仓库对客户一无所知。

如果我们不喜欢这样,并且决定我们不需要如此紧密地耦合对象。我们可以删除显式List并引入第三个对象,它只是关系(还包括另一个存储库)。

class CustomerDepotAssociation
{
    public Customer { get; }
    public Depot { get; }
}

class CustomerDepotAssociationRepository
{
    IList<Customer> GetCustomersFor(Depot depot) ...
    IList<Depot> GetDepotsFor(Customer customer) ...
    void Associate(Depot depot, Customer customer) ...
    void DeAssociate(Depot depot, Customer customer) ...
}

这是另一种选择。该关联的存储库不需要公开它如何将客户与Depot相关联(顺便说一下,从我所知道的,这是@Jason D的代码试图做的事情)

我可能更喜欢这个实例中的单独对象,因为我们所说的是Customer和Depot的关联本身就是一个实体。

请继续阅读一些领域驱动设计书籍,并购买Martin Fowlers PoEAA(企业应用程序架构模式)

答案 3 :(得分:0)

希望这是不言自明的。

<强> OO

depot_model_01

<强> ER

depot_model_02