C# 如何为抽象关联建模?
假设我有以下接口:C# 如何为抽象关联建模?,c#,entity-framework,oop,design-patterns,orm,C#,Entity Framework,Oop,Design Patterns,Orm,假设我有以下接口: //abstract entity public class Order { int Amount { get; set; } } //concrete entity public class OnlineOrder : Order { string Website { get; set; } } //concrete entity class PhoneOrder : Order { string CallCenter { get; set; }
//abstract entity
public class Order
{
int Amount { get; set; }
}
//concrete entity
public class OnlineOrder : Order
{
string Website { get; set; }
}
//concrete entity
class PhoneOrder : Order
{
string CallCenter { get; set; }
}
//abstract entity
abstract class Customer
{
string City { get; set; }
List<Order> Orders { get; set; }
}
//concrete entity
class OnlineCustomer : Customer
{
IEnumerable<OnlineOrder> OnlineOrders { get; set; }
}
//concrete entity
class PhoneCustomer : Customer
{
IEnumerable<PhoneOrder> PhoneOrders { get; set; }
}
//抽象实体
公共阶级秩序
{
整数金额{get;set;}
}
//具体实体
公共类在线订单:订单
{
字符串网站{get;set;}
}
//具体实体
类PhoneOrder:Order
{
字符串调用中心{get;set;}
}
//抽象实体
抽象类客户
{
字符串城市{get;set;}
列表顺序{get;set;}
}
//具体实体
在线类别客户:客户
{
IEnumerable OnlineOrders{get;set;}
}
//具体实体
类别电话客户:客户
{
IEnumerable PhoneOrders{get;set;}
}
问题是关于客户订单的关联。事实上,Order是OnlineOrder和PhoneOrder的基类。开发人员应该能够在Customer类型的引用上访问和写入Linq,并对其与订单的关联设置条件。所以我在Customer中建立了Order类型的关联。订单和客户都是抽象的实体
我想知道定义两个关联是否是一种好的做法,一个在客户中,另一个在OnlineOrder类型的OnlineCustomer中?如果是这样,如何使用EF将此模型映射到关系数据库
假定抽象的Customer和Order类位于共享程序集中,并且共享项目程序集中不会通知继承的具体类。TPH、TPC或TPT以及我们应该将关系放在哪里?DbSet类表示数据库表
请记住,您在DbContext
中定义的DbSet
类表示数据库中的表。这些表是具有实列的实项目。每个列都由类中的一个属性表示,该属性将放在DbSet
中。如果列不是实的,并且表示一个关系,则该属性被定义为虚拟。您可以在一对多相关类中的虚拟声明中看到这一点
其效果是,DbContext'中的“DbSet类”不是
接口,但真正的类
另一方面,这些类可能实现您的接口
派生接口
您的在线客户
只有在线订单
。如果您向在线客户
索要他们的订单
,您希望得到的对象与向他们索要他们的在线订单
时得到的对象相同。那么为什么要使用不同的函数名呢
但是我想要一个不同的返回值强>
如果在接口中有派生,并且派生接口中的函数应与基函数执行相同的操作,除了返回值之外,更常见的是使用相同的函数名并使用显式接口实现
在IEnumerable.GetEnumerator()
和IEnumerable.GetEnumerator()
两个函数使用相同的方法名,它们的返回值不同。如果您有一个IEnumerable
并询问GetEnumerator()
,您就知道您得到了一个IEnumerator
,它是从IEnumerator
派生的
另一个示例:一个列表
实现了从IList
派生的IList
。接口IList
正常实现,而IList
显式实现
这与您的在线用户
非常相似。您想要的是,如果您向在线用户
询问他们的订单
,您只希望在线订单
。毕竟,在线用户
只有在线订单
。您还希望返回的订单都具有IOnlineOrders
界面。此接口还实现了IOrders
,因此,如果您向IOnlineUser
询问其订单
,您将获得其IOnlineOrders
功能以及IOrder
功能
这比让用户决定是否需要调用在线订单
或订单
因此,我的建议是:
// unchanged:
interface IOrder {...}
interface IOnlineOrder : IOrder {...}
interface IPhoneOrder : IOrder {...}
interface ICustomer {...}
// similar to IEnumerator<T> and IEnumerator
interface IOnlineCustomer : ICustomer
{
new List<IOnlineOrder> Orders { get; set; }
}
interface IPhoneCustomer : ICustomer
{
new List<IPhoneOrder> Orders { get; set; }
}
调试器将显示调用的是OnlineCustomers.Orders,而不是显式实现的ICCustomer.Orders。尽管您只能访问IOrder
函数,但所有返回的元素实际上都是IOnlineOrders,这当然实现了IOrder`
另见
最后一句话
您决定将订单
函数的返回值设置为列表
。您确定订单[4]
在您的上下文中有定义的含义吗
返回一个ICollection',或者甚至返回一个IReadOnlyCollection不是更好吗?毕竟,枚举和知道元素数量是调用者的关键特性。他们可能不想调用
Orders[4]`
定义顺序[4]
的正确含义相当困难,否则您将不得不进行一些排序或其他操作,这可能是对处理能力的浪费,因为大多数函数调用方并不真正需要排序的集合
考虑更改返回值。使用哪种语言?@tadman C#language反对这一点的唯一理由是,除非使用TPH继承,否则必须在抽象实体中映射关系,在这种情况下,遍历对象树并收集所有导航属性可能会很麻烦。除此之外,这很好,而且可能是一种方式。@多态无行为接口可能是一种代码气味。你为什么需要这些抽象概念?@guillaume31他们可能有行为。为了关注结构,我删除了行为。我编辑了问题,以反映根本问题。我将接口转换为类,以便澄清我不坚持将它们作为接口。我发现new关键字的作用很有趣。如果抽象的Customer和Order类位于共享程序集中,并且共享项目中,那么新的设计应该如何映射
ICustomer customer = new OnlineCustomer(...);
List<IOrders> orders = customer.Orders();