In the previous post we looked at how many-to-many relationships can be mapped using a join entity. In this post we’ll make the navigation properties to the join entity private so that they don’t appear in the public surface of our entity types. We’ll then add public IEnumerable properties that expose the relationship for reading without reference to the join entity.

Updating the model

In the first post our entity types that look like this:

public class Post
{
public int PostId { get; set; }
public string Title { get; set; } public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
} public class Tag
{
public int TagId { get; set; }
public string Text { get; set; } public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
}

But really we want our entity types to look more like this:

public class Post
{
public int PostId { get; set; }
public string Title { get; set; } public ICollection<Tag> Tags { get; } = new List<Tag>();
} public class Tag
{
public int TagId { get; set; }
public string Text { get; set; } public ICollection<Post> Posts { get; } = new List<Post>();
}

One way to do this is to make the PostTags navigation properties private and add public IEnumerable projections for their contents. For example:

public class Post
{
public int PostId { get; set; }
public string Title { get; set; } private ICollection<PostTag> PostTags { get; } = new List<PostTag>(); [NotMapped]
public IEnumerable<Tag> Tags => PostTags.Select(e => e.Tag);
} public class Tag
{
public int TagId { get; set; }
public string Text { get; set; } private ICollection<PostTag> PostTags { get; } = new List<PostTag>(); [NotMapped]
public IEnumerable<Post> Posts => PostTags.Select(e => e.Post);
}

Configuring the relationship

Making the navigation properties private presents a few problems. First, EF Core doesn’t pick up private navigations by convention, so they need to be explicitly configured:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId }); modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany("PostTags"); modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany("PostTags");
}

Using Include

Next, the Include call can no longer easily access to the private properties using an expression, so we use the string-based API instead:

var posts = context.Posts
.Include("PostTags.Tag")
.ToList();

Notice here that we can’t just Include tags like this:

var posts = context.Posts
.Include(e => e.Tags) // Won't work
.ToList();

This is because EF has no knowledge of “Tags”–it is not mapped. EF only knows about the private PostTags navigation property. This is one of the limitations I called out in Part 1. It would currently require messing with EF internals to be able to use Tags directly in queries.

Using the projected navigation properties

Reading the many-to-many relationship can now use the public properties directly. For example:

foreach (var tag in post.Tags)
{
Console.WriteLine($"Tag {tag.Text}");
}

But if we want to add and remove Tags we still need to do it using the PostTag join entity. We will address this in Part 3, but for now we can add a simple helper that gets PostTags by Reflection. Updating our test application to use this we get:

public class Program
{
public static void Main()
{
using (var context = new MyContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated(); var tags = new[]
{
new Tag { Text = "Golden" },
new Tag { Text = "Pineapple" },
new Tag { Text = "Girlscout" },
new Tag { Text = "Cookies" }
}; var posts = new[]
{
new Post { Title = "Best Boutiques on the Eastside" },
new Post { Title = "Avoiding over-priced Hipster joints" },
new Post { Title = "Where to buy Mars Bars" }
}; context.AddRange(
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] }); context.SaveChanges();
} using (var context = new MyContext())
{
var posts = LoadAndDisplayPosts(context, "as added"); posts.Add(context.Add(new Post { Title = "Going to Red Robin" }).Entity); var newTag1 = new Tag { Text = "Sweet" };
var newTag2 = new Tag { Text = "Buzz" }; foreach (var post in posts)
{
var oldPostTag = GetPostTags(post).FirstOrDefault(e => e.Tag.Text == "Pineapple");
if (oldPostTag != null)
{
GetPostTags(post).Remove(oldPostTag);
GetPostTags(post).Add(new PostTag { Post = post, Tag = newTag1 });
}
GetPostTags(post).Add(new PostTag { Post = post, Tag = newTag2 });
} context.SaveChanges();
} using (var context = new MyContext())
{
LoadAndDisplayPosts(context, "after manipulation");
}
} private static List<Post> LoadAndDisplayPosts(MyContext context, string message)
{
Console.WriteLine($"Dumping posts {message}:"); var posts = context.Posts
.Include("PostTags.Tag")
.ToList(); foreach (var post in posts)
{
Console.WriteLine($" Post {post.Title}");
foreach (var tag in post.Tags)
{
Console.WriteLine($" Tag {tag.Text}");
}
} Console.WriteLine(); return posts;
} private static ICollection<PostTag> GetPostTags(object entity)
=> (ICollection<PostTag>)entity
.GetType()
.GetRuntimeProperties()
.Single(e => e.Name == "PostTags")
.GetValue(entity);
}

This test code will be simplified significantly in the next post where we show how to make the projected navigations updatable.

原文链接

Many-to-many relationships in EF Core 2.0 – Part 2: Hiding as IEnumerable的更多相关文章

  1. Many-to-many relationships in EF Core 2.0 – Part 3: Hiding as ICollection

    In the previous post we ended up with entities that hide the join entity from the public surface. Ho ...

  2. Many-to-many relationships in EF Core 2.0 – Part 1: The basics

    转载这个系列的文章,主要是因为EF Core 2.0在映射数据库的多对多关系时,并不像老的EntityFramework那样有原生的方法进行支持,希望微软在以后EF Core的版本中加入原生支持多对多 ...

  3. Many-to-many relationships in EF Core 2.0 – Part 4: A more general abstraction

    In the last few posts we saw how to hide use of the join entity from two entities with a many-to-man ...

  4. EF Core 1.0 和 SQLServer 2008 分页的问题

    EF Core 1.0 在sqlserver2008分页的时候需要指定用数字分页. EF Core1.0 生成的分页语句中使用了 Featch Next.这个语句只有在SqlServer2012的时候 ...

  5. ASP.NET Core 开发-Entity Framework (EF) Core 1.0 Database First

    ASP.NET Core 开发-Entity Framework Core 1.0 Database First,ASP.NET Core 1.0 EF Core操作数据库. Entity Frame ...

  6. EF Core 1.0中使用Include的小技巧

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:由于EF Core暂时不支持Lazy Loading,所以利用Include来加载额外 ...

  7. .NET Core 1.0、ASP.NET Core 1.0和EF Core 1.0简介

    .NET Core 1.0.ASP.NET Core 1.0和EF Core 1.0简介 英文原文:Reintroducing .NET Core 1.0, ASP.NET Core 1.0, and ...

  8. EF Core 2.0 新特性

    前言 目前 EF Core 的最新版本为 2.0.0-priview1-final,所以本篇文章主要是针对此版本的一些说明. 注意:如果你要在Visual Studio 中使用 .NET Core 2 ...

  9. EF Core 2.0使用MsSql/Mysql实现DB First和Code First

    参考地址 EF官网 ASP.NET Core MVC 和 EF Core - 教程系列 环境 Visual Studio 2017 最新版本的.NET Core 2.0 SDK 最新版本的 Windo ...

随机推荐

  1. 不同浏览器下word-wrap,word-break,white-space强制换行和不换行总结

    强制换行与强制不换行用到的属性 我们一般控制换行所用到的CSS属性一共有三个:word-wrap; word-break; white-space.这三个属性可以说是专为了文字断行而创造出来的.首先我 ...

  2. Found 1 slaves: Use of uninitialized value in printf at /usr/local/percona-toolkit/bin/pt-online-schema-change line 8489

    1. problem description: as the title show, i miss the first problem using pt-online-schema-change to ...

  3. Python基础-模块与包

    一.如何使用模块 上篇文章已经简单介绍了模块及模块的优点,这里着重整理一下模块的使用细节. 1. import 示例文件:spam.py,文件名spam.py,模块名spam #spam.py pri ...

  4. 基础架构之日志管理平台搭建及java&net使用

    在现代化的软件开发流程中,日志显得非常的重要,不可能再零散的游离在各个项目中,等查看日志的时候再登录服务器去到特定的目录去查看,这显然很繁琐且效率低下,所有整合一套日志管理平台,也显得非常重要,这篇文 ...

  5. C# Winform窗体和控件自适应大小

    1.在项目中创建类AutoSizeForm AutoSizeForm.cs文件代码: using System; using System.Collections.Generic; using Sys ...

  6. 批量删除微博的js代码

    清空微博,网上找了一段js代码,试了下,还行. var fileref=document.createElement('script') fileref.setAttribute("type ...

  7. 【Python】安装配置Anaconda

    优点:解决Python 库依赖问题 清华安装镜像 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/

  8. 【JAVA语法】04Java-多态性

    多态性 instanceof 关键字 接口的应用 一.多态性 1.多态性的体现: 方法的重载和重写 对象的多态性 2.对象的多态性: 向上转型: 程序会自动完成 父类 父类对象 = 子类实例 向下转型 ...

  9. 天诛进阶之D算法 #3700

    http://mp.weixin.qq.com/s/ngn98BxAOLxXPlLU8sWH_g 天诛进阶之D算法 #3700 2015-11-24 yevon_ou 水库论坛 天诛进阶之D算法 #3 ...

  10. TaskScheduler内幕天机解密:Spark shell案例运行日志详解、TaskScheduler和SchedulerBackend、FIFO与FAIR、Task运行时本地性算法详解等

    本课主题 通过 Spark-shell 窥探程序运行时的状况 TaskScheduler 与 SchedulerBackend 之间的关系 FIFO 与 FAIR 两种调度模式彻底解密 Task 数据 ...