EFCore-11 自引用关系配置

在数据库中表示这样的一个组织关系,可以使用自引用的方式实现,也就是主键和外键在同一张表中。

新建一个控制台应用程序

控制台项目结构:

项目引用的程序集:

Microsoft.EntityFrameworkCore.SqlServer 

Microsoft.EntityFrameworkCore.Tools

1.在实体类中定义关系属性

OrgUnit.cs

using System.Collections.Generic;

namespace 自引用的组织结构树
{
    class OrgUnit
    {
        /// <summary>
        /// 主键
        /// </summary>
        public long Id { get; set; }

        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 父节点
        /// </summary>
        public OrgUnit Parent { get; set; }

        /// <summary>
        /// 子节点
        /// </summary>
        public List<OrgUnit> Children { get; set; } = new List<OrgUnit>();
    }
}


 

2.FluentAPI关系配置

OrgUnitConfig.cs

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace 自引用的组织结构树
{
    class OrgUnitConfig : IEntityTypeConfiguration<OrgUnit>
    {
        public void Configure(EntityTypeBuilder<OrgUnit> builder)
        {
            builder.ToTable("T_OrgUnits");
            builder.Property(o => o.Name).IsUnicode().IsRequired().HasMaxLength(50);
            builder.HasOne<OrgUnit>(o => o.Parent).WithMany(o => o.Children); //因为根节点没有父节点,所以不能定义为IsRequired
        }
    }
}

3.DbContext配置

用于定义数据库表和数据库链接等信息

MyDbContext.cs

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 自引用的组织结构树
{
    class MyDbContext : DbContext
    {

        public DbSet<OrgUnit> orgUnits { get; set; }


        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);

            string connStr = "Server=.;Database=EFCoreDemo2;Trusted_Connection=True;MultipleActiveResultSets=true";
            optionsBuilder.UseSqlServer(connStr);


            //打印log日志
            //optionsBuilder.LogTo(msg =>
            //{
            //    if (msg.Contains("CommandExecuting"))
            //    {
            //        Console.WriteLine(msg);
            //    }
            //    else
            //    {
            //        return;
            //    }
            //});
        }



        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            //从当前程序集加载所有IEntityTypeConfiguration
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }
}

4.生成并执行数据库迁移脚本

使用命令生成数据库表创建脚本,并执行相关脚本。具体操作参考文章

EFCore-5 Migration操作

操作完成后,数据库会新建一张表。

5.往数据库表插入记录

using System;
using System.Linq;
using System.Threading.Tasks;

namespace 自引用的组织结构树
{
    class Program
    {
        static async Task Main(string[] args)
        {
            OrgUnit orgRoot = new OrgUnit { Name = "娃哈哈集团全球总部" };

            OrgUnit orgAsia = new OrgUnit { Name = "娃哈哈集团亚洲总部" };
            OrgUnit orgChina = new OrgUnit { Name = "娃哈哈集团中国总部" };

       
            //指定对象的父节点
            orgAsia.Parent = orgRoot;
            orgChina.Parent = orgAsia;

            OrgUnit orgEurope = new OrgUnit { Name = "娃哈哈集团欧洲总部" };
            OrgUnit orgEngland = new OrgUnit { Name = "娃哈哈集团英国总部" };

            orgEurope.Parent = orgRoot;
            orgEngland.Parent = orgEurope;


            using (MyDbContext ctx = new MyDbContext())
            {
                //将所有对象都添加到上下文的对象中
                ctx.orgUnits.Add(orgRoot);
                ctx.orgUnits.Add(orgAsia);
                ctx.orgUnits.Add(orgChina);
                ctx.orgUnits.Add(orgEurope);
                ctx.orgUnits.Add(orgEngland);
            
                //保存数据到数据库
                await ctx.SaveChangesAsync();
            }

        }

    }
}

程序运行后,查看数据库表记录:

 数据已经入库,并且自动插入了父节点的ID。

6.查询实例

下面使用递归打印出所有节点的信息:

using System;
using System.Linq;
using System.Threading.Tasks;

namespace 自引用的组织结构树
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                OrgUnit orgRoor = ctx.orgUnits.Single(o => o.Parent == null); //查询根节点
                Console.WriteLine(orgRoor.Name);
                PrintChildren(1, ctx, orgRoor);
            }

        }


        /// <summary>
        /// 缩进打印parent所有的子节点
        /// </summary>
        /// <param name="indentLevel">打印时的缩进级别</param>
        /// <param name="ctx">上下文</param>
        /// <param name="parent">父节点</param>
        static void PrintChildren(int indentLevel, MyDbContext ctx, OrgUnit parent)
        {
            var children = ctx.orgUnits.Where(o => o.Parent == parent);
            foreach (var child in children)
            {
                Console.WriteLine(new String('\t', indentLevel) + child.Name);

                PrintChildren(indentLevel + 1, ctx, child);//递归调用,打印以当前节点的子节点
            }
        }


    }
}

执行结果:

该方法使用于自己引用自己的场景,比如员工信息表(员工和上级)等。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值