首页 > 解决方案 > EF 只包含一次嵌套实体

问题描述

实体框架不会多次包含嵌套实体及其数据。

以下是邮递员的回复:

[{
    "ProjectID": 29,
    "ProjectName": "Angular",
    "ProjectDescription": "This is angular project for developing webpages.",
    "ProjectURL": "angular.com",
    "ApplicationEntity": [],
    "ApplicationID": 21
},
{
    "ProjectID": 30,
    "ProjectName": "Dot Net 6",
    "ProjectSlug": "MAUI",
    "ProjectDescription": "This is project for .net framwork MAUI.",
    "ProjectURL": "maui.com",
    "ApplicationEntity": [
        {
            "ApplicationID": 21,
            "ApplicationName": "Custom Application",
            "ApplicationVersion": "1.0.0.0",
            "ApplicationDescription": "This is for a custom implementation."
        }
    ],
    "ApplicationID": 21
}]

如您所见"ApplicationID" : 21,两个条目中的内容相同。但是ApplicationEntity不包括它的值"ProjectID":29

创建项目.cs

public ProjectEntity CreateProject(int id, ProjectEntity projectEntity)
    {

        projectEntity.ApplicationID = id;

        CustomerSupportDBContext dBContext = new CustomerSupportDBContext();
        ApplicationEntity applicationEntity = dBContext.ApplicationEntities.SingleOrDefault(e => e.ApplicationID == id);

        using (dBContext)
        {
            projectEntity.ApplicationEntity.Add(applicationEntity);
            dBContext.ProjectEntities.Add(projectEntity);
            dBContext.SaveChanges();
        }
        return projectEntity;
    }

编辑: ProjectController.cs

[Route("getAllProjects")]
    [HttpGet]
    public HttpResponseMessage GetAllProjects()
    {
        try
        {
            ProjectService projectService = new ProjectService();
            IQueryable<ProjectEntity> response = projectService.GetAllProjects();
            return Request.CreateResponse(HttpStatusCode.OK, response);
        }
        catch (Exception ex)
        {
            return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message + " " + ex.StackTrace);
        }
    }

项目服务.cs

public IQueryable<ProjectEntity> GetAllProjects()
    {
        CustomerSupportDBContext dBContext = new CustomerSupportDBContext();
        var projectEntities = dBContext.ProjectEntities.Include("ApplicationEntity");
        return projectEntities;
    }

项目实体.cs

[Table("ProjectEntity")]
public class ProjectEntity
{
    [Key]
    public int ProjectID { get; set; }
    public string ProjectName { get; set; }
    public string ProjectDescription { get; set; }        
    public string ProjectURL { get; set; }
    public List<ApplicationEntity> ApplicationEntity { get; set; }
    public int ApplicationID { get; set; }        

    public ProjectEntity()
    {
        ApplicationEntity = new List<ApplicationEntity>();
        IssueEntity = new List<IssueEntity>();
    }
}

应用实体.cs

[Table("ApplicationEntity")]
public class ApplicationEntity
{
    [Key]
    public int ApplicationID { get; set; }
    public string ApplicationName { get; set; }
    public string ApplicationVersion { get; set; }
    public string ApplicationDescription { get; set; }
}

数据库上下文.cs

public class CustomerSupportDBContext : DbContext
{
    public DbSet<ProjectEntity> ProjectEntities { get; set; }
    public DbSet<ApplicationEntity> ApplicationEntities { get; set; }

    public CustomerSupportDBContext()
    {
        Configuration.LazyLoadingEnabled = false;
    }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

只有最后一个实体ApplicationEntity拥有它的详细信息。

标签: c#.netentity-frameworkasp.net-web-api

解决方案


您的实现中有很多错误,我怀疑您的映射正在做一些非常奇怪的事情,以使事情看起来像是工作。

明显的问题是您在 Project 和 Application 之间定义一对多关系,其中 ProjectEntity 包含 ApplicationEntity 的集合,但是您在实体中的 FK 关系是向后的。ApplicationEntity 应该有一个 ProjectId 而不是 ProjectEntity 有一个 ApplicationId。我将首先仔细查看您生成的架构,以确定这些数据行使用什么以及如何相互链接。

如果 Project 有 0-1 Application,则 ProjectEntity 将有一个可为空的 ApplicationId 列,但将包含一个 ApplicationEntity 引用,而不是一个List<ApplicationEntity>. 如果一个项目可以有 0 个应用程序,那么 ProjectEntity 将有List<ApplicationEntity>但没有 ApplicationId,相反,ApplicationEntity 将有一个 ProjectId 作为 FK 映射回项目。

其他问题:

不要这样做:

CustomerSupportDBContext dBContext = new CustomerSupportDBContext();
var projectEntities = dBContext.ProjectEntities.Include("ApplicationEntity");
return projectEntities;

做这个:

using (CustomerSupportDBContext dBContext = new CustomerSupportDBContext())
{
    var projectEntities = dBContext.ProjectEntities.Include("ApplicationEntity");
    return projectEntities;
}

同样,不要这样做:

CustomerSupportDBContext dBContext = new CustomerSupportDBContext();
ApplicationEntity applicationEntity = dBContext.ApplicationEntities.SingleOrDefault(e => e.ApplicationID == id);

using (dBContext)
{
   // ...

利用:

using (CustomerSupportDBContext dBContext = new CustomerSupportDBContext())
{
    ApplicationEntity applicationEntity = dBContext.ApplicationEntities.SingleOrDefault(e => e.ApplicationID == id);

    // ....
}

如果项目不使用依赖注入来管理其生命周期范围,请始终确保释放 DbContext。这将破坏您的IQueryable实现,因为 DbContext 将在方法返回之前被释放。但是,像这样打开 DbContext 只是在寻找内存/数据库连接句柄泄漏的麻烦。

IQueryable<TEntity>对于存储库来说是一种非常灵活和强大的模式,但它只能在 DbContext 生命周期范围超出返回IQueryable. 利用依赖注入使调用者和存储库可以共享相同的 DbContext 实例,并且DbContext将保证在请求结束时被释放,或者使用存储库/服务可以的 DbContext 的工作单元包装器用于解析 DbContext 的实例。

具有工作范围单元的示例更像是:

using (var unitOfWork = ContextScopeFactory.Create())
{
    var project = ProjectService.GetProjectById(projectId)
        .Select(p => new ProjectViewModel 
        {
            // populate details about project and application(s) that View needs
        }).Single();
   return project;
}

ProjectService 有一个 DbContext 解析器,用于从包装上下文范围获取 DbContext 引用。这是一个更高级的示例,它可以让调用者利用IQueryable. 在您的实现中,您不能依赖做任何事情,IQueryable因为 DbContext 引用被挂起,并且当有人去修复内存泄漏或从未处理的 DbContext 打开 DB 连接泄漏时,该方法将失败。(也可以返回IEnumerable<ProjectEntity>


推荐阅读