非 EF 实体的“已释放对象上下文实例”

本文关键字:对象 上下文 实例 释放 EF 实体 | 更新日期: 2023-09-27 18:21:15

我正在使用Entity Framework 6通过返回Complex对象的存储过程查询一些对象。在将这些对象返回给客户端之前,我将它们"转换"为特定于客户端的实体。

在服务器端,我有以下代码从数据库中获取 DBMeeting 类型的复杂对象:

public static IEnumerable<Meeting> GetMeetings()
{
    using(var context = new MyDataContext())
    {
        var dbMeetings = context.GetMeetings(null, null);
        var result = ComplexToEntityTranslator.TranslateMeetings(dbMeetings);
        return result;
    }
}

TranslateMeetings方法:

internal static IEnumerable<Meeting> TranslateMeetings(IEnumerable<DBMeeting> dbMeetings)
{
    foreach (var dbMeeting in dbMeetings)
    {
        yield return TranslateMeeting(dbMeeting);
    }
}
internal static Meeting TranslateMeeting(DBMeeting dbMeeting)
{
    return new Meeting
    {
        Id = dbMeeting.ID,
        Name = dbMeeting.Name,
        Description = dbMeeting.Description
        // other properties
    };
}

现在在客户端,当我调用GetMeetings()并尝试枚举列表时,我得到一个InvalidOperationException说:

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

我特别不想在服务器端运行.ToList(),因为我希望枚举在客户端发生。但是,在我的TranslateMeetings()方法中,我正在创建新的会议对象,这些对象不会以任何方式映射到我的模型。那么,为什么它仍然需要上下文存在呢?为什么 EF 试图跟踪未映射的 (Meeting( 对象?

非 EF 实体的“已释放对象上下文实例”

问题是实体框架使用延迟计算。它不会尝试将对象从数据库中取出,除非绝对必要。

此外,通过在TranslateMeetings方法中使用yield return,您将在那里使用延迟评估。该方法中的代码在您实际迭代之前不会运行。

因此,当您返回result时,尚未对数据库进行调用。稍后,当您尝试迭代result时,TranslateMeetings方法将尝试迭代dbMeetings对象。这将触发实体框架执行 SQL 并尝试填充dbMeetings。但到那时,上下文已被释放,因此调用失败。

我知道你说你不想跑.ToList((,但这几乎是你必须做的。不能将评估推迟到上下文被释放之后!客户端(我假设这是一个 API?(不能是执行实体框架的客户端。它只是不是那样工作的。客户端需要接收填充的对象。

这是因为一旦到达using范围的末尾,您就已释放了 EF 上下文:

using(var context = new MyDataContext())
{
    var dbMeetings = context.GetMeetings(null, null);
    var result = ComplexToEntityTranslator.TranslateMeetings(dbMeetings);
    return result;
}

稍后,当您枚举IEnumerable时,您将尝试访问实体。