在EF中的POCO中加载方法所需的导航属性

时间:2015-04-12 18:55:25

标签: c# entity-framework properties poco eager-loading

请参阅以下我的问题示例。

我从result方法收到Calculate()变量后,会处理EF上下文。如果我稍后在此DoMethod()上调用result,则会收到错误,因为未加载EF导航属性SomeObjects

我能想到以下解决方案来防止这个问题吗?

  1. SomeObjects方法(Calculate())内急切地加载xyList = context.Xys.Include(x => x.SomeObjects).ToList();(如果以后不再使用,此属性的不必要负载)
  2. 不要关闭数据库上下文或使用全局上下文(非常糟糕!)
  3. DoMethod()
  4. 中加载缺少的EF导航属性

    我会选择第三个,因为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;
       }
    }
    

1 个答案:

答案 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更新结果。在它真正准备好使用之前,需要做好准备。

相关问题