c# – 实体框架导航属性未加载直到新的DbContext已建立

我对EF来说还是比较新的,所以请原谅我,如果我错过了一个明显的概念.

让我看看我是否可以简化这个…老问题在编辑历史中,但我想我可以将其提炼出来:

FWIW,DbContext是PER REQUEST,而不是静态,这就是第一个例子有效的原因.此时不在控制器上使用DI / IoC.

public class OrdersController : ApiController {
    private MyDbContext db = new MyDbContext();

    //controller methods....

    protected override void Dispose(bool disposing) {
      db.Dispose();
    }
}

作品(2个独立请求):

//request 1: client code sends in a new order to be added to db
public Order Post([FromBody]Order order) {
  db.Orders.Add(order);
  db.SaveChanges();

  return order;
}

//request 2: someone punches a button to send an email to CS about this order
public void NotifyCustomerService(int orderid) {
  var order = db.Orders.Find(orderid);
  //do email code here
}

破碎(单一请求):

//request: client code sends in a new order to be added to db AND notifies CS at same time
public Order Post([FromBody]Order order) {
  db.Orders.Add(order);
  db.SaveChanges();

  //notify CS via email here (nav properties are not populating)

  return order;
}

作品(单一请求)(但我知道这是一种可怕的做法):

//request: client code sends in a new order to be added to db AND notifies CS at same time (using a new dbcontext in the notification code)
public Order Post([FromBody]Order order) {
  db.Orders.Add(order);
  db.SaveChanges();

  using(var db2 = new MyDbContext()) {
    var sameOrderWTF = db.Orders.Find(order.ID);
    //notify CS via email using sameOrderWTF instance here (nav properties ARE populating)
  }
  return order;
}

因此,在我看来,在上下文中添加一个新的前所未见的实体会产生一些副作用,然后尝试将其导航属性设置为填充.但是如果你创建一个新的DbContext …即使在同一个请求中,它也必须直接命中该实体的数据库,而不是使用内存副本,这样导航属性就会神奇地起作用.那是让我难过的部分.

工作方案

//request: client code sends in a new order to be added to db AND notifies CS at same time
public Order Post([FromBody]Order o) {
  Order order = db.Orders.Create();
  db.Orders.Add(order);

  //copy values from input to proxy instance
  db.Entry(order).CurrentValues.SetValues(o);

  //copy input lines to proxy instance (same process as order for each line)
  o.OrderLines.ToList().ForEach(l => {
    var line = db.OrderLines.Create();
    db.OrderLines.Add(line);
    db.Entry(line).CurrentValues.SetValues(l);
    order.OrderLines.Add(line);
  });

  db.SaveChanges();

  //notify CS via email here (nav properties are not populating)

  return order;
}

因此,虽然我们会考虑回答这个问题(感谢Uber Bot),但是对于ASP.NET MVC和EF来说,经历所有这些问题的需要似乎比我的其他(通常是简短的)经验更加费力.我想也许我应该使用ViewModel并将VM属性映射到代理实例,而不是直接尝试使用EF类,但我真的看不到像这样简单调用的好处.

最佳答案 您的新订单实体实例未被代理包装,因此延迟加载将不起作用.

您可以强制上下文加载导航属性.

db.Entry(order).Reference(o => o.YouProperty).Load();

或者您可以使用上下文的工厂来创建实例来克服此问题.

db.Orders.Create();

A proxy instance will not be created if you create an instance of an
entity using the new operator. This may not be a problem, but if you
need to create a proxy instance (for example, so that lazy loading or
proxy change tracking will work) then you can do so using the Create
method of DbSet.

https://msdn.microsoft.com/en-us/data/jj592886.aspx

点赞