1-1 设置
//DataAnnotation 1-1 | 1-0 table //SQLtable : member , columns : memberId, name //SQL basic logic : 1个table的PK, 是另一个table的PK and FK , 这就是1-1和1-0的关系 [Table("member")] public class Member { [Key] public Int32 memberId { get; set; } public string name { get; set; } [Required] /* 1-0,这里就不要Required 1-1关系最好是放Required啦,在insert的时候才不会出错,但是... 如果你写Required,那么你在做update的时候这个address不能是Null,也就是说一定要inner join出来先,不是很方便. */ public virtual Address address { get; set; } } //SQLtable : address , columns : memberId, postcode, country [Table("address")] public class Address { /* 关键就是这个关系连接, "member" 是 tableName */ [Key, ForeignKey("member")] //foreignKey 一定要写在这里,不可以写在 Member 哦 public Int32 addressId { get; set; } //这里取名addressId也行 public string postcode { get; set; } public string country { get; set; } public virtual Member member { get; set; } }
注 : ForeignKey("member") 的member 是取至于 virtual Member "member" , 这2个要一直
基本的 CRUD
using (EFDB db = new EFDB()) { /*insert*/ db.members.Add(new Member { name = "keatkeat", address = new Address { postcode = "81300", country = "malaysia" } }); db.SaveChanges(); /*select*/ var members = db.members.Include(m => m.address).ToList(); //要include才会一起出来哦 /*update*/ var member = db.members.Include(m => m.address).First().name = "xinyao123"; db.SaveChanges(); /*delete*/ db.members.Remove(db.members.Include(m => m.address).First()); //要include出来才能一次删除2个table的数据! db.SaveChanges(); }
这里有一个要注意的 :
如果 A 和 B 是 1-1 关系, 而 B 是一抽象类 C,D 是B的派生类,那么当我们想要替换掉 C 时,我们并不能单纯的 a.b = new D()
我们也不可以先删除再添加
db.Bs.remove(a.b);
a.b = new D();
我们必须使用 transaction, SaveChanges 2 次,
transaction.open();
db.Bs.remove(a.b);
db.SaveChanges();
a.b = new D();
db.SaveChanges();
transaction.commit();
这样才可以!
1-n 设置
//1-n /* 基本上如果没有必要就不要写required了,这样比较方便在update单个table的时候.(insert的时候自己要控制好!) */ [Table("prod")] public class Prod { [Key] public int prodId { get; set; } public string code { get; set; } [InverseProperty("prod")] //因为有超过1个,所以我们要告诉entity,这对应哪一个 public virtual ICollection<Color> colors { get; set; } //有多个colors,用ICollection | List 也可以 [InverseProperty("prodx")] public virtual ICollection<Color> color2s { get; set; } } [Table("color")] public class Color { [Key] public int colorId { get; set; } public string name { get; set; } [ForeignKey("prod")] //指定一个FK, "prod" 是attr属性名,不是sql的哦 public int prodId { get; set; }
[ForeignKey("prodId")] //虽然写这里也是可以表达foreignKey 关系,但是最好不要这样! public virtual Prod prod { get; set; } //有一个prod [ForeignKey("prodx")] public int prodxId { get; set; } public virtual Prod prodx { get; set; } }
InverseProperty (如果有做,lastModifier,handler就会有了),2个表有超过1个 1-n 关系,就要表明清楚对应的是哪一个。
1-n 的 foreignKey 不可以是 0, 如果真的没有也只能放 Null, 而且sql 要是nullable , foreignKey 类型是 int?.
nullable 虽然不会影响sql对foreignKey关系的验证或者级联,但是对索引搜索不好,所以还个简单的方法就是主表default create 1 row for 这种没有的情况,那么foreignKey 就可以连接到这row id 了。
n-n 设置
n-n 我不鼓励用 DataAnnotation, 除非用 default的tableName,比如 category , prod = prodcategories , columns = Prod_prodId , Category_categoryId (基本上debug几次就可以知道它的自动命名了)
如果是要自己取名字的话,比如 category_and_prod,那就麻烦多多了...
参考 : https://social.msdn.microsoft.com/Forums/en-US/9d7a4803-53c6-44be-823d-dce6e85bb497/ef-41-customize-codefirst-manytomany-table-using-attributes
/* 2表的连接表 */ [Table("prod_mm_category")] public class ProdAndCategory { [Key] public Int32 ProdAndCategoryId { get; set; } [Key] [Column(Order = 1)] [ForeignKey("category")] public int categoryId { get; set; } [Key] [Column(Order = 2)] [ForeignKey("prod")] public int prodId { get; set; } public Category category { get; set; } public Prod prod { get; set; } } [Table("category")] public class Category { [Key] public Int32 categoryId { get; set; } public string name { get; set; } /* 注意这里是 ProdAndCategory 哦 */ public virtual ICollection<ProdAndCategory> prods { get; set; } } [Table("prod")] public class Prod { [Key] public Int32 prodId { get; set; } public string code { get; set; } public virtual ICollection<ProdAndCategory> categorys { get; set; } }
insert 也超麻烦..
db.categorys.Add(new Category { name = "Man", prods = new List<ProdAndCategory> { /* 这里很麻烦吧... */ new ProdAndCategory { prod = new Prod { code = "MK100", } }, new ProdAndCategory { prod = new Prod { code = "MK200", } }, } });
所以可以考虑用 Fluent API 替代
protected override void OnModelCreating(DbModelBuilder modelBuilder) { /* cateogry "HasMany" prod , prod "WithMany" category , mapping logic */ modelBuilder.Entity<Category>().HasMany<Prod>(c => c.prods).WithMany(p => p.categorys).Map(m => { m.MapLeftKey("Category_categoryId"); //第3 table 的连接 columnName 可以自己顺便取 m.MapRightKey("prodId"); //第3 table 的连接 columnName m.ToTable("prodcategories"); //第3 tableName }); base.OnModelCreating(modelBuilder); }
用 Fluent API 或是自动命名 insert 也简单了
db.categorys.Add(new Category { name = "Man", prods = new List<Prod> { new Prod { code = "MK100", }, new Prod { code = "MK200", } } }); db.SaveChanges();