假设我们在程序中要用到的类的结构是这样的,这里比较特别的是B在A中出现了最少两次

        public class B
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class A
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public B B1 { get; set; } /*第一次*/
public B B2 { get; set; } /*第二次*/
}

那我们如何通过EF框架映射到数据库中呢?

如果是下面这种方式就会非常简单,使用

public class B
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class A
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public B B1 { get; set; } /*第一次*/
}

Code First 指定外键名称中的任何方法都可以实现

-------------------------------------------------------------------------------------------

接下来将我使用过程中遇到的错误方法和正确方法都罗列出来

错误方法一:[ForeignKey("F_UserID")]注释 看代码

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApplication1
{
class Program
{ static void Main( string[] args )
{
using (var db = new MyTestContent())
{ db.A.Add(new A
{
Name = "A",
B1 = new B { Name = "B1" },
B2 = new B { Name = "B2" }
}); db.SaveChanges(); A a = db.A.FirstOrDefault();
}
} public class A
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int BId1 { get; set; }
[ForeignKey("BId1")]
public B B1 { get; set; } public int BId2 { get; set; }
[ForeignKey("BId2")]
public B B2 { get; set; }
}
public class B
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
} public class MyTestContent : DbContext
{
public MyTestContent( )
{
Database.SetInitializer<MyTestContent>(null);
}
public DbSet<B> B { get; set; }
public DbSet<A> A { get; set; }
}
}
}

执行add-migratiton createDB生成如下DbMigration

namespace EFLoading.Migrations
{
using System;
using System.Data.Entity.Migrations; public partial class createDB : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.A",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
BId1 = c.Int(nullable: false),
BId2 = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.B", t => t.BId1, cascadeDelete: true)
.ForeignKey("dbo.B", t => t.BId2, cascadeDelete: true)
.Index(t => t.BId1)
.Index(t => t.BId2); CreateTable(
"dbo.B",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.Id); } public override void Down()
{
DropForeignKey("dbo.A", "BId2", "dbo.B");
DropForeignKey("dbo.A", "BId1", "dbo.B");
DropIndex("dbo.A", new[] { "BId2" });
DropIndex("dbo.A", new[] { "BId1" });
DropTable("dbo.B");
DropTable("dbo.A");
}
}
}

在程序包管理器控制台中执行:Update-Database –Verbost,会出现这样的错误

Introducing FOREIGN KEY constraint 'FK_dbo.A_dbo.B_BId2' on table 'A' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.

Could not create constraint. See previous errors.

也就是说连数据库都没办法创建成功, 从字面意思上说可能这种方式需要在数据库中取消级联删除和更新功能,然后级联删除和更新需要代码操作来完成。

错误原因:不明确

错误方法二:[Column("BId1")]注释 看代码

按照错误一的方法操作,只是将类A的代码改为下方的样子

public class A
{
[Key]
public int Id { get; set; }
public string Name { get; set; } [Column("BId1")]
public int BId1 { get; set; }
public virtual B B1 { get; set; } [Column("BId2")]
public int BId2 { get; set; }
public virtual B B2 { get; set; }
}

错误方式同方法一:数据库创建失败

正确(可行)的方法:

依赖EF框架自动生成外键, 我们只需要指定导航属性即可,EF自动创建命名规则为:“表名_主键名”的复键

代码奉上

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace EFLoading
{
class Program
{
static void Main(string[] args)
{
using (var ctx = new EFLoadingContext())
{
A InsertA = new A
{
Name = "A",
B1 = new B { Name = "B1" },
B2 = new B { Name = "B2" }
};
ctx.A.Add(InsertA); ctx.SaveChanges(); A readA = ctx.A.FirstOrDefault();
}
}
} public class A
{
[Key]
public int Id { get; set; }
public string Name { get; set; } public virtual B B1 { get; set; } public virtual B B2 { get; set; }
}
public class B
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
} public class EFLoadingContext : DbContext
{
public EFLoadingContext( )
{
Database.SetInitializer<EFLoadingContext>(null);
}
public DbSet<A> A { get; set; }
public DbSet<B> B { get; set; }
}
}

执行add-migration createDB命令后生成的DbMigration的代码

namespace EFLoading.Migrations
{
using System;
using System.Data.Entity.Migrations; public partial class createDB : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.A",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
B1_Id = c.Int(),
B2_Id = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.B", t => t.B1_Id)
.ForeignKey("dbo.B", t => t.B2_Id)
.Index(t => t.B1_Id)
.Index(t => t.B2_Id); CreateTable(
"dbo.B",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.Id); } public override void Down()
{
DropForeignKey("dbo.A", "B2_Id", "dbo.B");
DropForeignKey("dbo.A", "B1_Id", "dbo.B");
DropIndex("dbo.A", new[] { "B2_Id" });
DropIndex("dbo.A", new[] { "B1_Id" });
DropTable("dbo.B");
DropTable("dbo.A");
}
}
}

执行

Update-Database -Verbose

命令数据库创建成功 数据库结构如下

我们再来看看程序执行后内存中的结果

级联更新成功了

同样方法测试级联删除也成功了

 

需要特别注意的地方

在A类的构造函数中不能包含B1 B2的new函数,比如下面的就会无法级联更新(更新后值都被清空了) ,下面是错误的 错误的 错误的

public class A
{
public A( )
{
B1 = new B { };
B2 = new B { };
}
[Key]
public int Id { get; set; }
public string Name { get; set; } public virtual B B1 { get; set; } public virtual B B2 { get; set; }
}

Code First:如何实现一个主类中包含多个复类的更多相关文章

  1. 一个.java源文件中可以有多个类吗?(内部类除外)有什么条件?

    一个.java源文件中可以有多个类吗?(内部类除外)有什么条件?带着这个疑惑,动手建几个测试类, 揭开心中的疑惑.以下是解开疑惑过程: package test;/** * 一个.java源文件中可以 ...

  2. 当实体类中entity/DTO/VO等类中,有枚举值,应该怎么输出?

    当实体类中entity/DTO/VO等类中,有枚举值,应该怎么输出? 问题: orderStatus 和 payStatus都是枚举类,并且枚举的个数达地10来个,我们不可能在模板页面(jsp/ftl ...

  3. Path,Files巩固,题目:从键盘接收两个文件夹路径,把其中一个文件夹中(包含内容)拷贝到另一个文件夹中

    这个题目用传统的File,InputStream可以做,但是如果用Files,Path类做,虽然思路上会困难一些,但是代码简洁了很多,以下是代码: import java.io.IOException ...

  4. YTU 2618: B 求类中数据成员的最大值-类模板

    2618: B 求类中数据成员的最大值-类模板 时间限制: 1 Sec  内存限制: 128 MB 提交: 430  解决: 300 题目描述 声明一个类模板,类模板中有三个相同类型的数据成员,有一函 ...

  5. 对Integer类中的私有IntegerCache缓存类的一点记录

    对Integer类中的私有IntegerCache缓存类的一点记录 // Integer类有内部缓存,存贮着-128 到 127. // 所以,每个使用这些数字的变量都指向同一个缓存数据 // 因此可 ...

  6. 黑马基础阶段测试题:创建Phone(手机)类,Phone类中包含以下内容:

    package com.swift; public class Phone { private String pinpai; private int dianliang; public String ...

  7. 如何在其他类中实现继承自CFormView类的对象

    今天项目开发中,我们创建了一个对话框资源,并创建了一个派生自CFormView的类(假设为CMyClassDlg)来管理它. CMyClassDlg.h #pragma once // CMyClas ...

  8. 在JBPM的Handle类中调用Spring管理的类

    我们在使用JBPM定义流程的时候经常要在流程定义文件中加入一个继承xxxHandler的类来实现我们的业务逻辑判断或者其他的需求,在这个类中一般都是用Spring的Application来获取,而这种 ...

  9. String类中"=="、equals和普通类中"=="、equals的比较

    package cn.method.demo; public class StringDemo2 { public static void main(String[] args) { String s ...

随机推荐

  1. ITopologicalOperator Buffer调用异常的解决方法 .异常来自 HRESULT:0x8004023E

    最近在做一个分析功能时,需要循环调用Buffer来对图形创建缓冲区.在开发测试时没问题,但拿到客户实际使用时,出现异常. 后来把出异常的数据拿来测试,发现在调用Buffer时出错.但做为参数传入的图形 ...

  2. 使用Netty进行Android与Server端通信实现文字发送接收与图片上传

    ANOTHER TITLE: Let’s use netty to achieve text send and receive and  image transfer to server based ...

  3. Clean Code(三):注释

    在工作中经常在刚开始写代码的时候,写下类与方法的注释,时间久了,CR多了,也不可能一直去修改注释了.加班都累成dog.注释才不改呢,就是这么任性,哈哈. 项目久了,注释会欺骗阅读者,本人被骗过,也骗过 ...

  4. html 如何引入一个公共的头部和底部

    2016-01-08 作者案:尽己之力,用心打造一个商城!当你有十二分力气时,发现用了十分力气依旧没什么进展,请不要放弃希望:或许你想得到的东西正在十二分力气的地方等你:假若你用尽了十二分力气,还是没 ...

  5. MVC知识总结(前序)

    距离2015年的来临还有1天的时间,是时候总结一下今年的经过脑子的知识了,由于今年里工作中接触MVC的时间特别多,所以打算针对MVC这个东西的知识进行一个总结,好歹对得起在几个项目中用了MVC来进行开 ...

  6. Asp.net 上传文件小叙(修改FileUpload显示文字等)

    想要在asp.net网站上上传文件就得用到FileUpload,可是这个控件中“浏览”没法修改,可以使用html中<input type="file" 来解决该问题. 首先页 ...

  7. 20151221jqueryUI---日历UI代码备份

    $(function () { $('#search_button').button({ icons : { primary : 'ui-icon-search', }, }); $('#reg'). ...

  8. Linux运维工程师面试

    一.Linux操作系统知识 1.常见的Linux发行版本都有什么?你最擅长哪一个?它的官网网站是什么?说明你擅长哪一块?   2.Linux开机启动流程详细步骤是什么?系统安装完,忘记密码如何破解? ...

  9. linux驱动(一)

    编写模块必须先声明下面两句: #include <linux/module.h>               //这个头文件包含了许多符号与函数的定义,这些符号与函数多与加载模块有关 #i ...

  10. /etc/rc.local ; /etc/init.d ;/etc/profile;/etc/bashrc;~/.bash_profile;~/.bashrc;~/.bash_logout

    1. /etc/rc.local 这是使用者自订开机启动程序,把需要开机自动运行的程序写在这个脚本里. 把脚本程序写在/etc/rc.d/init.d/目录下也可以  在完成 run level 3 ...