• EntityFramework 实体拆分和表拆分


    之前有人问过 EF 如何进行实体拆分和表拆分?我记得当时认为不可能,理由忘记了,后来又有人发了一段配置截图,发现原来是可以的,不记录的东西容易忘掉,关于 EF 实体拆分和表拆分,下面是自己的一些整理。

    两个概念:

    • 实体拆分:一个实体拆分成多个表,如 Blog 实体,可以拆分成 Blogs 和 BlogDetails 两个表。
    • 表拆分:一个表拆分成多个实体,如 Posts 表,可以拆分成 Post 和 PostDetail 两个实体。

    1. 实体拆分

    配置代码:

    public class BloggingContext : DbContext
    {
        public BloggingContext()
            : base("name=ef_split_dbcontext")
        {
        }
    
        public DbSet<Blog> Blogs { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Blog>()
                .Map(m =>
                {
                    m.Properties(t => new { t.Id, t.Title, t.Url });
                    m.ToTable("Blogs");
                })
                .Map(m =>
                {
                    m.Properties(t => new { t.Id, t.Remark });
                    m.ToTable("BlogDetails");
                });
    
            base.OnModelCreating(modelBuilder);
        }
    }
    
    public class Blog
    {
        [Key]
        public int Id { get; set; }
        public string Title { get; set; }
        public string Url { get; set; }
        public string Remark { get; set; }
    }
    

    映射效果:

    测试代码:

    using (var context=new BloggingContext())
    {
        context.Blogs.Add(new Blog { Remark = "", Title = "EntityFramework 实体拆分和表拆分", Url = "http://www.cnblogs.com/xishuai/p/ef-entity-table-splitting.html" });
        context.SaveChanges();
    }
    

    测试结果为 Blogs 和 BlogDetails 表中,分别产生一条数据,即使 Remark 的值为空。

    2. 表拆分

    配置代码:

    public class BloggingContext : DbContext
    {
        public BloggingContext()
            : base("name=ef_split_dbcontext")
        {
        }
    
        public DbSet<Post> Posts { get; set; }
        public DbSet<PostDetail> PostDetails { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Post>()
                .HasKey(t => t.PostId);
    
            modelBuilder.Entity<PostDetail>()
                .HasKey(t => t.PostId);
    
            modelBuilder.Entity<PostDetail>()
                .HasRequired(t => t.Post)
                .WithRequiredPrincipal(t => t.PostDetail);
    
            modelBuilder.Entity<Post>().ToTable("Posts");
            modelBuilder.Entity<PostDetail>().ToTable("Posts");
    
            base.OnModelCreating(modelBuilder);
        }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    
        public virtual PostDetail PostDetail { get; set; }
    }
    
    public class PostDetail
    {
        public int PostId { get; set; }
        public string Remark { get; set; }
    
        public virtual Post Post { get; set; }
    }
    

    映射效果:

    测试代码:

    using (var context=new BloggingContext())
    {
        context.Posts.Add(new Post { Title="EntityFramework 实体拆分和表拆分"});
        context.PostDetails.Add(new PostDetail {  Remark=""});
        context.SaveChanges();
    }
    

    测试结果为 Posts 表中产生一条数据,注意映射配置中的这段代码:modelBuilder.Entity<PostDetail>().HasRequired(t => t.Post).WithRequiredPrincipal(t => t.PostDetail);,我们一般在外键配置的时候会用到 HasRequired,Required 表示的意思是必须,还有一种写法是:modelBuilder.Entity<PostDetail>().HasOptional(t => t.Post).WithOptionalPrincipal(t => t.PostDetail);,关键词 Optional,但映射会抱下面错误:The entity types 'Post' and 'PostDetail' cannot share table 'Posts' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them.

    其实我的想法是,在上面测试代码中,用了两个 Add(而不是一个 Add,然后用 PostDetail 属性赋值),那会不会在 Posts 表中产生两条数据,但显然没有,因为我们在映射配置的时候,使用的是 Required,可以理解为“强制合并为一个表”,不论你怎么添加数据,都会只添加一条数据,另外,需要注意的是,在上面表拆分示例中,主实体是 Post,所以,如果只有 context.PostDetails.Add(new PostDetail { Remark=""});,会抱下面错误:Invalid data encountered. A required relationship is missing. Examine StateEntries to determine the source of the constraint violation.

    参考:使用 Fluent API 配置/映射属性和类型

  • 相关阅读:
    Python2使用telnetlib库telnet登陆服务器
    Python paramiko使用密钥ssh登陆服务器
    paramiko AttributeError: 'NoneType' object has no attribute 'time'
    Python使用pexpect实现telnet登陆服务器
    Python paramiko SSH远程登陆服务器
    Java开发学习(十八)AOP通知获取数据(参数、返回值、异常)
    Java开发学习(二十五)使用PostMan完成不同类型参数传递
    Java开发学习(二十二)Spring事务属性、事务传播行为
    Java开发学习(十六)AOP切入点表达式及五种通知类型解析
    Java开发学习(二十四)SpringMVC设置请求映射路径
  • 原文地址:https://www.cnblogs.com/xishuai/p/ef-entity-table-splitting.html
Copyright © 2020-2023  润新知