简体   繁体   English

实体框架核心 .Include() 问题

[英]Entity Framework core .Include() issue

Been having a play about with ef core and been having an issue with the include statement.一直在玩 ef core 并且对 include 语句有问题。 For this code I get 2 companies which is what i expected.对于此代码,我得到了 2 家公司,这正是我所期望的。

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

This returns这返回

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

As you can see there are 2 companies and all related properties are null as i havnt used any includes, which is what i expected.正如您所看到的,有 2 家公司,所有相关属性都为空,因为我没有使用任何包含,这正是我所期望的。 Now when I update the method to this:现在,当我将方法更新为:

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

    return c;
}

this is what it returns:这是它返回的内容:

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

It only returns one company and only includes the admins.它只返回一家公司并且只包括管理员。 Why did it not include the 2 companies and their employees?为什么不包括这两家公司及其员工?

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; }
}

I'm not sure if you've seen the accepted answer to this question , but the problem is to do with how the JSON Serializer deals with circular references.我不确定您是否已经看到了这个问题的公认答案,但问题在于 JSON Serializer 如何处理循环引用。 Full details and links to more references can be found at the above link, and I'd suggest digging into those, but in short, adding the following to startup.cs will configure the serializer to ignore circular references:可以在上面的链接中找到更多参考的完整详细信息和链接,我建议深入研究这些,但简而言之,将以下内容添加到startup.cs将配置序列化程序以忽略循环引用:

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

Lazy loading is not yet possible with EF Core. EF Core 尚无法进行延迟加载。 Refer here . 请参阅此处

Alternatively you can use eager loading.或者,您可以使用预先加载。

Read this article阅读这篇文章

Below is the extension method i have created to achieve the eager loading.下面是我为实现急切加载而创建的扩展方法。

Extension Method:扩展方法:

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();
}

Repository Call:存储库调用:

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

Usage:用法:

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

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

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

Dependencies:依赖项:

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;
    }
}

Will be glad if it helps.如果有帮助会很高兴。 Please note this is not a production ready code.请注意,这不是生产就绪代码。

I test your code, this problem exist in my test.我测试你的代码,这个问题存在于我的测试中。 in this post LINK Proposed that use data projection.在这篇文章中LINK建议使用数据投影。 for your problem Something like the following, is work.对于您的问题 类似以下的内容是有效的。

[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;
}

I know this is an old issue, but its the top result in google, so im putting my solution i I found here.我知道这是一个老问题,但它是谷歌的最高结果,所以我把我的解决方案放在了这里。 For a Core 3.1 web project there is a quick fix.对于 Core 3.1 web 项目,有一个快速修复。 Add nuget package Microsoft.EntityFrameworkCore.Proxies .添加 nuget 包Microsoft.EntityFrameworkCore.Proxies Then you simply just need to specify in your options builder when configuring your services.然后,您只需要在配置服务时在选项构建器中指定即可。 Docs: https://docs.microsoft.com/en-us/ef/core/querying/related-data文档: 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"));
    });
}

Now your lazy loading should work as it did in previous EF versions.现在您的延迟加载应该像在以前的 EF 版本中一样工作。 If you not using it for a web project, you can do it right inside of the OnConfiguringMethod inside of your DbContext itself.如果您不将它用于 Web 项目,则可以在 DbContext 本身的 OnConfiguringMethod 内部进行。

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

My EF stuff is kept in a separate class library so i can re-use it through multiple company applications.我的 EF 内容保存在一个单独的类库中,因此我可以通过多个公司应用程序重新使用它。 So having the ability to not lazy load when not needed for a particular application is useful.因此,能够在特定应用程序不需要时不延迟加载是很有用的。 So i prefer passing in the build options, for reuse-ability purposes.所以我更喜欢传递构建选项,用于重用能力的目的。 But both will work.但两者都会起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM