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