请参阅以下我的问题示例。
我从result
方法收到Calculate()
变量后,会处理EF上下文。如果我稍后在此DoMethod()
上调用result
,则会收到错误,因为未加载EF导航属性SomeObjects
。
我能想到以下解决方案来防止这个问题吗?
SomeObjects
方法(Calculate()
)内急切地加载xyList = context.Xys.Include(x => x.SomeObjects).ToList();
(如果以后不再使用,此属性的不必要负载)DoMethod()
我会选择第三个,因为DoMethod()
并不总是被调用,因此如果不是SomeObjects
我就不需要。{/ p>
我的问题是如何实现第三种解决方案?这是正确的方法吗?查询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;
}
}
答案 0 :(得分:1)
以下是我尝试或想过的一些选项。 #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。
除了加载的Context的内存开销之外,我还没有看到它的缺点。 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更新结果。在它真正准备好使用之前,需要做好准备。