c# - 如何定义类内封装属性的导航?

标签 c# entity-framework-core

我继承了一个共享项目,其中定义了模型。为了更容易的 XML 序列化,它们采用以下形式:

    public class Blog
    {
        public int Id { get; set; }
        public Posts Posts { get; set; }
    }

    public class Posts
    {
        public List<Post> PostsCollection { get; set; }
    }

    public class Post
    {
        public int BlogId { get; set; }
        public int PostId { get; set; }
        public string Content { get; set; }
    }
如何在 OnModelCreating 中指定 EF DbContext使用方法Posts.PostsCollection作为导航属性?让我们假设,我不允许在 Post 和 Blog 类中更改任何内容。我只需要以编程方式指定 EF 的关系。是否可以?我读过 defining relationships on MS site以及有关在本网站和其他各种网站上定义模型的其他主题,但找不到适合我的场景的任何内容。

最佳答案

这是可能的,但中间类必须映射为假实体,作为一对多关系的主体,并依赖于与实际主体的一对一关系。
拥有的实体类型看起来不错,但由于 EF Core 不允许拥有的实体类型作为主体的限制,它必须配置为与“所有者”共享同一张表的常规“实体”(所谓的表拆分) 和影子“PK”/“FK”属性实现所谓的共享主键关联。
由于中间的“实体”和与所有者的“关系”是通过影子属性处理的,因此涉及的模型类都不需要修改。
以下是示例模型的流畅配置

modelBuilder.Entity<Posts>(entity =>
{
    // Table splitting
    entity.ToTable("Blogs");
    // Shadow PK
    entity.Property<int>(nameof(Blog.Id));
    entity.HasKey(nameof(Blog.Id));
    // Ownership
    entity.HasOne<Blog>()
        .WithOne(related => related.Posts)
        .HasForeignKey<Posts>(nameof(Blog.Id));
    // Relationship
    entity
        .HasMany(posts => posts.PostsCollection)
        .WithOne()
        .HasForeignKey(related => related.BlogId);
});
影子 PK/FK 属性的名称可以是任何名称,但您需要知道所有者表名称/模式以及 PK 属性名称和类型。所有这些信息都可以从 EF Core 模型元数据中获得,因此可以将更安全和可重用的配置提取到这样的自定义扩展方法中(EF Core 3.0+,可以针对 2.x 进行调整)
namespace Microsoft.EntityFrameworkCore
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using Metadata.Builders;

    public static class CustomEntityTypeBuilderExtensions
    {
        public static CollectionNavigationBuilder<TContainer, TRelated> HasMany<TEntity, TContainer, TRelated>(
            this EntityTypeBuilder<TEntity> entityTypeBuilder,
            Expression<Func<TEntity, TContainer>> containerProperty,
            Expression<Func<TContainer, IEnumerable<TRelated>>> collectionProperty)
            where TEntity : class where TContainer : class where TRelated : class
        {
            var entityType = entityTypeBuilder.Metadata;
            var containerType = entityType.Model.FindEntityType(typeof(TContainer));
            // Table splitting
            containerType.SetTableName(entityType.GetTableName());
            containerType.SetSchema(entityType.GetSchema());
            // Shadow PK
            var key = containerType.FindPrimaryKey() ?? containerType.SetPrimaryKey(entityType
                .FindPrimaryKey().Properties
                .Select(p => containerType.FindProperty(p.Name) ?? containerType.AddProperty(p.Name, p.ClrType))
                .ToArray());
            // Ownership
            entityTypeBuilder
                .HasOne(containerProperty)
                .WithOne()
                .HasForeignKey<TContainer>(key.Properties.Select(p => p.Name).ToArray());
            // Relationship
            return new ModelBuilder(entityType.Model)
                .Entity<TContainer>()
                .HasMany(collectionProperty);
        }
    }
}
使用上面的自定义方法,示例模型的配置将是
modelBuilder.Entity<Blog>()
    .HasMany(entity => entity.Posts, container => container.PostsCollection)
    .WithOne()
    .HasForeignKey(related => related.BlogId);
如果集合导航属性直接位于 Blog 上,则这与标准配置几乎相同(只有一个额外的 lambda 参数)
modelBuilder.Entity<Blog>()
    .HasMany(entity => entity.PostsCollection)
    .WithOne()
    .HasForeignKey(related => related.BlogId);

关于c# - 如何定义类内封装属性的导航?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65807844/

相关文章:

c# - 尝试使用 <list> 将对象放入数组时无法从 void 转换为模型

c# - LINQ .OrderBy 创建嵌套套管

c# - Entity Framework 编译查询容器?

c# - 基于输入的动态返回类型 - asp.net

C# - 如何防止鼠标滚轮在我的组合框中滚动?

c# - 连接到文件的 Entity Framework 连接字符串

c# - 如何在 GridView 的 CommandArgument 中传递多个参数?

sql-server - SQL Server 安装 INI 文件以允许非管理员用户连接时出现问题?

C# Entity Framework IQueryable 内存泄漏

c# - 如何正确定义 EF Core 创建的这些类之间的关系?