C# 在EF核心多对多关系中创建直接导航属性

C# 在EF核心多对多关系中创建直接导航属性,c#,sql-server,entity-framework,asp.net-core,entity-framework-core,C#,Sql Server,Entity Framework,Asp.net Core,Entity Framework Core,我已经完成了以下教程,介绍了如何使用实体框架核心创建多对多关系: 我正在开发组管理功能,我的模型如下: public class Group { public int GroupId { get; set; } public string GroupName { get; set;} public virtual List<GroupMember> GroupMembers { get; set; } = new List<Gro

我已经完成了以下教程,介绍了如何使用实体框架核心创建多对多关系:

我正在开发组管理功能,我的模型如下:

public class Group
{
    public int GroupId { get; set; }
    public string GroupName { get; set;}            
    public virtual List<GroupMember> GroupMembers { get; set; } = new List<GroupMember>();
}

public class GroupMember
{
    public int GroupId { get; set; }
    public Group Group { get; set; }

    public int UserId { get; set; }
    public User User{ get; set; } 
}

public class User
{
    public int UserId { get; set; }
    public string Email { get; set; }
    public List<GroupMember> MemberOf {get; set;} = new List<GroupMember>();
}
公共类组
{
public int GroupId{get;set;}
公共字符串组名{get;set;}
公共虚拟列表组成员{get;set;}=new List();
}
公共类组成员
{
public int GroupId{get;set;}
公共组组{get;set;}
public int UserId{get;set;}
公共用户{get;set;}
}
公共类用户
{
public int UserId{get;set;}
公共字符串电子邮件{get;set;}
{get;set;}=new List()的公共列表成员;
}
在dbContext中,我定义了连接表,用于映射两个独立的一对多关系:

public DbSet<Group> Groups { get; set; }   
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{ 
                modelBuilder.Entity<GroupMember>()
                    .HasKey(t => new { t.UserId, t.GroupId });

                modelBuilder.Entity<GroupMember>()
                    .HasOne(pt => pt.User)
                    .WithMany(p => p.MemberOf)
                    .HasForeignKey(pt => pt.UserId);

                modelBuilder.Entity<GroupMember>()
                    .HasOne(pt => pt.Group)
                    .WithMany(t => t.GroupMembers)
                    .HasForeignKey(pt => pt.GroupId);
}
公共数据库集组{get;set;}
公共数据库集用户{get;set;}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{ 
modelBuilder.Entity()
.HasKey(t=>new{t.UserId,t.GroupId});
modelBuilder.Entity()
.HasOne(pt=>pt.User)
.WithMany(p=>p.MemberOf)
.HasForeignKey(pt=>pt.UserId);
modelBuilder.Entity()
.HasOne(pt=>pt.Group)
.WithMany(t=>t.GroupMembers)
.HasForeignKey(pt=>pt.GroupId);
}
我需要创建一个导航属性来直接访问组的成员,而不必使用.Include()来包含GroupMembers联接表,然后使用第二个.Include()来包含用户对象


这是因为a)客户端需要一个组对象,该组对象在第一级具有用户对象数组的属性;b)我无法用json序列化返回的对象,因为这会导致GroupMember表的group属性出现自引用循环。

在EF6中,您只需省略链接实体和EF将在幕后创建链接表

乙二醇


在EF6中,您只需省略链接实体,EF将在幕后创建链接表

乙二醇


问题中包含的文档链接清楚地表明,EF Core还不支持您提出的问题。是的,我发现相关的github问题仍在实施中。然而,我的问题是,如果有人知道一个“黑客”来创建一个导航属性/getter方法,直接在我的模型中检索用户列表,而不必重构我的客户机代码所期望的对象,请参见下面关于黑客的更新答案:)不需要黑客。将存储模型(EF实体模型)与序列化、表示、域/业务等模型混为一谈通常是错误的。创建特殊对象,从实体模型中投影/映射所需数据。问题中包含的文档链接清楚地表明,EF Core还不支持您提出的问题。是的,我发现相关的github问题仍在实施中。然而,我的问题是,如果有人知道一个“黑客”来创建一个导航属性/getter方法,直接在我的模型中检索用户列表,而不必重构我的客户机代码所期望的对象,请参见下面关于黑客的更新答案:)不需要黑客。将存储模型(EF实体模型)与序列化、表示、域/业务等模型混为一谈通常是错误的。创建特殊对象,从实体模型中投影/映射所需的数据,然后就可以了!谢谢!现在,使用新的nav属性在客户机上获得我需要的东西!实际上,我可以在配置中添加以下行:AddJsonOptions(options=>options.SerializerSettings.ReferenceLoopHandling=Newtonsoft.Json.ReferenceLoopHandling.Ignore);这解决了我的递归json对象问题,是的!谢谢!现在,使用新的nav属性在客户机上获得我需要的东西!实际上,我可以在配置中添加以下行:AddJsonOptions(options=>options.SerializerSettings.ReferenceLoopHandling=Newtonsoft.Json.ReferenceLoopHandling.Ignore);这解决了我的递归json对象问题
public class Group
{
    public int GroupId { get; set; }
    public string GroupName { get; set;}            
    public virtual List<User> GroupMembers { get; set; } = new List<User>();
}
public class User
{
    public int UserId { get; set; }
    public string Email { get; set; }
    public virtual List<Group> MemberOf {get; set;} = new List<Group>();
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json.Serialization;
using System.Reflection;

namespace EFCore2Test
{
    public class Group
    {
        public int GroupId { get; set; }
        public string GroupName { get; set; }
        [JsonIgnore]
        public virtual ICollection<GroupMember> GroupMembers { get; } = new HashSet<GroupMember>();

        [NotMapped]
        public IList<User> Users => GroupMembers.Select(m => m.User).ToList();
    }

    public class GroupMember
    {
        public int GroupId { get; set; }
        public Group Group { get; set; }
        public int UserId { get; set; }
        public User User { get; set; }
    }

    public class User
    {

        public int UserId { get; set; }
        public string Email { get; set; }

        [JsonIgnore]
        public virtual ICollection<GroupMember> MemberOf { get; } = new HashSet<GroupMember>();

        [NotMapped]
        public IList<Group> Groups => MemberOf.Select(m => m.Group).ToList();
    }

    public class Db : DbContext
    {


        public DbSet<User> Users { get; set; }
        public DbSet<Group> Groups { get; set; }

        public DbSet<GroupMember> GroupMembers { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<GroupMember>()
                .HasKey(t => new { t.UserId, t.GroupId });

            modelBuilder.Entity<GroupMember>()
                .HasOne(pt => pt.User)
                .WithMany(p => p.MemberOf)
                .HasForeignKey(pt => pt.UserId);

            modelBuilder.Entity<GroupMember>()
                .HasOne(pt => pt.Group)
                .WithMany(t => t.GroupMembers)
                .HasForeignKey(pt => pt.GroupId);
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Server=(local);Database=Test;Trusted_Connection=True;MultipleActiveResultSets=true");
            base.OnConfiguring(optionsBuilder);
        }
    }


    class Program
    {

        public class DontSerialze<T> : DefaultContractResolver
        {


            protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
            {

                JsonProperty property = base.CreateProperty(member, memberSerialization);

                if (property.PropertyType == typeof(T))
                {
                    property.ShouldSerialize = o => false;
                }

                return property;
            }
        }
        static void Main(string[] args)
        {

            using (var db = new Db())
            {
                db.Database.EnsureDeleted();
                db.Database.EnsureCreated();

                var users = Enumerable.Range(1, 20).Select(i => new User() { Email = $"user{i}@wherever" }).ToList();

                var groups = Enumerable.Range(1, 5).Select(i => new Group() { GroupName = $"group{i}" }).ToList();

                var userGroups = (from u in users from g in groups select new GroupMember() { User = u, Group = g })
                                 .OrderBy(gm => (gm.Group.GroupName + gm.User.Email).GetHashCode())
                                 .Take(100)
                                 .ToList();

                db.Users.AddRange(users);
                db.Groups.AddRange(groups);
                db.GroupMembers.AddRange(userGroups);

                db.SaveChanges();

                var ser = new JsonSerializer();
                ser.Formatting = Formatting.Indented;
                ser.ContractResolver = new DontSerialze<IList<User>>();

                foreach (var u in users.Take(2))
                {
                    ser.Serialize(Console.Out, u);
                    Console.WriteLine();
                }

            }
            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}
{
  "UserId": 20,
  "Email": "user1@wherever",
  "Groups": [
    {
      "GroupId": 4,
      "GroupName": "group1"
    },
    {
      "GroupId": 2,
      "GroupName": "group3"
    },
    {
      "GroupId": 5,
      "GroupName": "group4"
    },
    {
      "GroupId": 1,
      "GroupName": "group5"
    },
    {
      "GroupId": 3,
      "GroupName": "group2"
    }
  ]
}
{
  "UserId": 18,
  "Email": "user2@wherever",
  "Groups": [
    {
      "GroupId": 2,
      "GroupName": "group3"
    },
    {
      "GroupId": 1,
      "GroupName": "group5"
    },
    {
      "GroupId": 5,
      "GroupName": "group4"
    },
    {
      "GroupId": 3,
      "GroupName": "group2"
    },
    {
      "GroupId": 4,
      "GroupName": "group1"
    }
  ]
}
Hit any key to exit