在 EF 的 POCO 中加载方法所需的导航属性
Load navigation properties necessary for a method in POCO in EF
请看下面我的问题的例子。
在我从 Calculate()
方法收到 result
变量后,EF 上下文被释放。如果我稍后在此 result
上调用 DoMethod()
,我会收到错误消息,因为未加载 EF 导航 属性 SomeObjects
。
我可以想到以下解决方案来防止这个问题?
- 在
Calculate()
方法中预先加载 SomeObjects
(xyList = context.Xys.Include(x => x.SomeObjects).ToList();
)(如果以后不使用,则不必要地加载此 属性)
- 不要关闭数据库上下文或使用全局上下文(非常糟糕!)
- 在
DoMethod()
中加载缺少的 EF 导航 属性
我会选择第三个,因为 DoMethod()
并不总是被调用,因此如果不是,我不需要 SomeObjects
。
我的问题是如何实现第三种方案?这是正确的方法吗?从 POCO 中查询以获取必要的数据似乎有点奇怪。
class Program
{
static void Main(string[] args)
{
...
Xy result = Calculation.Calculate();
...
//Maybee this method is invoked
result.DoMethod();
}
}
// POCO class
public class XY
{
public virtual List<Xz> SomeObjects { get; set; }
public void DoMethod()
{
foreach (var obj in SomeObjects)
{
...
}
}
}
class Calculation
{
public static Xy Calculate() {
Xy result;
using (var context = new MyContext())
{
xyList = context.Xys.ToList();
...
result = xyList[calculatedIndex];
}
return result;
}
}
以下是我尝试过或想到的一些选项。 #3 是对您指定的首选方法的一次尝试。
1.只在最后一刻计算。
这会产生每次需要结果时创建上下文的开销,但会推迟使用上下文直到需要结果为止。您的用例决定了这是否有帮助。
class Program
{
static void Main(string[] args)
{
...
Calculation calc = new Calculation();
...
//Maybe this method is invoked
calc.GetResult().DoMethod();
}
}
class Calculation
{
public Xy GetResult();
{
Xy result;
using (var context = new MyContext())
{
xyList = context.Xys.ToList();
...
result = xyList[calculatedIndex];
}
return result;
}
}
2。缓存结果并保持上下文活动
这是您的选项 #2,但没有全局上下文(您理所当然地担心)。如果您担心不处理上下文,请看一下:http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html。
除了加载上下文的内存开销之外,我看不出有什么缺点。 EF 将延迟加载 SomeObjects
,直到您首先通过调用 DoMethod()
需要它们。您正在交易保留上下文,除非需要,否则不必加载 SomeObject。
class Program
{
static void Main(string[] args)
{
...
Calculation calc = new Calculation(new MyContext());
//use result, perhaps many times
/*something with calc.Result; */
...
//Maybe this method is invoked
calc.Result.DoMethod();
//context will not go away until Calculation does
}
}
class Calculation
{
private MyContext context = null;
private Xy result = null;
public Calculation(MyContext context)
{
this.context = context;
}
public Xy Result {
get {
if (result == null) {
result = Calculate();
}
return result;
}
}
private Xy Calculate();
{
Xy result;
xyList = context.Xys.ToList();
...
result = xyList[calculatedIndex];
return result;
}
}
3。通过动态代理实施您的选项 #3
这允许将 XY 包装在行为类似于 XY 的代理中,但拦截对 DoMethod
的调用以获取新上下文,以便 SomeObjects
可以在新上下文中解析。我使用了 Castle.Core 项目中提供的 Castle Dynamic Proxy,您只需通过 Nuget 添加即可。有足够的概念开销,我认为这可能是概念的反证明。也就是说,它表明保留上下文以便 SomeObjects
可以针对原始上下文进行延迟加载可能是最干净的想法。再一次,请参考 http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html 中的论点,了解为什么保留上下文可能没问题。顺便说一句,那篇文章来自与 EF 开发团队的对话。
使用 Castle.DynamicProxy;
class Program
{
static void Main(string[] args)
{
...
Calculation calc = new Calculation(new MyContext());
//use result, perhaps many times
/*something with calc.Result; */
...
//Maybe this method is invoked
calc.Result.DoMethod();
}
}
// POCO class
public class XY
{
public virtual List<Xz> SomeObjects { get; set; }
public virtual void DoMethod()
{
foreach (var obj in SomeObjects)
{
...
}
}
}
public class XYInterceptor : XY, IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (invocation.Method.Name == "DoMethod")
{
//get a new context so that we can have SomeObjects resolve properly
using (var context = new MyContext())
{
var newXy = context.Xys.Find(((XY)invocation.InvocationTarget).Id);
newXy.DoMethod();
}
}
else
{
//Any other method goes straight through
invocation.Proceed();
}
}
}
public class Calculation
{
private XY result = null;
public XY Result {
get {
if (result == null) {
result = Calculate();
}
return result;
}
}
private XY Calculate()
{
XY proxyResult;
using (var context = new MyContext())
{
xyList = context.Xys.ToList();
...
Xy realResult = xyList[calculatedIndex];
proxyResult = (new ProxyGenerator()).CreateClassProxyWithTarget<XY>(realResult, new XYInterceptor());
return proxyResult;
}
}
}
我的第三个草图的一个烦人的方面是它没有用新的 XY 更新结果。在它真正准备好使用之前,需要让它发挥作用。
请看下面我的问题的例子。
在我从 Calculate()
方法收到 result
变量后,EF 上下文被释放。如果我稍后在此 result
上调用 DoMethod()
,我会收到错误消息,因为未加载 EF 导航 属性 SomeObjects
。
我可以想到以下解决方案来防止这个问题?
- 在
Calculate()
方法中预先加载SomeObjects
(xyList = context.Xys.Include(x => x.SomeObjects).ToList();
)(如果以后不使用,则不必要地加载此 属性) - 不要关闭数据库上下文或使用全局上下文(非常糟糕!)
- 在
DoMethod()
中加载缺少的 EF 导航 属性
我会选择第三个,因为 DoMethod()
并不总是被调用,因此如果不是,我不需要 SomeObjects
。
我的问题是如何实现第三种方案?这是正确的方法吗?从 POCO 中查询以获取必要的数据似乎有点奇怪。
class Program
{
static void Main(string[] args)
{
...
Xy result = Calculation.Calculate();
...
//Maybee this method is invoked
result.DoMethod();
}
}
// POCO class
public class XY
{
public virtual List<Xz> SomeObjects { get; set; }
public void DoMethod()
{
foreach (var obj in SomeObjects)
{
...
}
}
}
class Calculation
{
public static Xy Calculate() {
Xy result;
using (var context = new MyContext())
{
xyList = context.Xys.ToList();
...
result = xyList[calculatedIndex];
}
return result;
}
}
以下是我尝试过或想到的一些选项。 #3 是对您指定的首选方法的一次尝试。
1.只在最后一刻计算。
这会产生每次需要结果时创建上下文的开销,但会推迟使用上下文直到需要结果为止。您的用例决定了这是否有帮助。
class Program
{
static void Main(string[] args)
{
...
Calculation calc = new Calculation();
...
//Maybe this method is invoked
calc.GetResult().DoMethod();
}
}
class Calculation
{
public Xy GetResult();
{
Xy result;
using (var context = new MyContext())
{
xyList = context.Xys.ToList();
...
result = xyList[calculatedIndex];
}
return result;
}
}
2。缓存结果并保持上下文活动
这是您的选项 #2,但没有全局上下文(您理所当然地担心)。如果您担心不处理上下文,请看一下:http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html。
除了加载上下文的内存开销之外,我看不出有什么缺点。 EF 将延迟加载 SomeObjects
,直到您首先通过调用 DoMethod()
需要它们。您正在交易保留上下文,除非需要,否则不必加载 SomeObject。
class Program
{
static void Main(string[] args)
{
...
Calculation calc = new Calculation(new MyContext());
//use result, perhaps many times
/*something with calc.Result; */
...
//Maybe this method is invoked
calc.Result.DoMethod();
//context will not go away until Calculation does
}
}
class Calculation
{
private MyContext context = null;
private Xy result = null;
public Calculation(MyContext context)
{
this.context = context;
}
public Xy Result {
get {
if (result == null) {
result = Calculate();
}
return result;
}
}
private Xy Calculate();
{
Xy result;
xyList = context.Xys.ToList();
...
result = xyList[calculatedIndex];
return result;
}
}
3。通过动态代理实施您的选项 #3
这允许将 XY 包装在行为类似于 XY 的代理中,但拦截对 DoMethod
的调用以获取新上下文,以便 SomeObjects
可以在新上下文中解析。我使用了 Castle.Core 项目中提供的 Castle Dynamic Proxy,您只需通过 Nuget 添加即可。有足够的概念开销,我认为这可能是概念的反证明。也就是说,它表明保留上下文以便 SomeObjects
可以针对原始上下文进行延迟加载可能是最干净的想法。再一次,请参考 http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html 中的论点,了解为什么保留上下文可能没问题。顺便说一句,那篇文章来自与 EF 开发团队的对话。
使用 Castle.DynamicProxy;
class Program
{
static void Main(string[] args)
{
...
Calculation calc = new Calculation(new MyContext());
//use result, perhaps many times
/*something with calc.Result; */
...
//Maybe this method is invoked
calc.Result.DoMethod();
}
}
// POCO class
public class XY
{
public virtual List<Xz> SomeObjects { get; set; }
public virtual void DoMethod()
{
foreach (var obj in SomeObjects)
{
...
}
}
}
public class XYInterceptor : XY, IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (invocation.Method.Name == "DoMethod")
{
//get a new context so that we can have SomeObjects resolve properly
using (var context = new MyContext())
{
var newXy = context.Xys.Find(((XY)invocation.InvocationTarget).Id);
newXy.DoMethod();
}
}
else
{
//Any other method goes straight through
invocation.Proceed();
}
}
}
public class Calculation
{
private XY result = null;
public XY Result {
get {
if (result == null) {
result = Calculate();
}
return result;
}
}
private XY Calculate()
{
XY proxyResult;
using (var context = new MyContext())
{
xyList = context.Xys.ToList();
...
Xy realResult = xyList[calculatedIndex];
proxyResult = (new ProxyGenerator()).CreateClassProxyWithTarget<XY>(realResult, new XYInterceptor());
return proxyResult;
}
}
}
我的第三个草图的一个烦人的方面是它没有用新的 XY 更新结果。在它真正准备好使用之前,需要让它发挥作用。