假设我有以下接口:
//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; }
}
问题在于与客户订单的关联。实际上,Order是OnlineOrder和PhoneOrder的基类。开发人员应该能够在类型为Customer的引用上访问和编写Linq,并对其与Order的关联设置条件。所以我在客户中放置了Order类型的关联。订单和客户都是抽象的实体。
我想知道定义两个关联是一个好习惯,一个在Customer中,另一个在OnlineOustder类型的OnlineCustomer中?如果是这样,我如何使用EF将此模型映射到关系数据库?
鉴于抽象的Customer和Order类位于共享程序集中,并且共享项目程序集不会通知继承的具体类。 TPH,TPC或TPT我们应该把关系放在哪里?
答案 0 :(得分:0)
请记住,您在DbSet
中定义的DbContext
类代表数据库中的表。这些表是带有真实列的真实项目。每个列都由类中的属性表示,您将放在DbSet
中。如果列不是真实的,并且表示关系,则属性被定义为虚拟。您可以在一对多相关类的虚拟声明中看到这一点。
效果是,&#39; DbSet
classes in your
DbContext`不是 接口,但真正的类。
另一方面,这些类可能会实现您的接口。
您的OnlineCustomers
只有OnlineOrders
。如果您向OnlineCustomer
询问Orders
他们获得相同的对象,那么您希望他们向OnlineOrders
提出相同的对象。那么为什么要使用不同的函数名呢?
但我想要一个不同的回报值!
如果在接口中有派生,派生接口中的函数应该与基函数相同,除了返回值之外,更常见的是使用相同的函数名称并使用显式接口实现。
您也会在IEnumerable.GetEnumerator()
和IEnumerable<T>.GetEnumerator()
中看到这两个函数使用相同的方法名称,它们的返回值不同。如果你有一个IEnumerable<T>
并且你问GetEnumerator()
,那么就知道你得到的IEnumerator<T>
来自IEnumerator
。
另一个例子:List<T>
实现IList<T>
,它派生自IList
。接口IList<T>
正常实施,IList
明确实施。
这与您的OnlineUsers
非常相似。你想要的是,如果你问OnlineUser
他们的Orders
你只期望OnlineOrders
。毕竟,OnlineUsers
只有OnlineOrders
。您还希望返回的订单都具有IOnlineOrders
接口。此界面还会实现IOrders
,因此,如果您向IOnlineUser
询问Orders
,则会获得IOnlineOrders
功能以及IOrder
功能。
这样更实用,然后让用户决定是否需要致电OnlineOrders
或Orders
所以我的建议是:
// 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; }
}
请注意,我使用new
关键字。每当我向IOnlineCustomer询问他Orders
我不想要&#39; ICustomer.Orders`时。
班级OnlineCustomer
同时实施IOnlineCustomer
和ICustomer
。 IOnlineCustomer函数是隐式实现的,ICustomer函数是显式实现的。此函数可能会使用Cast调用隐式Order函数。
尝试以下方法:
ICustomer customer = new OnlineCustomer(...);
List<IOrders> orders = customer.Orders();
您的调试器将向您显示调用OnlineCustomers.Orders,而不是显式实现的ICustomer.Orders。虽然您只能访问IOrder
函数,但所有返回的元素实际上都是IOnlineOrders , which of course implement the
IOrder`。
另见Why implement interface explicitly?
您决定将Orders
函数的返回值设为List<IOrder>
。您确定Orders[4]
在您的上下文中有明确的含义吗?
最好不要返回ICollection<IOrder>', or maybe even an
IReadOnlyCollection ? After all the possibility to enumerate and to know the number of elements are key features for your callers. They probably don't want to call
订单[4]。
定义Order[4]
的正确含义相当困难,或者你必须做一些排序或其他事情,这可能会浪费处理能力,因为你职能的大多数来电者都不是真的需要一个有序的集合。
考虑更改返回值。