-
Notifications
You must be signed in to change notification settings - Fork 2k
"non-nullable property with a nullable backing field" example doesn't work in practice #1994
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
@roji It looks like the guidance for non-nullable navigations is incorrect; As far as I can tell, the pattern described will not work most of the time. |
@Costo can you please try with EF Core 3.1? At least with the following trivial sample everything seems to work well, if you're still hitting and issue could you please post a similar code sample? class Program
{
static void Main(string[] args)
{
using (var ctx = new BlogContext())
{
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();
ctx.Blogs.Add(new Blog { Name = "Blog1" });
ctx.SaveChanges();
}
using (var ctx = new BlogContext())
{
var blogs = ctx.Blogs.ToList();
}
}
}
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(@"...");
}
public class Blog
{
string _name;
public int Id { get; set; }
public string Name
{
get => _name ?? throw new InvalidOperationException("Uninitialized property: " + nameof(Name));
set => _name = value;
}
} @ajcvickers in the latest EF source code I can't see ReadPropertyValue being called by SetProperty, so I'm guessing this is maybe a 2.2 issue? |
@roji There are plenty of places we read the property when it could be null--for example, in fixup. I just be missing something about why this pattern is expected to work for anything but trivial graphs. |
OK. We can discuss briefly in design, but if that's the way things work we should remove this guidance and just recommend to annotate the property as non-nullable (and initialize to null), with the assumption it will always be populated on entities returned by EF Core. It's not ideal but I guess that's that. |
@roji I modified your example slightly to add a I can confirm that this program works with EF Core 3.1 (no error), but when switching to EF Core 2.2, I get the exception "System.InvalidOperationException: Uninitialized property: Blog". #nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
class Program
{
static void Main(string[] args)
{
using (var ctx = new BlogContext())
{
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();
var blog = new Blog { Name = "Blog1" };
var post = new Post { Title = "Post Title", Blog = blog };
ctx.AddRange(blog, post);
ctx.SaveChanges();
}
using (var ctx = new BlogContext())
{
var blogs = ctx
.Blogs
.Include(x => x.Posts)
.ToList();
}
}
}
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; } = default!;
public DbSet<Post> Posts { get; set; } = default!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite("Data Source=mydb.db;");
}
public class Blog
{
public int Id { get; set; }
public string Name { get; set; } = default!;
public IList<Post> Posts { get; set; } = new List<Post>();
}
public class Post
{
private Blog? _blog;
public int Id { get; set; }
public string Title { get; set; } = default!;
public Blog Blog
{
get => _blog ?? throw new InvalidOperationException("Uninitialized property: " + nameof(Blog));
set => _blog = value;
}
} |
@Costo thanks for testing - so do I understand correctly that you have no issues whatsoever on 3.1? @ajcvickers I understand you're thinking of our scenarios where this would be a problem - even in 3.1 - so I'll keep this open for changing the guidance (out of curiosity, though, can you provide a quick example?). |
@roji Correct, I have no issues with this sample program on 3.1. |
@roji I missed that this is explicitly making use of the change we made in 3.0 to use backing fields directly. The reason EF is not calling the property getter here is because it knows the backing field and is configured to use it directly. It would probably be useful to update the documentation to make it explicit that:
|
@ajcvickers that makes perfect sense, submitted #2024 to add a note on this. |
Note: I'm using EF Core 2.2
I tried to apply the pattern described in this article of using a backing field for non-nullable navigation properties:
When I do this, I get an error because EF reads the property when materializing entities: "System.InvalidOperationException: Uninitialized property: RouteDetail"
Call stack:
Document Details
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
The text was updated successfully, but these errors were encountered: