Entity Framework Code First 在内部使用虚拟 属性,调用时为空引用

Entity Framework Code First using virtual property internally, null reference when called

我对 Entity Framework 完全陌生。我创建了一个模型来监视远程机器上的服务。目前看起来像这样:

注意:我已尽力使用易于理解的通用名称,因为这是来自公司环境。

public class RemoteService
{
    [Key, Column(Order = 0)]
    public string Name { get; set; }

    [Key, Column(Order = 1)]
    public string MachineName { get; set; }

    [ForeignKey("MachineName")]
    public virtual RemoteEnvironment RunningAt { get; set; }
}

public class RemoteEnvironment
{
    [Key]
    public string MachineName { get; set; }

    public string Configuration { get; set; }

    public string EnvironmentNr { get; set; }

    public virtual ICollection<RemoteService> Services { get; set; }
}

有上下文 class:

public class MyDBContext : DbContext
{
    public DbSet<RemoteEnvironment> RemoteEnvironments { get; set; }
    public DbSet<RemoteService> RemoteServices { get; set; }
}

此模型在创建数据库和播种时运行良好。 RemoteService class 还包含此 Version 属性,以从 Octopus Deploy API.

获取版本
private string _version = "N/A";
public string Version
{
    get
    {
        var item = GetItemFromOctopus(Name, RunningAt.EnvironmentNr);
        if (item != null)
        {
            _version = item.ReleaseVersion;
        }
        return _version;
    }
    set
    {
    }
}

这在创建和播种数据库时也能正常工作。当我尝试从数据库中获取数据时出现我的问题。像这样:

using (MyDBContext context = new MyDBContext())
{
    var serviceNames = new List<string>(); 
    foreach (RemoteService service in context.RemoteServices)
    {
        serviceNames.Add(service.Name); 
    }
}

在执行上面的代码时我得到一个 NullReferenceException,因为 RunningAt 是空的。

从调试中我了解到,当我从 context.RemoteServices 中获取 service 时,它会获取所有属性。首先 Name,然后 MachineName,跳过 RunningAt (为什么?延迟加载?) 然后尝试获取 Version,但是由于RunningAt 为空,不能。

我尝试使用

定义RunningAt获取和设置方法
private RemoteEnvironment _runningAt 

但问题依然存在,从数据库中获取数据时,RunningAt_runningAt 均为 null。

似乎我需要以某种方式在 Version 的获取中从数据库加载对 RemoteEnvironment 的引用。但是在这里创建上下文似乎有点多。

我还能做什么?我真的很想保持 Version 不变。如果无法完成,我将不得不制定解决方法。单独检查版本,主动获取和设置它。

编辑 1: 我现在已经尝试了三种不同的方法来 "pre-load" RemoteEnvironments.

第一个: "Pre-loading" 通过从数据库中获取所有 RemoteEnvironments

using (MyDBContext context = new MyDBContext())
{
    var serviceNames = new List<string>();
    List<M3BEnvironment> activateEnv = db.M3BEnvvironments.ToList(); //added line
    foreach (RemoteService service in context.RemoteServices)
    {
        serviceNames.Add(service.Name); 
    }
}

由于 RunningAt 为空,仍然出现 NullReferenceException。

第二个: 删除虚拟关键字。

[ForeignKey("MachineName")]
public RemoteEnvironment RunningAt { get; set; } // virtual keyword removed

由于 RunningAt 为空,仍然出现 NullReferenceException。

第三个: 使用 Include 强制急切加载。

using (MyDBContext context = new MyDBContext())
{
    var serviceNames = new List<string>(); 
    var services = context.RemoteServices.Include(s => s.RunningAt).ToList();
    foreach (RemoteService service in services)
    {
        serviceNames.Add(service.Name); 
    }
}

由于 RunningAt 为空,仍然出现 NullReferenceException。

我什至尝试了以上的所有组合,包括同时使用所有三种。 仍然收到 NullReferenceException,因为 RunningAt 为空。

我也试过走另一条路,通过 RemoteEnvironments 并包括 Services:

using (MyDBContext context = new MyDBContext())
{
    var environments = context.RemoteEnvironments.Include(env => env.Services).ToList(); // NullRefereceException happens at this call
    var serviceNames = new List<string>(); 
    var services = context.RemoteServices.Include(s => s.RunningAt).ToList();
    foreach (RemoteService service in services)
    {
        serviceNames.Add(service.Name); 
    }
}

仍然收到 NullReferenceException,因为 RunningAt 为空。只有这一次它发生在 environments 调用试图包含它们时。

我想做的似乎不可能。Version 属性 中使用引用的 RunningAt 属性 ,因为它永远不会从数据库中加载。甚至当我强制它在调用 RemoteService 和随后调用 Version 之前加载 RemoteEnvironments 时也不行。

我想做的事情似乎不可能。核心问题是有一个导航属性然后在另一个属性中引用它'获取或设置方法。这似乎不起作用。我尝试关闭延迟加载,强制加载 Include 并预加载导航 属性 指向的 DbSet。在每一种情况下,导航 属性 引用,在我的例子中 RunningAt,在另一个 属性 的 get/set 方法中都是空的。

Entity Framework 似乎不支持在另一个 属性 中引用导航 属性。如果有人知道,请告诉我如何。同时,这将是公认的答案。