In the last few posts we saw how to hide use of the join entity from two entities with a many-to-many relationship. This post doesn’t add any additional functionality, it just abstracts some of what we saw so it can be re-used more easily.

To start with we define an interface for join entities:

public interface IJoinEntity<TEntity>
{
TEntity Navigation { get; set; }
}

Any join entity will implement this interface twice; once for each side:

public class PostTag : IJoinEntity<Post>, IJoinEntity<Tag>
{
public int PostId { get; set; }
public Post Post { get; set; }
Post IJoinEntity<Post>.Navigation
{
get => Post;
set => Post = value;
} public int TagId { get; set; }
public Tag Tag { get; set; }
Tag IJoinEntity<Tag>.Navigation
{
get => Tag;
set => Tag = value;
}
}

We can now re-write our facade colection to use any types that implement this interface:

public class JoinCollectionFacade<TEntity, TOtherEntity, TJoinEntity>
: ICollection<TEntity>
where TJoinEntity : IJoinEntity<TEntity>, IJoinEntity<TOtherEntity>, new()
{
private readonly TOtherEntity _ownerEntity;
private readonly ICollection<TJoinEntity> _collection; public JoinCollectionFacade(
TOtherEntity ownerEntity,
ICollection<TJoinEntity> collection)
{
_ownerEntity = ownerEntity;
_collection = collection;
} public IEnumerator<TEntity> GetEnumerator()
=> _collection.Select(e => ((IJoinEntity<TEntity>)e).Navigation).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator(); public void Add(TEntity item)
{
var entity = new TJoinEntity();
((IJoinEntity<TEntity>)entity).Navigation = item;
((IJoinEntity<TOtherEntity>)entity).Navigation = _ownerEntity;
_collection.Add(entity);
} public void Clear()
=> _collection.Clear(); public bool Contains(TEntity item)
=> _collection.Any(e => Equals(item, e)); public void CopyTo(TEntity[] array, int arrayIndex)
=> this.ToList().CopyTo(array, arrayIndex); public bool Remove(TEntity item)
=> _collection.Remove(
_collection.FirstOrDefault(e => Equals(item, e))); public int Count
=> _collection.Count; public bool IsReadOnly
=> _collection.IsReadOnly; private static bool Equals(TEntity item, TJoinEntity e)
=> Equals(((IJoinEntity<TEntity>)e).Navigation, item);
}

The main advantage of this new abstraction is that specific delegates to select target entities and create join entities are not needed anymore. So now in our entities we can create collections like so:

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

Everything else, including the little test application, is unchanged from the previous post.

原文链接

Many-to-many relationships in EF Core 2.0 – Part 4: A more general abstraction的更多相关文章

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

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

  2. Many-to-many relationships in EF Core 2.0 – Part 2: Hiding as IEnumerable

    In the previous post we looked at how many-to-many relationships can be mapped using a join entity. ...

  3. 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 ...

  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. C Primer Plus note6

    error: invalid preprocessing directive #difine| 无效的宏定义处理 宏定义define 写成了 difine.

  2. csharp: QR Code Barcode

    /// <summary> /// /// </summary> /// <param name="sender"></param> ...

  3. css-布局的漂浮

    float ** 属性值 left: 文本流向对象的右边 right:文本流向对象的左边 <html> <head> <title>World</title& ...

  4. Java中避免空指针的几个方法

    equals Object类中的equals 方法在非空对象引用上实现相等关系,具有对称性 x.equals(y) 和 y.equals(x) 结果是一样的,但当x == null时会抛出空指针异常 ...

  5. 轻松看懂机器学习十大常用算法 (Machine Learning Top 10 Commonly Used Algorithms)

    原文出处: 不会停的蜗牛    通过本篇文章可以对ML的常用算法有个常识性的认识,没有代码,没有复杂的理论推导,就是图解一下,知道这些算法是什么,它们是怎么应用的,例子主要是分类问题. 每个算法都看了 ...

  6. 01_Jdk自带SPI

    [SPI的设计目标] 面向对象的设计里,模块之间是基于接口编程,模块之间不对实现类进行硬编码. 一旦代码里设计具体的实现类,就违法了可插拔的原则,如果需要替代一种实现,就要修改代码. 为了实现在模块装 ...

  7. 深度优先搜索算法(Depth-First-Search,DFS)

    深度优先搜索算法的概念 与广度优先搜索算法不同,深度优先搜索算法类似与树的先序遍历.这种搜索算法所遵循的搜索策略是尽可能"深"地搜索一个图.它的基本思想如下:首先访问图中某一个起始 ...

  8. SQL Server ->> 与SQL Server服务配置相关的DMV

    1) sys.dm_server_services这个DMV可以告诉我们与当前版本的SQL Server相关的服务的启动状态和最后一次启动的时间,诸如这样的信息. SELECT * FROM sys. ...

  9. Visio2010新建E-R图

    visio2010没有内置E-R图的模板,需要自己配置模具.步骤如下: 1.文件->新建->基本流程图->右键菱形->添加到新模具->命名为E-R图. 2.更多形状-&g ...

  10. lvm 相关

    求教:/home分区和/root分区的关系 lvm扩容试验 [复制链接] lvm快速使用http://imysql.cn/2008_05_05_quick_startup_lvm Linux LVM学 ...