【问题标题】:Entity Framework core .Include() issue实体框架核心 .Include() 问题
【发布时间】:2016-11-27 19:05:18
【问题描述】:

一直在玩 ef core 并且遇到了 include 语句的问题。对于这段代码,我得到了 2 家公司,这是我所期望的。

public IEnumerable<Company> GetAllCompanies(HsDbContext db)
{
    var c = db.Company;
    return c;
}

返回

[
    {
        "id":1,
        "companyName":"new",
        "admins":null,
        "employees":null,
        "courses":null
    },
    {
        "id":2,
        "companyName":"Test Company",
        "admins":null,
        "employees":null,
        "courses":null
    }
]

如您所见,有 2 家公司,所有相关属性均为空,因为我没有使用任何包含,这是我所期望的。现在,当我将方法更新为此:

public IEnumerable<Company> GetAllCompanies(HsDbContext db)
{
    var c = db.Company
        .Include(t => t.Employees)
        .Include(t => t.Admins)
        .ToList();

    return c;
}

这是它返回的内容:

[
    {
        "id":1,
        "companyName":"new",
        "admins":[
            {
                "id":2,
                "forename":"User",
                "surname":"1",
                "companyId":1
            }
        ]
    }
]

它只返回一家公司,并且只包括管理员。为什么不包括这两家公司及其员工?

public class Company
{
    public int Id { get; set; }
    public string CompanyName { get; set; }
    public List<Admin> Admins { get; set; }
    public List<Employee> Employees { get; set; }
    public List<Course> Courses { get; set; }

    public string GetFullName()
    {
        return CompanyName;
    }
}

public class Employee
{
    public int Id { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public int CompanyId { get; set; }
    [ForeignKey("CompanyId")]
    public Company company { get; set; }

    public ICollection<EmployeeCourse> Employeecourses { get; set; }
}

public class Admin
{
    public int Id { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public int CompanyId { get; set; }
    [ForeignKey("CompanyId")]
    public Company Company { get; set; }
}

【问题讨论】:

  • 您是否尝试过使用 EF 6 或更低版本的相同代码?
  • 你的 EF 版本是什么?
  • @Mafii,它是 EF Core - EF 7,见标题
  • 你能包括你的类定义和映射吗?预期什么?
  • 我和@JohnMorrison 有同样的问题,虽然下面@MohammadAkbari 接受的答案确实解决了这个问题,但这意味着我的模型失去了任何类型的强类型。 MS 文档 (docs.microsoft.com/en-us/ef/core/querying/related-data) 表明 include 应该可以工作,以及如何使用 Entry API 来实现它,尽管出于各种原因,我宁愿不在我的 API 中公开上下文。我将DbContext 注入到我的存储库中。关于导致此问题的原因以及如何解决它以使Include() 按预期运行的任何进一步的想法?

标签: c# entity-framework-core


【解决方案1】:

我不确定您是否已经看到对此question 的公认答案,但问题在于 JSON 序列化程序如何处理循环引用。完整的详细信息和更多参考的链接可以在上面的链接中找到,我建议深入研究这些,但简而言之,将以下内容添加到 startup.cs 将配置序列化程序以忽略循环引用:

services.AddMvc()
    .AddJsonOptions(options => {
        options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    });

【讨论】:

  • 我认为这个问题与 JSON 无关。
  • 我不同意,尽管@JohnMorrison 完全有可能只使用 JSON 作为一种方便的方式来表达输出。它确实看起来像一个序列化程序引用循环处理问题,但由于使用 JSON 来显示输出,它看起来很可能是这样的。为什么不在投票前让 OP 说明情况是否如此?
  • 准确地说,这是修复它的方法。非常感谢!
  • +1 提供了一个非常好的提示,对我很有帮助。现在,我尝试将处理设置为 Error 而不是 Ignore 只是为了看看会发生什么,但没有抛出异常。邮递员一无所获(甚至没有包含未填充子字段的列表)。但是没有 500 状态或任何反馈。当我尝试在本地执行代码并对其进行断点时,VS 也不会弹出任何内容。想过吗? (将 Core 2/EF 7 用于 COre 2。)
  • 我同意@Langdon。这显然是早期 EF 核心版本中的一个错误。后来的评论者没有相同的版本。
【解决方案2】:

EF Core 尚无法进行延迟加载。 Refer here.

您也可以使用预先加载。

阅读此article

下面是我创建的用于实现预加载的扩展方法。

扩展方法:

public static IQueryable<TEntity> IncludeMultiple<TEntity, TProperty>(
    this IQueryable<TEntity> source,
    List<Expression<Func<TEntity, TProperty>>> navigationPropertyPath) where TEntity : class
{
    foreach (var navExpression in navigationPropertyPath)
    {
        source= source.Include(navExpression);
    }
    return source.AsQueryable();
}

存储库调用:

public async Task<TEntity> FindOne(ISpecification<TEntity> spec)
{
    return await Task.Run(() => Context.Set<TEntity>().AsQueryable().IncludeMultiple(spec.IncludeExpression()).Where(spec.IsSatisfiedBy).FirstOrDefault());
}

用法:

List<object> nestedObjects = new List<object> {new Rules()};

ISpecification<Blog> blogSpec = new BlogSpec(blogId, nestedObjects); 

var challenge = await this._blogRepository.FindOne(blogSpec);

依赖关系:

public class BlogSpec : SpecificationBase<Blog>
{
    readonly int _blogId;
    private readonly List<object> _nestedObjects;

    public ChallengeSpec(int blogid, List<object> nestedObjects)
    {
        this._blogId = blogid;
        _nestedObjects = nestedObjects;
    }

    public override Expression<Func<Challenge, bool>> SpecExpression
    {
        get { return blogSpec => blogSpec.Id == this._blogId; }
    }

    public override List<Expression<Func<Blog, object>>> IncludeExpression()
    {
        List<Expression<Func<Blog, object>>> tobeIncluded = new List<Expression<Func<Blog, object>>>();
        if (_nestedObjects != null)
            foreach (var nestedObject in _nestedObjects)
            {
                if (nestedObject is Rules)
                {
                    Expression<Func<Blog, object>> expr = blog => blog.Rules;
                    tobeIncluded.Add(expr);
                }
                
            }

        return tobeIncluded;
    }
}

如果有帮助会很高兴。请注意,这不是生产就绪代码。

【讨论】:

    【解决方案3】:

    我测试你的代码,这个问题在我的测试中存在。在这篇帖子中LINK 建议使用数据投影。对于您的问题,如下所示是可行的。

    [HttpGet]
    public dynamic Get()
    {
        var dbContext = new ApplicationContext();
    
        var result = dbContext.Companies
            .Select(e => new { e.CompanyName, e.Id, e.Employees, e.Admins })
            .ToList();
    
        return result;
    }
    

    【讨论】:

    • 是的,这种方式有效。谢谢。那么您认为实体框架上的延迟加载是个问题吗?
    • 当使用include加载方式为eagerloading时,第一次查询时加载数据
    【解决方案4】:

    确保您使用的是“Microsoft.EntityFrameworkCore”中的包含和“System.Data.Entity”中的Not

    【讨论】:

      【解决方案5】:

      我知道这是一个老问题,但它在 google 中的结果最高,所以我把我找到的解决方案放在这里。对于 Core 3.1 Web 项目,有一个快速修复。 添加 nuget 包 Microsoft.EntityFrameworkCore.Proxies。 然后,您只需在配置服务时在选项生成器中指定。文档:https://docs.microsoft.com/en-us/ef/core/querying/related-data

      public void ConfigureServices(IServiceCollection services) {    
          services.AddDbContextPool<YourDbContext>(options => {
              options.UseLazyLoadingProxies();
              options.UseSqlServer(this.Configuration.GetConnectionString("MyCon"));
          });
      }
      

      现在您的延迟加载应该可以像在以前的 EF 版本中那样工作了。 如果您不将它用于 Web 项目,则可以直接在 DbContext 本身的 OnConfiguringMethod 内部进行。

      protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
          optionsBuilder.UseLazyLoadingProxies();
      }
      

      我的 EF 内容保存在一个单独的类库中,因此我可以通过多个公司应用程序重复使用它。因此,在特定应用程序不需要时能够不延迟加载是很有用的。因此,出于重用能力的目的,我更喜欢传入构建选项。但两者都可以。

      【讨论】:

      • 这显然是早期版本的 EF 内核中的一个错误。它只是没有返回所有公司,那么延迟加载会如何改变呢?此外,在序列化实体时,延迟加载是您最不想发生的事情。
      猜你喜欢
      • 2021-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-09
      • 1970-01-01
      • 2021-08-23
      • 1970-01-01
      • 2021-07-14
      相关资源
      最近更新 更多