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();