我有一个关于我应该为这种情况设置的正确对象关系的快速问题:
我有一个带有相关参数的Customer对象和一个带有相关参数的depot对象。每个仓库为一组客户提供服务,客户需要访问各自仓库的特定信息。
我想知道我应该建立什么样的正确关系,以便一组客户对象都引用特定软件仓库对象的相同实例。我想确保它没有为每个客户创建重复的Depot对象。此外,我希望能够在不通过客户本身的情况下更改仓库的属性。
我知道这可能是一个相当基本的问题,但C#有许多不同的“功能”,它会不时混淆。
感谢您的帮助!
答案 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分钟内)。
首先必须解决内存中只有一个引用特定软件仓库的对象的问题。我们将通过称为存储库的东西实现这一目标。 CustomerRepository
有GetCustomer()
方法,DepotRepository
有GetDepot()
方法。我要挥挥手,假装刚刚发生。
其次,您需要编写一些测试来指示您希望代码如何工作。我无法知道,但无论如何都要忍受我。
// 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一次并不是什么大问题。 您应该始终考虑您的数据和数据访问策略。
我们可以在Customer
和Depot
中都有列表。当我们这样做时,我们必须小心实施。添加或删除关联时,我们需要立即对两个列表进行更改。否则,我们让客户认为他们与仓库相关联,但仓库对客户一无所知。
如果我们不喜欢这样,并且决定我们不需要如此紧密地耦合对象。我们可以删除显式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)