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

时间:2016-07-24 09:59:28

标签: c# entity-framework-core

一直在玩ef核心,并且在使用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
    }
]

正如你所看到的,有两家公司,所有相关的属性都是null,因为我曾经使用过任何包含,这正是我的预期。现在,当我将方法更新为:

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

4 个答案:

答案 0 :(得分:23)

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

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

答案 1 :(得分:1)

我测试你的代码,我的测试中存在这个问题。在这篇文章中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;
}

答案 2 :(得分:1)

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 :(得分:0)

我知道这是一个老问题,但是它在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资料保存在一个单独的类库中,因此我可以通过多个公司应用程序重新使用它。因此,具有在特定应用程序不需要时不延迟加载的功能非常有用。因此,出于重用能力的目的,我更喜欢传递构建选项。但是两者都可以。