在查询中包含父级和子级?

时间:2016-11-28 22:24:03

标签: c# asp.net-core entity-framework-core

我有两个具有一对多关系的实体,如下所示:

  

客户1 ...... *预订

 public class Customer
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Booking> Bookings { get; set; }
    }


  public class Booking
    {
        [Key]
        public int Id { get; set; }
        public string Title{ get; set; }
        public DateTime BookingDate { get; set; }
        public int? CustomerId { get; set; }
        public Customer Customer { get; set; };

    }

如果_context是DbContext,则后面的两个查询最终会出现循环依赖。

_context.Customers.Include(x=> x.Bookings).ToList();

OR

_context.Bookings.Include(x=> x.Customer).ToList();

问题是如果我们必须在B的查询中包含实体A,我们如何查询这些相互依赖的关系,反之亦然?

2 个答案:

答案 0 :(得分:0)

您可以使用System.ComponentModel.DataAnnotations.Schema命名空间中定义的ForeignKey属性。

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace ParentChild
{
    public class Customer
    {
        [Key]
        public int? Id { get; set; }
        public string Name { get; set; }
        public virtual List<Booking> Bookings { get; set; }
    }

    public class Booking
    {
        [Key]
        public int? Id { get; set; }
        public string Title { get; set; }
        public DateTime? BookingDate { get; set; }
        public int? CustomerId { get; set; }
        [ForeignKey("CustomerId")]
        public virtual Customer Customer { get; set; }
    }

    public class ParentChildDbContext : Microsoft.EntityFrameworkCore.DbContext
    {
        public ParentChildDbContext(String connectionString)
        {
            ConnectionString = connectionString;
        }

        public String ConnectionString { get; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(ConnectionString);

            base.OnConfiguring(optionsBuilder);
        }

        public DbSet<Customer> Customer { get; set; }

        public DbSet<Booking> Booking { get; set; }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            var dbContext = new ParentChildDbContext("server=(local);database=ParentChild;integrated security=yes;");

            var data = dbContext.Customer.Include(p => p.Bookings).ToList();

            Console.WriteLine("Customer: {0}, Bookings: {1}", data.First().Name, data.First().Bookings.Count);

            Console.ReadKey();
        }
    }
}

另外,我为此测试创建了一个数据库:

create database ParentChild
go

use ParentChild
go

create table Customer
(
    Id int not null identity(1, 1),
    Name varchar(50) not null
)

create table Booking
(
    Id int not null identity(1, 1),
    Title varchar(50) not null,
    BookingDate datetime not null,
    CustomerId int not null
)
go

insert into Customer values ('Parent')
go

insert into Booking values ('First booking', getdate(), 1)
go

输出结果:

客户:家长,预订:1

答案 1 :(得分:0)

在我使用Asp.Net Core的情况下,上述问题的一个可能解决方案是告诉MVC选项如下处理引用循环:

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

我已经与EF核心团队一起提出issue来回答是否应该在消费者端处理参考循环,或者应该由EF Core处理。一旦得到答复,将更新答案。