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



我是EF的新手,我正在尝试使用一种扩展方法,该方法将我的数据库类型User转换为我的信息类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的正确方法是什么?

这个问题&这个答案让我相信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)
{
...
}

作为IQueryable<T>IEnumerable<T>公开的对象在被迭代或以其他方式访问(例如被组合成List<T>)之前,实际上不会"执行"。当EF返回一个IQueryable<T>时,它本质上只是在编写能够检索数据的东西,直到你使用它,它才真正执行检索

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

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

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

using (var dataContext = new dataContext())
{

这行代码除了构建SQL语句之外,实际上什么都没做

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

Any()是一个枚举IQueryable的操作,因此SQL被发送到数据源(通过dataContext),然后发送到。对其执行任意()操作

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与ASP.net与MVC等)。它实现的模式是工作单元模式。创建一个新的上下文对象几乎没有成本,所以一般规则是创建一个,完成你的工作,然后处理它。在web应用程序中,有些人会根据请求创建一个上下文。

它抛出错误的原因是对象被释放,之后我们试图通过对象访问表值,但对象被释放了。最好将其转换为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)
{
...
}

这里您正试图在非活动DBContext上执行IQueryable对象。您的DBcontext已被处置。只能在DBContext被释放之前执行IQueryable对象。意味着您需要使用作用域在内部编写users.Select(x => x.ToInfo()).ToList()语句

这可以像在存储库中添加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();
}
}

更改此项:

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();
} 

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

using(var database=new DatabaseEntities()){}

不要使用using语句。就像一样写

DatabaseEntities database=new DatabaseEntities ();{}

它会起作用的。

有关using语句的文档,请参阅此处。

最新更新