EF5:如何只返回外键?

时间:2013-09-26 23:45:47

标签: c# ef-code-first entity-framework-5

我在N层应用程序中使用EF5代码优先与WCF结合使用。

我是异步并逐步加载客户端和服务器之间的相关实体。一切都运作良好,但我想做一些优化。

考虑一个虚构的实体Car,其中包含相关实体Wheel

public class Car
{
    public int Id { get; set; }        
    public virtual List<Wheel> Wheels { get; set; }
}

public class Wheel
{
    public int Id { get; set; }        
    public virtual int CarId { get; set; }
}

相关的DTO:

public class CarDTO
{
    public int Id { get; set; }
    public virtual int CarId { get; set; }
    public virtual List<int> Wheels { get; set; }
}

请注意,Wheels是DTO中的外键列表。我不需要通过Web服务传输每个Wheel对象 - 如果需要,客户端将在稍后的WCF调用中加载它。现在我正在使用AutoMapper将相关实体展平为FK列表。

这样做的问题是我的Web服务并不总是需要在检索DbContext对象时从Car加载整个Wheel对象。我无论如何都不会通过网络服务发送它们(除非后来要求它们)。但是查询将整个wheel对象从数据库加载到Web服务调用中,只是被丢弃。如果每Wheels有100 Car,则表示Web服务和数据库之间存在大量不必要的数据。

我想做的是将Car更改为以下内容:

public class Car
{
    public int Id { get; set; }        
    public virtual List<Wheel> Wheels { get; set; }  // Normally empty
    public virtual List<int> WheelIDs { get; set; }  // Normally full
}

因此,当Web服务知道它需要加载所有Wheels时,它可以通过向查询添加.Include(car => car.Wheel)来实现,但通常只需返回{{{{}}中的FK列表1}}可以在以后检索。

EF5会轻易做到这一点吗?如果是这样,怎么样?

1 个答案:

答案 0 :(得分:0)

替换

public virtual List<Wheel> Wheels { get; set; }

public virtual ICollection<Wheel> Wheels { get; set; }

这样,EntityFramework会将其理解为延迟加载的子列表,并且在需要之前不会填充值。

通过使用LinqToEntities,您只能将密钥检索到新列表中,例如:

public virtual List<int> WheelIDs
{
   get { return Wheels.Select(_w => _w.Id).ToList(); }
}

这不会加载轮子列表,但只返回他们的ID。

请记住,这只是猜测。 您的需求可能会发生变化,您可能需要每次都避免DB命中以检查车轮ID和所有,但这应该可以帮助您找到自己的方式。

编辑:这里有一些代码可以更好地使用IQueryable ......

{
   using(var context = new MyContext())
   {
      // Doing this the wheel collection WON'T GET LOADED
      var cars = context.Cars;
   }

   using(var context = new MyContext())
   {
      // Doing this the wheel collection WILL get loaded
      var cars = context.Cars.Include(_c => _c.Wheels);
   }

   using(var context = new MyContext())
   {
      // Doing this also gets the wheel collection loaded
      // But this time, it will retrieve wheel-by-wheel on each loop.
      var cars = context.Cars;
      foreach(var car in cars)
      {
         foreach(var wheel in wheels)
         { /* do something */ }
      }
   }

   using (var context = new MyContext())
   {
      // Doing this will get your id's without loading the entire wheel collection
      var cars = context.Cars;
      var wheelsIDs = cars.Wheels.Select(_w => _w.Id).ToList();
   }

   using (var context = new MyContext())
   {
      // Doing this will return every wheel for your car.
      // This improves performance over the other that retrieves wheel-by-wheel.
      var cars = context.Cars;
      foreach(var car in cars)
      {
         var wheels = car.Wheels.ToList();
      }
   }
}