I have an EmailMessage class, as follows...
c#
public class EmailMessage {
public int Id { get; set; }
public User User { get; set; } = new();
public DateTime Date { get; set; }
public int FromId { get; set; }
public EmailMessageRecipient From { get; set; } = new();
public List<EmailMessageRecipient> Tos { get; set; } = new();
public List<EmailMessageRecipient> Ccs { get; set; } = new();
public EmailMessageRecipient IncomingBcc { get; set; } = new();
public List<EmailMessageRecipient> OutgoingBccs { get; set; } = new();
public string Subject { get; set; }
public string Body { get; set; }
}
...where EmailMessageRecipient looks like this...
c#
public class EmailMessageRecipient {
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int? EmailMessageToId { get; set; }
public virtual EmailMessage EmailMessageTo { get; set; }
public int? EmailMessageCcId { get; set; }
public virtual EmailMessage EmailMessageCc { get; set; }
}
The reason for all the links between the two is that an email will have a From address, and a number (possibly zero) of CC and To addresses. It may also have a BCC address (only one, as the point of BCC is that you don't see the other people BCCed).
The DbContext class contains the following two lines...
c#
public virtual DbSet<EmailMessage> EmailMessages { get; set; }
public virtual DbSet<EmailMessageRecipient> EmailMessageRecipients { get; set; }
...and OnModelCreating includes the following...
c#
builder.Entity<EmailMessage>(entity => {
entity.HasMany(m => m.Tos)
.WithOne(r => r.EmailMessageTo)
.OnDelete(DeleteBehavior.ClientSetNull);
entity.HasMany(m => m.Ccs)
.WithOne(r => r.EmailMessageCc)
.OnDelete(DeleteBehavior.ClientSetNull);
});
There's nothing else in there at all related to these two classes.
The data for a single message is loaded with the following code...
c#
_msg = await AppDbContext.EmailMessages
.Include(em => em.From)
// Other .Include statements here
.FirstOrDefaultAsync(m => m.Id == Id);
However, when the message loads, the From navigation property is always a default instance, meaning it is not null, but has an Id of zero, and the two string properties are null.
I ran SQL Server profiler, and looked at the SQL that was used. Running that in a Query Analyser window showed the From Id, Name and Address included correctly in the returned data, so it looks like the data is being sent correctly to the code, but that EF creating the From entity, but is not populating it with the data.
When I did the original migration for these classes, I didn't include a FromId property in the EmailMessage class. When I updated the database, EF created a column in the table, and added a foreign key reference as well. If I query the database from LinqPad, I can see the related data correctly.
As an experiment, I added a FromId property to the EmailMessage class. The migration code that EF generated showed that it was dropping the existing foreign key and recreating exactly the same thing. Nevertheless, just to be sure, I updated the database and tried running again. I could see the correct FromId value on the EmailMessage entity, but again, the From entity had a zero id and null string properties.
Anyone any idea what's going on here? I've done this sort of thing countless times before, and never seen this happen.