如何从延迟加载和单表继承的数据库中获取数据

时间:2014-05-01 19:17:35

标签: c# sql database entity-framework inheritance

我有一个设置了延迟加载的数据库(需要)。 我还有一个ASP.NET MVC应用程序,它连接到数据库和' Does Stuff'。 数据库是在我的类中建模的。

我目前有3个使用继承的类:

public class DeliveryNotification
    {
        [Key]
        public int Id { get; set; }

        public Status Status { get; set; }
    }

    public class TrackedDelivery : DeliveryNotification
{
    [Required]
    public DateTime Date { get; set; }

    [Required]
    public DateTime Time { get; set; }
}

public class SignedForDelivery : TrackedDelivery
    {
        [Required]
        public string Image { get; set; }

    }

这些都使用名为' DeliveryNotification'

的单个表存储在数据库中

我已获得DeliveryNotification ID,并希望获得所有信息。

我的一个控制器内部是这种方法:

var query = (from s in context.SignedForDeliverys
                    where s.Id == id
                             select s
                    ).SingleOrDefault();

^尽管有一个带有ID

的DeliveryNotification,但这将返回null
var deliveryNotifications = context.DeliveryNotifications.SingleOrDefault(o => o.Id == id);

^这将仅返回id和状态。

假设我已经获得了一个Id,我怎么能返回所有数据(最好是SignedForDeliverys)?

修改: 试图添加一个包括:

var signedForDeliverys = (from s in context.SignedForDeliverys.Include("TrackedDelivery ").Include("DeliveryNotification")
                select s).ToList();

然后我收到此错误:

An exception of type 'System.InvalidOperationException' occurred in EntityFramework.SqlServer.dll but was not handled in user code

Additional information: A specified Include path is not valid. The EntityType 'ClassLibrary.SignedForDelivery' does not declare a navigation property with the name 'TrackedDelivery'.

2 个答案:

答案 0 :(得分:3)

如果你想要SignedForDelivery,你可以......

context.DeliveryNotifications.OfType<SignedForDelivery>().Single(n => n.Id == id)
        // or .Find(id)

或者,如果每个子类型都有DbSet个属性...

context.SignedForDeliveries.Single(n => n.Id == id) // or .Find(id)

...而且您已获得所有数据。

但是有更多的话要说。

根据您的描述,似乎在不知道预先输入类型的情况下通过Id获取对象是业务案例(或用例)。如果是这样,在我看来你不应该使用继承。你唯一能做的就是检索一个DeliveryNotification,然后尝试将它转换为所有继承的类型以查看它是什么(因为鉴别器字段不是类模型的一部分)。那非常笨拙。

我不会通过继承来解决这个问题。我会使用一种类型,获取记录,然后决定哪些数据是相关的,例如通过将其映射到特定的视图模型或域类,具体取决于现在可以看到的Type属性。这可以通过策略模式来完成,这是一种组合模式。 (参考&#34;构成而非继承&#34;辩论)。

此外,使用TPH继承(一个表),您无法强制执行特定属性(如Image),因为它们应该在数据库表中可以为空。它是一个&#34;软&#34;从某种意义上说,它的执行取决于软件。其他继承方案TPT,TPC确实允许强有力的执行,但它们还有其他缺点。

简而言之,继承本身并不总是最好的模式,再加上ORM,它往往更好地避免。

答案 1 :(得分:1)

查询1:

var query = (from s in context.SignedForDeliverys
            where s.Id == id
            select s
            ).SingleOrDefault();
如果ID与SignedForDelivery匹配,则

应返回SignedForDelivery。如果ID与DeliveryNotification不匹配SignedForDelivery,则它将返回null。

查询2:

var deliveryNotifications = context.DeliveryNotifications
                                   .SingleOrDefault(o => o.Id == id);
如果ID与DeliveryNotification匹配,则

应返回DeliveryNotification。要将其设为TrackedDeliverySignedForDelivery,您需要将其投射:

var trackedDelivery = deliveryNotifications as DeliveryNotification;
if(trackedDelivery != null)
{
   DateTime d = trackedDelivery.Date;
}

var signedForDelivery = deliveryNotifications as SignedForDelivery 
if(signedForDelivery != null)
{
   String image = signedForDelivery.Image;
}

查询3:

var signedForDeliverys = (from s in context.SignedForDeliverys
                                           .Include("TrackedDelivery")
                                           .Include("DeliveryNotification")
                          select s).ToList();

失败,因为Include方法的目的是load related entities。即通过导航属性相关的实体。 SignedForDelivery未声明名称为TrackedDelivery

的导航属性