Reputation: 851
I am completely new to Entity Framework. I have created a model to watch services on remote machines. Currently it looks like this:
Note: I have done my best to use understandable generic names since this is from a corporate environment.
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; }
}
With context class:
public class MyDBContext : DbContext
{
public DbSet<RemoteEnvironment> RemoteEnvironments { get; set; }
public DbSet<RemoteService> RemoteServices { get; set; }
}
This model works fine when creating the database and seeding it. The RemoteService
class also contains this Version
property, to get the version from the 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
{
}
}
This also works fine when creating and seeding the database. My issue appears when I try to grab data from the database. Something like this:
using (MyDBContext context = new MyDBContext())
{
var serviceNames = new List<string>();
foreach (RemoteService service in context.RemoteServices)
{
serviceNames.Add(service.Name);
}
}
While executing the above code I get a NullReferenceException
, because RunningAt
is null.
From debugging I have gathered that it is when I grab service
from context.RemoteServices
that it fetches all the properties. First Name
, then MachineName
, skips RunningAt
(why? Lazy Loading?) and then tries to get Version
, but since RunningAt
is null, it can't.
I have tried defining the RunningAt
get and set methods using
private RemoteEnvironment _runningAt
but the issue stays the same, both RunningAt
and _runningAt
are null while fetching data from the database.
It seems that I somehow need to load the reference to the RemoteEnvironment
from the database in the get of Version
. But creating a context here seems a bit much.
What else can I do? I would really like to keep Version
as is. If it can't be done, I will have to make a workaround. Checking version separately, actively getting and setting it instead.
Edit 1:
I have now tried three different ways to "pre-load" the RemoteEnvironments
.
First:
"Pre-loading" by getting all RemoteEnvironments
from database.
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);
}
}
Still getting NullReferenceException since RunningAt is null.
Second: Removing the virtual keyword.
[ForeignKey("MachineName")]
public RemoteEnvironment RunningAt { get; set; } // virtual keyword removed
Still getting NullReferenceException since RunningAt is null.
Third:
Using Include
to force eagerly loading.
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);
}
}
Still getting NullReferenceException since RunningAt is null.
I have even tried all combinations of the above, including all three at the same time. Still getting NullReferenceException since RunningAt is null.
I also tried going the other way, through RemoteEnvironments
and including 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);
}
}
Still getting NullReferenceException since RunningAt is null. Only this time it happens while the environments
call is trying to include them.
What I want to do does not seem possible. Using the referenced RunningAt
property within the Version
property, because it is never loaded from the database. Not even when I force it to load the RemoteEnvironments
before the call to the RemoteService
and subsequently the call to Version
.
Upvotes: 0
Views: 910
Reputation: 851
What I am trying to do does not seem possible. The core issue is having a navigation property and then referencing it in another property's get or set method. This does not seem to work. I have tried turning off lazy loading, forcing loading with Include
and also pre-loading the DbSet which the navigation property points to. In each and every case the navigation property reference, in my case RunningAt
, has been null inside the other property's get/set method.
The Entity Framework does not seem to support referencing a navigation property inside another property. If anyone knows otherwise please tell me how. In the mean time, this will be the accepted answer.
Upvotes: 1