Skip to content

Question: Best practices for providing encapsulation of collection navigation properties with lazy loading #22752

Closed as not planned
@JamesMenetrey

Description

Question

Dear EF Core community,

I would like to provide encapsulation with my collection navigation properties while using lazy loading. For that purpose, I usually expose an IReadOnlyList for my public property while being backing by a backing field. EF Core does not lazy load the value of my backing field, so querying it result of an empty list. What are the best practices in EF Core 3 or 5 to gatekeep the alterations of my collections while using lazy loading? I provide a small example below to work on.

Code

    public class Foo
    {
        private readonly List<Bar> _bars; // I would like to gatekeep the changes of that collection

        public Foo(long id)
        {
            Id = id;
            _bars = new List<Bar>();
        }

        public virtual IReadOnlyList<Bar> Bars => _bars.AsReadOnly();

        public long Id { get; set; }

        public int NumberOfBars => _bars.Count; // A dummy example of extracting some info from the backing field. Note that in reality, this may alter the collection.

        public void AddBar(Bar bar) // This method gatekeeps the changes in the collection
        {
            _bars.Add(bar);
        }
    }

    public class Bar
    {
        public long Id { get; set; }

        public Bar(long id)
        {
            Id = id;
        }
    }
    private static void Main(string[] args)
    {
        using (var db = new DbContext())
        {
            var foo = new Foo(1);
            foo.AddBar(new Bar(1));
            foo.AddBar(new Bar(2));

            db.Foos.Add(foo);

            db.SaveChanges();
        }

        using (var db = new DbContext())
        {
            var f = db.Foos.First();
            Console.WriteLine(f.NumberOfBars); // Print 0, while I would like 2 to be printed
        }
    }

Output

The application prints 0, while 2 is printed when querying the navigation property instead. I understand this is due to how lazy loading is working, by subclassing Foo at runtime. I would like to know the recommended way to structure my classes to provide encapsulation while still using lazy loading. Feel free to change my structure to orient me in the right direction.

Version information

EF Core version: 3.1.8
Database provider: PostgreSQL
Target framework: .NET Core 3.1
Operating system: Windows
IDE: Visual Studio 2019 16.7.2

Thanks!

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions