C# 无法完成该操作,因为DbContext已被释放

C# 无法完成该操作,因为DbContext已被释放,c#,entity-framework,extension-methods,C#,Entity Framework,Extension Methods,我是EF新手,我正在尝试使用一种扩展方法,将我的数据库类型User转换为我的info类UserInfo。 如果这有什么不同,我会先使用数据库吗 下面的代码给出了错误 无法完成该操作,因为DbContext已被释放 试试看 { 易读用户; 使用(var dataContext=new dataContext()) { users=dataContext.users 。其中(x=>x.AccountID==AccountID&&x.IsAdmin==false); if(users.Any()==f

我是EF新手,我正在尝试使用一种扩展方法,将我的数据库类型
User
转换为我的info类
UserInfo

如果这有什么不同,我会先使用数据库吗

下面的代码给出了错误

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

试试看
{
易读用户;
使用(var dataContext=new dataContext())
{
users=dataContext.users
。其中(x=>x.AccountID==AccountID&&x.IsAdmin==false);
if(users.Any()==false)
{
返回null;
}
}
return users.Select(x=>x.ToInfo()).ToList();//这一行就是问题所在
}
捕获(例外情况除外)
{
//...
}
我知道它为什么会这样做,但我也不明白为什么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)
{
    ...
}
试试看
{
易读用户;
使用(var dataContext=new dataContext())
{
users=dataContext.users.Where(x=>x.AccountID==AccountID&&x.IsAdmin==false);
if(users.Any()==false)
{
返回null;
}
其他的
{
return users.Select(x=>x.ToInfo()).ToList();//这一行就是问题所在
}
}
}
捕获(例外情况除外)
{
...
}

它抛出错误的原因是对象已被释放,之后我们试图通过对象访问表值,但对象已被释放。最好将其转换为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)
{
    ...
}
试试看
{
易读用户;
var-ret=null;
使用(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();
}
}
返回ret;
}
捕获(例外情况除外)
{
...
}

作为
IQueryable
IEnumerable
公开的对象实际上不会“执行”,直到它们被迭代或以其他方式访问,例如组合到
列表中。当EF返回一个
IQueryable
时,它实际上只是在编写一些能够检索数据的东西,在您使用它之前,它实际上不会执行检索

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


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

您需要记住,在枚举之前,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的范围内


如何正确地使用它是另一个问题,其中有一些可能是正确的答案,这取决于您的应用程序(表单、ASP.net和MVC等)。它实现的模式是工作单元模式。创建一个新的上下文对象几乎没有成本,所以一般的规则是创建一个,完成工作,然后处理它。在web应用程序中,有些人会为每个请求创建上下文

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

public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
    using (var ctxt = new RcContext())
    {
        // causes an error
        return ctxt.MyObjects.Where(x => x.MyObjects.Id == id);
    }
}
public IEnumerable GetMyObjectsForId(字符串id)
{
使用(var ctxt=new RcContext())
{
//引起错误
返回ctxt.MyObjects.Where(x=>x.MyObjects.Id==Id);
}
}
将在调用类中产生Db Context disposed错误,但这可以通过在LINQ操作上添加ToList()显式执行枚举来解决:

public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
    using (var ctxt = new RcContext())
    {
        return ctxt.MyObjects.Where(x => x.MyObjects.Id == id).ToList();
    }
}
public IEnumerable GetMyObjectsForId(字符串id)
{
使用(var ctxt=new RcContext())
{
返回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();
} 

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

这里您试图在非活动DBContext上执行IQueryable对象。您的DBcontext已被释放。您只能在释放DBContext之前执行IQueryable对象。意味着您需要编写
用户。在使用范围内选择(x=>x.ToInfo()).ToList()
语句

using(var database=new DatabaseEntities()){}
不要使用using语句。就这样写吧

DatabaseEntities database=new DatabaseEntities ();{}
它会起作用的


有关使用
语句的
文档,请参阅。

谢谢。我现在可以看到我可以
DatabaseEntities database=new DatabaseEntities ();{}