Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework core .Include() issue

Been having a play about with ef core and been having an issue with the include statement. For this code I get 2 companies which is what i expected.

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. 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; }
}
like image 777
John Morrison Avatar asked Jul 24 '16 09:07

John Morrison


People also ask

What's new in Entity Framework Core?

EF Core has improved many issues found in previous versions of Entity Framework. While still being an abstraction for database engines, the interface additions to EF Core seem to embrace the inevitability of dealing with the database. Enhancements include combining, splitting, and executing SQL in previously tricky ways.

What is include and then include in Entity Framework?

Entity Framework Core Tutorial Include & ThenInclude. Include. The Include method specifies the related objects to include in the query results. It can be used to retrieve some information from the database and also want to include related entities.

How to include multiple levels of related data in EF Core?

For example, Customer contains a list of invoices and each invoice then contains a list of items. EF Core has a new extension method ThenInclude(). You can drill down thru relationships to include multiple levels of related data using the ThenInclude method.

What are the problems with EF Core and ASP NET Core?

There is an assortment of powerful technologies in the .NET space, but developers can run into problems when using them together. In ASP.NET Core and EF Core implementations, developers might accidentally take the “easy” path and return the EF objects directly from a LINQ Query.


4 Answers

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. 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:

services.AddMvc()
    .AddJsonOptions(options => {
        options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    });
like image 120
Ashley Bye Avatar answered Sep 24 '22 07:09

Ashley Bye


Lazy loading is not yet possible with 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.

like image 38
Venkatesh Avatar answered Sep 24 '22 07:09

Venkatesh


I test your code, this problem exist in my test. in this post LINK Proposed that use data projection. 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;
}
like image 38
Mohammad Akbari Avatar answered Sep 20 '22 07:09

Mohammad Akbari


Make sure you are using Include from "Microsoft.EntityFrameworkCore" And Not from "System.Data.Entity"

like image 36
2 revs, 2 users 67% Avatar answered Sep 22 '22 07:09

2 revs, 2 users 67%