Closed alex-samuilov closed 1 month ago
I'm not sure I understand what are you trying to achieve. But basically your Fields
property is of type ParameterCollection<FieldScheme>
, yet the backing field _fields
is ICollection<FieldScheme>
/List<FieldScheme>
.
As I'm not sure what is your goal, it's hard say what direction to take. One option would be to have not mapped property for ParameterCollection<FieldScheme>
and regular ICollection<FieldScheme>
for rest of what EF is doing. Something like this:
using var db = new MyContext();
await db.Database.EnsureDeletedAsync();
await db.Database.EnsureCreatedAsync();
var contactScheme = new ObjectScheme()
{
Name = "test",
FriendlyFields = new(
[
new FieldScheme { Name = "test" }
]),
};
db.Set<ObjectScheme>().Add(contactScheme);
public interface IFriendlyId
{
public string FriendlyId { get; }
}
public class ParameterCollection<T> : IReadOnlyCollection<T> where T : IFriendlyId
{
private readonly Dictionary<string, T> _items;
public ParameterCollection(IEnumerable<T> parameters)
{
_items = new Dictionary<string, T>(parameters.Select(parameter => new KeyValuePair<string, T>(parameter.FriendlyId, parameter)));
}
public T this[string name] => _items[name];
public IEnumerator<T> GetEnumerator() => _items.Values.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _items.Values.GetEnumerator();
public int Count => _items.Count;
}
public class FieldScheme : IFriendlyId
{
public Guid Id { get; set; }
public required string Name { get; init; }
public string FriendlyId => Name;
}
public class ObjectScheme
{
public Guid Id { get; set; }
public required string Name { get; init; }
private readonly ICollection<FieldScheme> _fields = new List<FieldScheme>();
public required ParameterCollection<FieldScheme> FriendlyFields
{
get => new(_fields);
init => _fields = new List<FieldScheme>(value);
}
public ICollection<FieldScheme> Fields
{
get => _fields;
init => _fields = value;
}
}
class MyContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlite("Data Source=test.db");
optionsBuilder.LogTo(Console.WriteLine);
optionsBuilder.EnableSensitiveDataLogging();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ObjectScheme>(Configure);
void Configure(EntityTypeBuilder<ObjectScheme> builder)
{
builder.HasKey(t => t.Id);
builder.HasMany(t => t.Fields)
.WithMany();
builder.Property(t => t.Name).IsRequired();
builder.Ignore(x => x.FriendlyFields);
}
}
}
@cincuranet, thank you for your time.
I want to allow clients of a class to only access fields through the ParameterCollection
. I can of course change the entity configuration to directly store the _fields
something like that:
builder.Ignore(t => t.Fields);
builder.HasMany("_fields").WithMany();
And it works. However, my question is more about why it doesn't work in my original case. Moreover, EF Core validates the configuration (at least the System.InvalidOperationException
exception is not thrown through the Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ThrowPropertyNotMappedException
method), and correctly finds the backing field (_fields
). And since EF Core found the backing field, then what is the difference between my original code and the configuration that uses _field
directly?
why it doesn't work in my original case
Because Fields
property is not ignored there.
exception is not thrown
That's fine. Because you can have ICollection<FieldScheme>
that would cast to ParameterCollection<FieldScheme>
.
And since EF Core found the backing field, then what is the difference between my original code and the configuration that uses _field directly?
Now the Field
, because it is ignored, cannot be used for i.e. querying or any data manipulation through EF Core. For EF Core it's like the property is not there.
Hello everyone!
I introduced a custom collection for accessing parameters through its friendly ID -
ParameterCollection
(in fact, the collection works with all entities that have this “friendly id”):And I'm trying to use it as a navigation collection for an
ObjectScheme
entity consisting of multipleFieldSchemes
:Entity configuration is following:
My DbContext:
However, when adding an entity (
dbContext.ObjectSchemes.Add(contactScheme)
), an exception is thrown:Adding conversion methods from
List
toParameterCollection
also doesn't give a positive result - they aren't even called.My model is:
As far as I can see, EF Core found a backing field (
_fields
) in the navigation collection. But if you explicitly configure the entity to save the_fields
field instead of theFields
property, the exception above isn't thrown.I would be glad for any advice on what I'm doing wrong.
Thank you in advance.
Additional information: EF Core version: 8.0.7 Database provider: Npgsql.EntityFrameworkCore.PostgreSQL, version 8.0.4 Target framework: .NET 8.0 Operating system: macOS Sonoma 14.5 IDE: Rider 2024.1