[英]Create Direct Navigation Property in EF Core Many to Many Relationship
I've been working through the following tutorial on how to create a many-to-many relationship using Entity Framework Core: https://docs.microsoft.com/en-us/ef/core/modeling/relationships . 我一直在研究以下有关如何使用Entity Framework Core创建多对多关系的教程: https : //docs.microsoft.com/zh-cn/ef/core/modeling/relationships 。
I'm working on a group management feature and my models are the following: 我正在使用组管理功能,我的模型如下:
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>();
}
And in my dbContext I have defined my join table for mapping two separate one-to-many relationships: 在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);
}
What I need is to create a navigation property to access a group's members directly rather than having to use a .Include() to include the GroupMembers join table, followed by a second .Include() to include the User objects. 我需要创建一个导航属性来直接访问组的成员,而不必使用.Include()来包含GroupMembers连接表,然后再使用第二个.Include()来包括User对象。
The reason for this is that a) the client side is expecting a group object with a property for an array of user objects at the first level and b) I am unable to serialize the returned object in json because it is resulting in an in self referencing loops for the group property of the GroupMember table. 这样做的原因是:a)客户端期望在第一级具有用户对象数组属性的组对象,并且b)我无法在json中序列化返回的对象,因为它导致in self GroupMember表的group属性的引用循环。
In EF6 you would just omit the Linking Entity and EF will create the linking table behind the scenes. 在EF6中,您只需省略链接实体,EF就会在后台创建链接表。
eg 例如
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>();
}
But, that doesn't work on EF Core. 但是,这在EF Core上不起作用。 So let's go around to the workshop and build a workaround. 因此,让我们转到工作坊并建立解决方法。
One idea is to put a NotMapped property on the entities that gives us the skip-level navigation property, and then ignore the real Navigation Properties in JSON serialization. 一种想法是将NotMapped属性放在为我们提供跳过级导航属性的实体上,然后忽略JSON序列化中的实际导航属性。 Also to break the cycles there's a ContractResolver that will skip serialization of the "navigation property" to eliminate cycles in the object graph. 为了打破循环,还有一个ContractResolver,它将跳过“导航属性”的序列化以消除对象图中的循环。
Like this: 像这样:
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();
}
}
}
outputs 输出
{
"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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.