40

我是 EF 的新手,我正在尝试使用一种扩展方法,它将我的 Database 类型转换User为我的 info 类UserInfo
如果这有什么不同,我会先使用数据库吗?

我下面的代码给出了错误

操作无法完成,因为 DbContext 已被释放。

try
{
    IQueryable<User> users;
    using (var dataContext = new dataContext())
    {
        users = dataContext.Users
                  .Where(x => x.AccountID == accountId && x.IsAdmin == false);
        if(users.Any() == false)
        {
            return null;
        }
    }
    return users.Select(x => x.ToInfo()).ToList(); // this line is the problem
}
catch (Exception ex)
{
    //...
}

我可以看到它为什么会这样做,但我也不明白为什么 where 语句的结果没有保存到users对象中?

所以我想我的主要问题是为什么它不起作用,其次使用扩展方法和 EF 的正确方法是什么?

4

8 回答 8

39

这个问题和答案让我相信 IQueryable 的操作需要一个活动的上下文。这意味着你应该试试这个:

try
{
    IQueryable<User> users;

    using (var dataContext = new dataContext())
    {
        users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

        if(users.Any() == false)
        {
            return null;
        }
        else
        {
            return users.Select(x => x.ToInfo()).ToList(); // this line is the problem
        }
    }


}
catch (Exception ex)
{
    ...
}
于 2012-11-29T02:13:34.423 回答
28

对象暴露为IQueryable<T>并且IEnumerable<T>实际上不会“执行”,直到它们被迭代或以其他方式访问,例如被组合成List<T>. 当 EF 返回时,IQueryable<T>它本质上只是在组合能够检索数据的东西,在您使用它之前它实际上并没有执行检索。

IQueryable您可以通过在定义的位置放置断点来感受这一点,而不是在.ToList()调用时。(正如 Jofry 正确指出的那样,在数据上下文的范围内。)提取数据的工作是在ToList()调​​用期间完成的。

因此,您需要将其保持IQueryable<T>在数据上下文的范围内。

于 2012-11-29T02:26:04.260 回答
16

您需要记住,在枚举 IQueryable 查询之前,它们实际上并不会针对数据存储执行。

using (var dataContext = new dataContext())
{

这行代码实际上除了构建 SQL 语句之外没有做任何事情

    users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

.Any() 是枚举 IQueryable 的操作,因此将 SQL 发送到数据源(通过 dataContext),然后对其执行 .Any() 操作

    if(users.Any() == false)
    {
        return null;
    }
}

您的“问题”行正在重用上面构建的 sql,然后执行附加操作 (.Select()),这只是添加到查询中。如果你把它留在这里,没有例外,除了你的问题行

return users.Select(x => x.ToInfo()).ToList(); // this line is the problem

调用 .ToList(),它枚举 IQueryable,这会导致 SQL 通过原始 LINQ 查询中使用的 dataContext 发送到数据源。由于此 dataContext 已被释放,因此不再有效,并且 .ToList() 会引发异常。

这就是“为什么它不起作用”。解决方法是将这行代码移到 dataContext 的范围内。

如何正确使用它是另一个问题,其中有一些可以说是正确的答案,具体取决于您的应用程序(Forms vs. ASP.net vs. MVC 等)。它实现的模式是工作单元模式。创建一个新的上下文对象几乎没有成本,所以一般规则是创建一个,完成你的工作,然后将其丢弃。在 Web 应用程序中,有些人会为每个请求创建一个上下文。

于 2012-11-29T02:40:26.440 回答
4

抛出错误的原因是对象已被释放,之后我们尝试通过对象访问表值,但对象已被释放。最好将其转换为 ToList() 以便我们可以拥有值

也许在您使用它之前它实际上并没有获取数据(它是延迟加载),因此当您尝试完成工作时 dataContext 不存在。我敢打赌,如果您在范围内执行 ToList() 就可以了。

try
{
    IQueryable<User> users;
    var ret = null;

    using (var dataContext = new dataContext())
    {
        users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

        if(users.Any())
        {
            ret = users.Select(x => x.ToInfo()).ToList(); 
        }

     }

   Return ret;
}
catch (Exception ex)
{
    ...
}
于 2012-11-29T02:23:04.890 回答
2

这可以像在存储库中添加 ToList() 一样简单。例如:

public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
    using (var ctxt = new RcContext())
    {
        // causes an error
        return ctxt.MyObjects.Where(x => x.MyObjects.Id == id);
    }
}

将在调用类中产生 Db Context 处理错误,但这可以通过在 LINQ 操作上添加 ToList() 显式执行枚举来解决:

public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
    using (var ctxt = new RcContext())
    {
        return ctxt.MyObjects.Where(x => x.MyObjects.Id == id).ToList();
    }
}
于 2014-11-18T16:07:28.643 回答
2

改变这个:

using (var dataContext = new dataContext())
{
    users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

    if(users.Any())
    {
        ret = users.Select(x => x.ToInfo()).ToList(); 
    }

 }

对此:

using (var dataContext = new dataContext())
{
    return = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false).Select(x => x.ToInfo()).ToList();
} 

要点是您只想强制枚举上下文数据集一次。让调用者按照他们应该的方式处理空集场景。

于 2016-04-25T10:23:11.440 回答
2

在这里,您尝试在非活动 DBContext 上执行 IQueryable 对象。你的 DBcontext 已经被处理掉了。您只能在处理 DBContext 之前执行 IQueryable 对象。意味着您需要users.Select(x => x.ToInfo()).ToList()在使用范围内编写语句

于 2017-08-09T00:00:14.507 回答
1
using(var database=new DatabaseEntities()){}

不要使用 using 语句。就这样写

DatabaseEntities database=new DatabaseEntities ();{}

它会起作用的。

有关该using声明的文档,请参见此处

于 2019-01-31T05:57:04.700 回答