我是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的正确方法?
答案 0 :(得分:34)
这question & answer让我相信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)
{
...
}
答案 1 :(得分:26)
作为IQueryable<T>
和IEnumerable<T>
公开的对象在迭代或以其他方式访问之前实际上并不“执行”,例如组成List<T>
。当EF返回IQueryable<T>
时,它基本上只是组成能够检索数据的东西,它实际上并不是在你使用它之前执行检索。
通过在定义IQueryable
的位置和调用.ToList()
的位置放置断点,可以了解这一点。 (正如Jofry正确指出的那样,来自数据上下文的范围。)在ToList()
调用期间完成了提取数据的工作。
因此,您需要将IQueryable<T>
保留在数据上下文的范围内。
答案 2 :(得分:13)
您需要记住,在枚举之前,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应用程序中,有些人会根据请求创建一个Context。
答案 3 :(得分:3)
抛出错误的原因是对象被处理掉,然后我们尝试通过对象访问表值,但是对象被处理掉了。更好地将它转换为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)
{
...
}
答案 4 :(得分: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 dispos错误,但这可以通过在LINQ操作上添加ToList()显式地执行枚举来解决:
public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
using (var ctxt = new RcContext())
{
return ctxt.MyObjects.Where(x => x.MyObjects.Id == id).ToList();
}
}
答案 5 :(得分: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();
}
要点是你只想强制一次上下文数据集的枚举。让调用者处理空集场景,就像他们应该的那样。
答案 6 :(得分:2)
在这里,您尝试在非活动DBContext上执行IQueryable对象。你的DBcontext已经被处理掉了。您只能在处理DBContext之前执行IQueryable对象。意味着您需要使用范围
编写users.Select(x => x.ToInfo()).ToList()
语句
答案 7 :(得分:0)
using(var database=new DatabaseEntities()){}
不要使用using语句。就这样写
DatabaseEntities database=new DatabaseEntities ();{}
它将起作用。
有关using
语句的文档,请参见https://developer.android.com/training/location/change-location-settings。