【问题标题】:Load a navigation property value using entity framework core without going through DB context使用实体框架核心加载导航属性值而不通过数据库上下文
【发布时间】:2023-01-26 23:00:38
【问题描述】:

TL;DR:在实体上加载单个导航属性的最简洁方法是什么?

假设我已经有一个实例实体 Foo 和一个孩子 ChildFoo 的实例我设置了ChildId,但未加载Child,即foo.ChildId == 1234foo.Child == null

如果它丢失了,我想得到Child。我知道我能做到:

if (foo.Child is null) {
    foo.Child = _dbContext.Foos.Include(f => f.Child).Single(f => f.Id == foo.Id).Child;
}

但我正在寻找一种懒惰的方式(双关语!)按需加载它(我不想按需加载所有属性,但是,只是我想显式加载的属性),比如:

var child = _dbContext.EnsureLoaded(da, e => e.Child);

有没有办法做到这一点?

【问题讨论】:

  • 你检查过文档中的Lazy Loading了吗?如果 Child 为 null,则表示未启用延迟加载并且 Child 属性不是 virtual

标签: entity-framework-core entity-framework-core-7.0


【解决方案1】:

可能你正在寻找Explicit Loading of Related Data

_dbContext.Entry(foo).Reference(f => f.Child).Load();

【讨论】:

    【解决方案2】:

    Lazy Loading 已经有空了。有两种选择:

    • 使用 EF Core 生成的代理对象自动加载相关实体或
    • ILazyLoader服务与POCO一起使用,在请求时加载相关实体

    代理

    要使用代理,必须先配置 DbContext:

    .AddDbContext<BloggingContext>(
        b => b.UseLazyLoadingProxies()
              .UseSqlServer(myConnectionString));
    

    之后,任何需要延迟加载的属性都必须设置为virtual

    public class Blog
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public virtual ICollection<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    
        public virtual Blog Blog { get; set; }
    }
    

    在运行时,EF 将返回继承自实体类的代理对象,并在首次请求时重载惰性属性以加载相关对象。

    ILazyLoader 服务

    另一个不需要继承的选项是使用 POCO 和 ILazyLoader 服务在需要时加载实体:

    public class Blog
    {
        private ICollection<Post> _posts;
    
        public Blog()
        {
        }
    
        private Blog(ILazyLoader lazyLoader)
        {
            LazyLoader = lazyLoader;
        }
    
        private ILazyLoader LazyLoader { get; set; }
    
        public int Id { get; set; }
        public string Name { get; set; }
    
        public ICollection<Post> Posts
        {
            get => LazyLoader.Load(this, ref _posts);
            set => _posts = value;
        }
    }
    

    这会添加对 ILazyLoader 接口本身的依赖,这又会在域或业务模型中添加对 EF Core 的依赖。

    这可以通过将加载程序作为拉姆达,以及一些约定的魔法:

    public class Blog
    {
        private ICollection<Post> _posts;
    
        public Blog()
        {
        }
    
        private Blog(Action<object, string> lazyLoader)
        {
            LazyLoader = lazyLoader;
        }
    
        private Action<object, string> LazyLoader { get; set; }
    
        public int Id { get; set; }
        public string Name { get; set; }
    
        public ICollection<Post> Posts
        {
            get => LazyLoader.Load(this, ref _posts);
            set => _posts = value;
        }
    }
    

    这与实际使用属性名称调用加载程序并设置其支持字段的扩展方法结合使用:

    public static class PocoLoadingExtensions
    {
        public static TRelated Load<TRelated>(
            this Action<object, string> loader,
            object entity,
            ref TRelated navigationField,
            [CallerMemberName] string navigationName = null)
            where TRelated : class
        {
            loader?.Invoke(entity, navigationName);
    
            return navigationField;
        }
    }
    

    正如文档警告的那样:

    延迟加载委托的构造函数参数必须称为“lazyLoader”。计划在未来版本中使用与此不同的名称进行配置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多