如何将复杂的实体字段包括在投影实体框架对象中?[英] How to include complex entity fields in a projected entity framework object?

本文是小编为大家收集整理的关于如何将复杂的实体字段包括在投影实体框架对象中?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我经常使用System.Data.Entity.DbExtensions Include()方法使我的存储库的查询结果中包括复杂的实体字段.但是,当我将实体投射到新课程中时,我似乎失去了包含复杂实体字段的"具体化".例如,说我想从我的存储库中返回Event对象,并能够访问复杂实体字段Assessment:

public class EventRepository {
...
    public IList<Event> GetEvents() {
        using (var context = new MyDatabaseContext()) {
            return context.Events
                .Include(evnt => evnt.ActualAssessment)
                .ToList();
        }
    }
...
}

我可以在上面使用的Include上不用挂钩运行以下代码:

var repoEvents = new EventRepository();
var events = repoEvents.GetEvents();
Console.WriteLine(events[0].ActualAssessment.AssessmentDate.ToString());

,但说我现在想将Event s投射到包装对象ExtendedEvent中,并带有一些额外的信息,例如:

public class EventRepository {
...
    public IList<ExtendedEvent> GetExtendedEvents() {
        using (var context = new MyDatabaseContext()) {
            return context.Events
                .Include(evnt => evnt.ActualAssessment)
                .Select(evnt => new {
                    TheEvent = evnt,
                    SomeExtraData = 123
                })
                .ToList()
                .Select(evntInfo => {
                    return new ExtendedEvent {
                        TheEvent = evntInfo.TheEvent,
                        SomeExtraData = evntInfo.SomeExtraData
                    };
                })
                .ToList();
        }
    }
...
}

我现在尝试运行此代码:

var repoEvents = new EventRepository();
var extendedEvents = repoEvents.GetExtendedEvents();
Console.WriteLine(extendedEvents[0].TheEvent.ActualAssessment.AssessmentDate.ToString());

这给我带来了错误:"对ObjectContext实例已被处置,不再可用于需要连接的操作." - 因此,尽管我使用Include,但ActualAssessment并未急切地加载,这显然是因为我将其投影到了一个新的包装对象中.如何将ActualAssessment包括在内?

推荐答案

是,Include在投影中被忽略.您可以尝试的是将相关导航属性放在匿名对象中:

public IList<ExtendedEvent> GetExtendedEvents() {
    using (var context = new MyDatabaseContext()) {
        return context.Events
            .Select(evnt => new {
                TheEvent = evnt,
                SomeExtraData = 123,
                ActualAssessment = evnt.ActualAssessment
            })
            .ToList()
            .Select(evntInfo => {
                return new ExtendedEvent {
                    TheEvent = evntInfo.TheEvent,
                    SomeExtraData = evntInfo.SomeExtraData
                };
            })
            .ToList();
    }
}

ActualAssessment将附加到上下文上,自动关系修复将使poplate TheEvent.ActualAssessment 如果

  • 您不会禁用更改跟踪
  • 这种关系并不多,

作为旁注:您可以使用AsEnumerable()而不是第一个ToList()来避免创建匿名对象列表的不必要的开销.

编辑

对于多一关系,或者在残疾变更跟踪的情况下,必须在构成数据库查询之后设置导航属性,例如:

            .Select(evntInfo => {
                evntInfo.TheEvent.ActualAssessment = evntInfo.ActualAssessment;
                return new ExtendedEvent {
                    TheEvent = evntInfo.TheEvent,
                    SomeExtraData = evntInfo.SomeExtraData
                };
            })

本文地址:https://www.itbaoku.cn/post/1556749.html

问题描述

I often use the System.Data.Entity.DbExtensions Include() method to cause complex entity fields to be included in query results from my repositories. However, when I project my entities into new classes, I seem to lose this "concretization" of included complex entity fields. For example, say I wanted to return an Event object from my repo, and be able to access the complex entity field Assessment:

public class EventRepository {
...
    public IList<Event> GetEvents() {
        using (var context = new MyDatabaseContext()) {
            return context.Events
                .Include(evnt => evnt.ActualAssessment)
                .ToList();
        }
    }
...
}

I can then run the following code without a hitch because of the Include I used above:

var repoEvents = new EventRepository();
var events = repoEvents.GetEvents();
Console.WriteLine(events[0].ActualAssessment.AssessmentDate.ToString());

But say I now want to project the Events into a wrapper object ExtendedEvent with some extra info, like this:

public class EventRepository {
...
    public IList<ExtendedEvent> GetExtendedEvents() {
        using (var context = new MyDatabaseContext()) {
            return context.Events
                .Include(evnt => evnt.ActualAssessment)
                .Select(evnt => new {
                    TheEvent = evnt,
                    SomeExtraData = 123
                })
                .ToList()
                .Select(evntInfo => {
                    return new ExtendedEvent {
                        TheEvent = evntInfo.TheEvent,
                        SomeExtraData = evntInfo.SomeExtraData
                    };
                })
                .ToList();
        }
    }
...
}

I now try and run this code:

var repoEvents = new EventRepository();
var extendedEvents = repoEvents.GetExtendedEvents();
Console.WriteLine(extendedEvents[0].TheEvent.ActualAssessment.AssessmentDate.ToString());

This gives me the error "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection." - so ActualAssessment has not been eager-loaded despite my use of Include, apparently because I projected it into a new wrapper object. How can I cause ActualAssessment to be included?

推荐答案

Yes, Include is ignored in projections. What you can try is to make the related navigation property part of your projection into the anonymous object:

public IList<ExtendedEvent> GetExtendedEvents() {
    using (var context = new MyDatabaseContext()) {
        return context.Events
            .Select(evnt => new {
                TheEvent = evnt,
                SomeExtraData = 123,
                ActualAssessment = evnt.ActualAssessment
            })
            .ToList()
            .Select(evntInfo => {
                return new ExtendedEvent {
                    TheEvent = evntInfo.TheEvent,
                    SomeExtraData = evntInfo.SomeExtraData
                };
            })
            .ToList();
    }
}

The ActualAssessment will be attached to the context and automatic relationship fixup will popolate TheEvent.ActualAssessment if

  • you don't disable change tracking
  • the relationship is not many-to-many

As a side note: You can use AsEnumerable() instead of the first ToList() to avoid the unnecessary overhead of creating a list of anonymous objects.

Edit

For a many-to-many relationship or in case of disabled change tracking you must set the navigation property after the DB query is materialized, like so for example:

            .Select(evntInfo => {
                evntInfo.TheEvent.ActualAssessment = evntInfo.ActualAssessment;
                return new ExtendedEvent {
                    TheEvent = evntInfo.TheEvent,
                    SomeExtraData = evntInfo.SomeExtraData
                };
            })