Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose loader IQueryable on ILazyLoader #23166

Open
Tracked by #22954
arti1zz opened this issue Nov 1, 2020 · 3 comments
Open
Tracked by #22954

Expose loader IQueryable on ILazyLoader #23166

arti1zz opened this issue Nov 1, 2020 · 3 comments

Comments

@arti1zz
Copy link

arti1zz commented Nov 1, 2020

Hi,

I have entity which contains other entities inside. I'm using ILazyLoader to load them when needed, but I want to filter them. How can I filter them?

Lets say I have:

 public class Lesson : EntityBase
    {
	    private readonly ILazyLoader _lazyLoader;

	    public Lesson(ILazyLoader lazyLoader)
	    {
		    _lazyLoader = lazyLoader;
	    }

            [Required]
		public List<Topic> Topics
		{
			get => _lazyLoader.Load(this, ref _topics);
			set => _topics = value;
		}

              private List<Topic> _topics;

I want to access lesson.Grades, but my grades should be filtered by IsValid field.

At the moment it will load all the grades.

I understand I can do _lazyLoader.Load(this, ref _topics).Where(x => x.IsValid).ToList();, but I want filter in database level

Is this possible?

Include provider and version information

EF Core version: 3.1.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: Core 3.1

@ajcvickers
Copy link
Member

Note for triage: we should consider exposing the underlying queryable on ILazyLoader.

@arti1zz There isn't currently a way to do this with ILazyLoader, but you can inject the DbContext to achieve this--see example code below. Note, however, that this does introduce a strong coupling of your entity types to DbContext.

public class Lesson
{
    private readonly DbContext _context;

    public Lesson()
    {
    }

    public Lesson(DbContext context)
    {
        _context = context;
    }

    public int Id { get; set; }

    [Required]
    public List<Topic> Topics
    {
        get
        {
            if (_topics == null
                && _context != null)
            {
                var collectionEntry = _context.Entry(this).Collection(e => e.Topics);
                collectionEntry.Query().Where(e => e.IsValid).Load();
                collectionEntry.IsLoaded = true;
            }

            return _topics;
        }
        set => _topics = value;
    }

    private List<Topic> _topics;
}

public class Topic
{
    public int Id { get; set; }
    
    public bool IsValid { get; set; }
}

public class Program
{
    public static void Main()
    {
        using (var context = new SomeDbContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            context.Add(
                new Lesson
                {
                    Topics = new List<Topic>
                    {
                        new Topic(),
                        new Topic {IsValid = true}
                    }
                });
        
            context.SaveChanges();
        }
        
        using (var context = new SomeDbContext())
        {
            var lesson = context.Lessons.Single();
            Console.WriteLine(lesson.Topics.Count);
        }
    }
}

public class SomeDbContext : DbContext
{
    public DbSet<Lesson> Lessons { get; set; }
    public DbSet<Topic> Topics { get; set; }

    private static ILoggerFactory ContextLoggerFactory
        => LoggerFactory.Create(b => b.AddConsole().SetMinimumLevel(LogLevel.Information));

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer(Your.ConnectionString)
            .EnableSensitiveDataLogging()
            .UseLoggerFactory(ContextLoggerFactory);
}

@arti1zz
Copy link
Author

arti1zz commented Nov 3, 2020

@ajcvickers thanks for info. Hopefully we will see this upgrade!

@arti1zz arti1zz closed this as completed Nov 4, 2020
@ajcvickers ajcvickers reopened this Nov 4, 2020
@arti1zz
Copy link
Author

arti1zz commented Nov 4, 2020

I have found more delicate solution if you don't want to parse soft deleted entities ever.

on context OnModelCreating you could do something like

builder.Entity<Topic>().HasQueryFilter(x => x.IsValid);

@ajcvickers ajcvickers changed the title How to filter second level entities with ILazyLoader? Expose loader IQueryable on ILazyLoader Nov 5, 2020
@ajcvickers ajcvickers added this to the Backlog milestone Nov 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants