ADO.NET Entity Framework学习笔记(2)建模
模型结构
[概念模型]中要有[实体键], [实体键]要与表中的 [主键] 对应,也就是说表必须要有主键.
表中的[唯一键]不会在[概念模型]中体现
在[概念模型]中默认是不允许修改[实体键]的值的
联合主健可以正常映射
如果为属性赋值超过字段长度保存时,会向数据库提交,数据库会返回错误
联合主健的主外关系可以正常映射
只有基于主健的主外关系可以在模型向导中自动建立
Conceptual Model |
概念模型 用于描述实体(Entity)类型及其关系 | |
Storage Model |
存储模型 用于描述数据库实际存储架构 | |
Mapping Specification |
映射规范 将概念模型和存储模型连接起来,以便进行操作转换 | |
Entity Class |
实体类 用于描述实体的属性,每一个实体类都会定义一个属性或多个属性为一个键属性(Key Properties),用于唯一标识一个实体 实体类型可以通过继承关系加以扩展 | |
Entity Set |
实体集 实体(Entity)存在于实体集(Entity Set)中,就像表格中的行存在于表格中的一样 | |
Entity Containe |
实体容器, 实体集定义在实体容器(Entity Container)中 | |
关联 |
关联 定义了实体之间的关系,可以直接通过关联,来访问相关联的对象,关联分为一对一、一对多、多对多 关联通过Association Type来定义,过实体类中的Navigation属性就可以访问与实体相关联的实体 |
模型关系说明
模型设计器结构说明
EDM
EF 没有采取 LINQ to SQL 基于Attribute映射的做法。
为了适应变化和提供更多的数据库类型扩展,EF 提供了专门的定义语言来完成模型设置
Conceptual schema definition language (.csdl) |
|
Store schema definition language (.ssdl) |
|
Mapping specification language (.msl) |
默认,Model设计器将(.csdl)(.ssdl)(.msl)存放在一个名为(.edmx)的XML 格式定义文件,并会根据设计自动生成对应的Context与实体类
Model设计器
数据库中的表 |
|
模型 |
|
模型生成选项 |
edmx文件
<?xml version="1.0" encoding="utf-8"?> <edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx"> <!-- EF Runtime content --> | |
<edmx:Runtime> | |
存储模型 |
<!-- SSDL content --> <edmx:StorageModels> <Schema Namespace="myModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl"> <EntityContainer Name="myModelStoreContainer"> <EntitySet Name="myTab" EntityType="myModel.Store.myTab" store:Type="Tables" Schema="dbo" /> </EntityContainer> <EntityType Name="myTab"> <Key> <PropertyRef Name="a" /> </Key> <Property Name="a" Type="nchar" Nullable="false" MaxLength="10" /> <Property Name="b" Type="nchar" Nullable="false" MaxLength="10" /> <Property Name="c" Type="nchar" MaxLength="10" /> <Property Name="d" Type="nchar" MaxLength="10" /> </EntityType> </Schema> </edmx:StorageModels> |
概念模型 |
<!-- CSDL content --> <edmx:ConceptualModels> <Schema Namespace="myModel" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm"> <EntityContainer Name="mySets"> <EntitySet Name="myTab" EntityType="myModel.myTab" /> </EntityContainer> <EntityType Name="myTab"> <Key> <PropertyRef Name="aa" /> </Key> <Property Name="aa" Type="String" Nullable="false" MaxLength="10" Unicode="true" FixedLength="true" /> <Property Name="bb" Type="String" Nullable="false" MaxLength="10" Unicode="true" FixedLength="true" /> <Property Name="cc" Type="String" MaxLength="10" Unicode="true" FixedLength="true" /> <Property Name="dd" Type="String" MaxLength="10" Unicode="true" FixedLength="true" />
</EntityType> </Schema> </edmx:ConceptualModels> |
映射 |
<!-- C-S mapping content --> <edmx:Mappings> <Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS"> <EntityContainerMapping StorageEntityContainer="myModelStoreContainer" CdmEntityContainer="mySets"> <EntitySetMapping Name="myTab"> <EntityTypeMapping TypeName="IsTypeOf(myModel.myTab)"> <MappingFragment StoreEntitySet="myTab"> <ScalarProperty Name="aa" ColumnName="a" /> <ScalarProperty Name="bb" ColumnName="b" /> <ScalarProperty Name="cc" ColumnName="d" /> <ScalarProperty Name="dd" ColumnName="c" /> </MappingFragment> </EntityTypeMapping> </EntitySetMapping> </EntityContainerMapping> </Mapping> </edmx:Mappings> |
</edmx:Runtime> | |
图形设计器的配置部份 |
<!-- EF Designer content (DO NOT EDIT MANUALLY BELOW HERE) --> <edmx:Designer xmlns="http://schemas.microsoft.com/ado/2007/06/edmx"> <edmx:Connection> <DesignerInfoPropertySet> <DesignerProperty Name="MetadataArtifactProcessing" Value="EmbedInOutputAssembly" /> </DesignerInfoPropertySet> </edmx:Connection> <edmx:Options> <DesignerInfoPropertySet> <DesignerProperty Name="ValidateOnBuild" Value="true" /> </DesignerInfoPropertySet> </edmx:Options> <!-- Diagram content (shape and connector positions) --> <edmx:Diagrams> <Diagram Name="myModel"> <EntityTypeShape EntityType="myModel.myTab" Width="1.5" PointX="0.75" PointY="0.75" Height="1.7" IsExpanded="true" /> </Diagram> </edmx:Diagrams> </edmx:Designer> |
</edmx:Edmx> |
Context
public class myContext :ObjectContext {
public myContext(EntityConnection connection) : base(connection, "mySets") {
}
public ObjectQuery<myTab> myTab { get { if ((this._myTab == null)) { this._myTab = base.CreateQuery<myTab>("[myTab]"); } return this._myTab; } } private ObjectQuery<myTab> _myTab;
public void AddTomyTab(myTab myTab) { base.AddObject("myTab", myTab); } } |
实体类
[EdmEntityType(NamespaceName = "myModel", Name = "myTab")] [DataContract(IsReference = true)] [Serializable()]
public class myTab :EntityObject {
public static myTab CreatemyTab(string aa, string bb) { myTab myTab = new myTab(); myTab.aa = aa; myTab.bb = bb; return myTab; }
private string _aa; [EdmScalarProperty(EntityKeyProperty = true, IsNullable = false)] [DataMember()] public string aa { get { return this._aa; } set { this.ReportPropertyChanging("aa"); this._aa = StructuralObject.SetValidValue(value, false); this.ReportPropertyChanged("aa"); } }
private string _bb; [EdmScalarPropertyAttribute(IsNullable = false)] [DataMemberAttribute()] public string bb { get { return this._bb; } set {
this.ReportPropertyChanging("bb"); this._bb = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false); this.ReportPropertyChanged("bb");
} }
// private string _cc; [EdmScalarProperty()] [DataMember()] public string cc { get { return this._cc; } set { this.ReportPropertyChanging("cc"); this._cc =StructuralObject.SetValidValue(value, true); this.ReportPropertyChanged("cc"); } } // private string _dd; [EdmScalarProperty()] [DataMember()] public string dd { get { return this._dd; } set { this.ReportPropertyChanging("dd"); this._dd = StructuralObject.SetValidValue(value, true); this.ReportPropertyChanged("dd"); } }
} |
使用
myContext cn;
EntityConnection econ = new EntityConnection();
string s = @" metadata=res://*/myModel.csdl |res://*/myModel.ssdl |res://*/myModel.msl ;
provider=System.Data.SqlClient;
provider connection string="" Data Source=.; Initial Catalog=LingTestDB; Integrated Security=True; MultipleActiveResultSets=True; "" ";
econ.ConnectionString = s;
cn = new myDate.myContext(econ);
this.dataGridView1.DataSource = cn.myTab; |
使用向导创建模型
映射基本规则
1.[概念模型]的[数据类型],[键],[非空]约束要与[存储模型]对应
2.[概念模型]的属性名与[存储模型]的字段名不必一致
3.[存储模型]的字段如果有[键],[非空]约束, [概念模型]必需要有对应的属性与之映射
4. [概念模型]中出现的属性,在[存储模型]中必须要有对应的字段映射
5. [存储模型]的多个字段可以映射到[概念模型]的同一个属性上
加载时, [概念模型]的属性加载[存储模型]的第一个字段值
保存时, [概念模型]的属性会保存到[存储模型]的多个字段上
映射条件
- 一个字段,如果要作为映射条件,就不能映射到属性上
- 加载数据时,只会加载符合条件的数据
- 添加数据时, 映射条件字段不可见, 射条件字段的字直接使用设计时的设定值
继承
1. 要实现继承,要使用映射条件进行筛选
2. [概念模型]与 [子概念模型] 所有的筛选条件不能有相的
3. [子概念模型] 自动得到 [概念模型] 的属性
4. [子概念模型] 中可以没有属性
5. 所的[子概念模型] 的筛选条件将以[Or]的方式影响[概念模型]的筛选,也就是说[概念模型]的记录将包括[子概念模型] 记录
6. [概念模型]与 [子概念模型] 共用同一个 [实体集], [实体集]的数据类型为 ObjectQuery<概念模型>
7. 可以使用类型转换的方式从[实体集]得[子概念模型]的数据
myContext context = new myContext(); ObjectQuery<myTab> a = context.myTabSets; ObjectQuery<myTabEx1> b = context.myTabSets.OfType<myTabEx1>(); ObjectQuery<myTabEx2> c = context.myTabSets.OfType<myTabEx2>(); |
多表联合
1. 不要求在数据库中有关系,最好是主键对主键
2. 使用对联的方式,只筛选出有对应键的记录
3. 插入时,会在两个表中同时插入键相同的记录
4. 删除时,会同时在两个表中删除键相同的记录
关联
[(1)—(*)] 关联
1. [(1)—(*)]为主外关系,主键表为(1),外键表为(*)
2. 外键字段不能出现在[概念模型]中
3. 可以在向导中直接跟据数据库中的关系直接生成,也可以在设计[概念模型]时任意添加
4. 关联要绑定到外键表上
5. 要删除主键时,要保证内存中没有对应的外键数据
6. 插入外键数据,可以插入到主键的外键集合,也可创建带主键信息的外键对象后直接插入到Context中
表
设置
操作数据
从主键表访问数据 |
myContext context = new myContext();
ObjectQuery<tab1> tab1_List = context.tab1;
foreach (tab1 tab1_Entity in tab1_List) {
Console.WriteLine("{0},{1}", tab1_Entity.a, tab1_Entity.b);
EntityCollection<tab2> tab2_List = tab1_Entity.Nvtab2;
tab2_List.Load(); //加载外键数据
foreach (tab2 tab2_Entity in tab2_List) { Console.WriteLine("{0},{1},{2}", tab2_Entity.ID,tab2_Entity.x,tab2_Entity.y); } } |
lzm ,456 3 ,5 ,6 4 ,7 ,8 wxd ,123 1 ,1 ,2 2 ,3 ,4 | |
从外键表访问数据 |
myContext context = new myContext(); ObjectQuery<tab2> tab2_List = context.tab2;
foreach (tab2 tab2_Entity in tab2_List) { Console.WriteLine("{0},{1},{2}", tab2_Entity.ID, tab2_Entity.x, tab2_Entity.y);
tab2_Entity.NVtab1Reference.Load(); //加载主键数据
tab1 tab1 = tab2_Entity.NVtab1;
Console.WriteLine("{0},{1}", tab1.a, tab1.b); } |
1 ,1 ,2 wxd ,123 2 ,3 ,4 wxd ,123 3 ,5 ,6 lzm ,456 4 ,7 ,8 lzm ,456 | |
删除主键 |
myContext context = new myContext(); var v = context.tab1.First(p => p.a == "wxd");
// 要删除主键时,要保证内存中对应的外键数据为空
//v.Nvtab2.Load(); //if (v.Nvtab2.Count != 0) //{ // List<tab2> list = new List<tab2>(); // foreach (var w in v.Nvtab2) // { // list.Add(w); // } // foreach (var ww in list) // {
// context.DeleteObject(ww); // } //}
context.DeleteObject(v);
context.SaveChanges(); |
直接插入外键数据 |
myContext context = new myContext(); tab2 tab2 = new tab2();
tab2.ID = "101"; tab2.x = "555"; tab2.y = "666";
tab2.Nvtab1 = context.tab1.First(p => p.a == "wxd"); //要有主键信息
context.AddTotab2(tab2);
context.SaveChanges(); |
通过主键插入外键数据 |
myContext context = new myContext();
var tab1 = context.tab1.First(p => p.a == "wxd");
//第1条外键记录 tab2 r1 = new tab2(); r1.ID = "103"; r1.x = "r1"; r1.y = "r1"; tab1.Nvtab2.Add(r1);
//第2条外键记录 tab2 r2 = new tab2(); r2.ID = "104"; r2.x = "r1"; r2.y = "r1"; tab1.Nvtab2.Add(r2);
context.SaveChanges(); |
[(1)—(1)]关联
1. [(1)—(1)]为主主关系,既两个表的主键关联
2. 两个表的地位是平等的
3. 主键字段不能删除
4. 可以在向导中直接跟据数据库中的关系直接生成,也可以在设计[概念模型]时任意添加
5. 关联可绑定到任意一个表上
6. 删除时,要先将关联数据删除
7. 插入时,要有与之关联的数据
表
设置
操作数据
读取 |
myContext context = new myContext();
ObjectQuery<tab1> tab1_List = context.tab1;
foreach (tab1 tab1_Entity in tab1_List) {
Console.WriteLine("{0},{1}", tab1_Entity.a, tab1_Entity.b);
tab1_Entity.NVtab3Reference.Load(); //加载关联数据
tab3 tab3 = tab1_Entity.NVtab3;
Console.WriteLine("{0},{1},{2}", tab3.a, tab3.m, tab3.n);
} |
lzm ,456 lzm ,mmmm ,nnnn wxd ,123 wxd ,nnn ,nnn | |
myContext context = new myContext();
ObjectQuery<tab3> tab3_List = context.tab3;
foreach (tab3 tab3_Entity in tab3_List) {
Console.WriteLine("{0},{1},{2}", tab3_Entity.a, tab3_Entity.m,tab3_Entity.n );
tab3_Entity.NVtab1Reference.Load(); //加载关联数据
tab1 tab1 = tab3_Entity.NVtab1;
Console.WriteLine("{0},{1}", tab1.a, tab1.b );
} | |
lzm ,mmmm ,nnnn lzm ,456 wxd ,nnn ,nnn wxd ,123 | |
删除 |
myContext context = new myContext();
var tab3 = context.tab3.First(p => p.a == "wxd");
//-要按顺序
tab3.NVtab1Reference.Load(); // 加载关联数据
context.DeleteObject(tab3.NVtab1); //删除关联数据
context.DeleteObject(tab3); // 删除
// context.SaveChanges(); |
插入 |
myContext context = new myContext();
tab1 tab1 = new tab1(); tab1.a = "wxwinter"; tab1.b = "789";
//关联 tab1.NVtab3 = new tab3(); tab1.NVtab3.a = "wxwinter"; tab1.NVtab3.m = "mm"; tab1.NVtab3.n = "mm";
context.AddTotab1(tab1);
context.SaveChanges(); |
[(1)—(0..1)]关联
设计与[(1)—(0..1)]相同.
1. 插入(1)时,可以不插入(0..1)
2. 删除(0..1) 时,可以不删除(1)
3. 插入(0..1)时,需要插入(1)
4. 删除(1) 时, 需要删除(0..1)
[(*)—(*)]关联
表
显示中转方式
隐示中转方式
1. 要实现这个关联,要将关联绑定到 [中转表(安排表)]上
2. 所用的[中转表(安排表)]不能以[概念模型]的方式出现
3. 两个表的地位是平等的
4. 添加一端的(*)时,不受另一端(*)约束
5. 可以在添加一端的(*)时,通过[关联集合]将数据也添加到[关联表]与[中转表]中
6. 如果加载关联数据,[中转表]中的关联数据也会被删除.
如果不加载关联数据,将不会删除[中转表]中的关联数据
操作数据
得到数据 |
myContext context = new myContext();
ObjectQuery<部门表> bList = context.部门表;
foreach (部门表 b in bList) { Console.WriteLine("{0},{1}", b.部门, b.部门说明);
b.场地表.Load();
EntityCollection<场地表> cList = b.场地表;
foreach (场地表 c in cList) { Console.WriteLine("{0},{1}", c.场地, c.场地说明); } } |
丙 ,1 a ,11 b ,22 c ,33 甲 ,2 a ,11 乙 ,3 a ,11 b ,22 | |
myContext context = new myContext();
ObjectQuery<场地表> cList = context.场地表;
foreach (场地表 c in cList) { Console.WriteLine("{0},{1}", c.场地, c.场地说明);
c.部门表.Load();
EntityCollection<部门表> bList = c.部门表;
foreach (部门表 b in bList) { Console.WriteLine("{0},{1}", b.部门, b.部门说明); } } | |
a ,11 丙 ,1 甲 ,2 乙 ,3 b ,22 丙 ,1 乙 ,3 c ,33 丙 ,1 | |
删除数据 |
myContext context = new myContext();
场地表 c = context.场地表.First(p => p.场地 == "a");
//1.如果加载关联数据,[中转表]中的关联数据也会被删除, //2.如果不加载关联数据,将不会删除[中转表]中的关联数据
//c.部门表.Load(); // <-------[是/否]
context.DeleteObject(c); context.SaveChanges(); |
添加数据 |
myContext context = new myContext();
场地表 c = new 场地表(); c.场地 = "d"; c.场地说明 = "44"; context.AddTo场地表(c); context.SaveChanges(); |
同时添加数据到 [源表], [关联表], [中转表] |
//可以将数据同时添加[源表],[关联表],[中转表] myContext context = new myContext();
场地表 c = new 场地表(); c.场地 = "e"; c.场地说明 = "55"; c.部门表.Add(new 部门表() { 部门 = "vv", 部门说明 = "vv" }); context.AddTo场地表(c); context.SaveChanges(); |
映射存储过程与函数
返回表型存储过程
存储过程
查询 PRSelectDBItem |
CREATE PROCEDURE PRSelectDBItem @v nvarchar(50)
as select * from DBItem where ItemID=@v |
使用向导生成映射
myContext context = new myContext();
ObjectResult<DBItem> dbitemList= context.PRSelectDBITem("a");
foreach (var r in dbitemList) { Console.WriteLine("{0},{1}", r.ItemID, r.ItemMatter); } |
代码实现
public class Context :ObjectContext { public Context() : base("name=myContext", "myContext") { } public ObjectResult<DBItem> PRSelectDBITem(string v) { return base.ExecuteFunction<DBItem>("PRSelectDBITem", new ObjectParameter("v", v)); } } |
Context context = new Context();
ObjectResult<DBItem> dbitemList = context.PRSelectDBITem("a");
foreach (var r in dbitemList) { Console.WriteLine("{0},{1}", r.ItemID, r.ItemMatter); } |
命令型存储过程
存储过程
插入 PRinsertDBItem |
CREATE PROCEDURE PRinsertDBItem @ItemID nvarchar(50), @ItemMatter nvarchar(50)
as insert into DBItem (ItemID,ItemMatter) values (@ItemID ,@ItemMatter) |
使用向导生成映射
代码实现
string econString = @" metadata=res://*/myModel.csdl |res://*/myModel.ssdl |res://*/myModel.msl ;
provider=System.Data.SqlClient;
provider connection string="" Data Source=.; Initial Catalog=LingTestDB; Integrated Security=True; MultipleActiveResultSets=True; "" ";
EntityConnection econ = new EntityConnection(); econ.ConnectionString = econString;
EntityCommand ecmd = new EntityCommand();
ecmd.CommandType = CommandType.Text;
ecmd.Connection = econ;
ecmd.CommandText = "myContext.PRinsertDBItem";
ecmd.CommandType = CommandType.StoredProcedure;
EntityParameter p1 = new EntityParameter("ItemID", System.Data.DbType.String); p1.Value = "aaa";
EntityParameter p2 = new EntityParameter("ItemMatter", System.Data.DbType.String); p2.Value = "bbb";
ecmd.Parameters.Add(p1);
ecmd.Parameters.Add(p2);
econ.Open();
ecmd.ExecuteNonQuery(); |
参数返回值型存储过程
存储过程
算加法 PRadd |
CREATE PROCEDURE PRadd @x int = 0, @y int = 0, @s int = 0 output
as set @s= @x * @y |
使用向导生成映射
代码实现
string econString = @" metadata=res://*/myModel.csdl |res://*/myModel.ssdl |res://*/myModel.msl ;
provider=System.Data.SqlClient;
provider connection string="" Data Source=.; Initial Catalog=LingTestDB; Integrated Security=True; MultipleActiveResultSets=True; "" ";
EntityConnection econ = new EntityConnection(); econ.ConnectionString = econString;
EntityCommand ecmd = new EntityCommand();
ecmd.CommandType = CommandType.Text;
ecmd.Connection = econ;
ecmd.CommandText = "myContext.PRadd";
ecmd.CommandType = CommandType.StoredProcedure;
EntityParameter p1 = new EntityParameter("x", System.Data.DbType.Int32); p1.Value = 123;
EntityParameter p2 = new EntityParameter("y", System.Data.DbType.Int32); p2.Value = 456;
EntityParameter rt = new EntityParameter("s", System.Data.DbType.Int32, 0, ParameterDirection.Output, false, 0, 0, "", DataRowVersion.Current, 0);
ecmd.Parameters.Add(p1);
ecmd.Parameters.Add(p2); ecmd.Parameters.Add(rt);
econ.Open();
ecmd.ExecuteNonQuery();
Console.WriteLine(rt.Value); //579 |