1
首先在项目中添加一个ADO.NET实体数据模型,选好需要的表后,完成。然后它就会自动给我们应用了如下这些类库,下面是介绍这些类库的作用,及对edmx这个XML文件进行介绍
关于edmx文件的介绍 (如何查看这个Model1.edmx文件的XML文件呢?Model1.edmx-->(右键)打开方式-->XML(文本)编辑器)
展开来看看
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="3.0" xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx">
<!-- EF Runtime content -->
<edmx:Runtime>
<!-- SSDL content 【第一个节点:这是一个描述数据库的配置节】-->
<edmx:StorageModels>
<Schema Namespace="salesModel.Store" Provider="System.Data.SqlClient" ProviderManifestToken="2008" Alias="Self" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/11/edm/ssdl">
<EntityType Name="T_User">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="int" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="UserName" Type="nvarchar" MaxLength="50" Nullable="false" />
<Property Name="Password" Type="varchar" MaxLength="50" Nullable="false" />
<Property Name="Email" Type="varchar" MaxLength="50" Nullable="false" />
<Property Name="Errors" Type="int" Nullable="false" />
<Property Name="Session" Type="nvarchar" MaxLength="50" Nullable="false" />
</EntityType>
<EntityType Name="T_UserInfo">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="int" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="UserName" Type="nvarchar" MaxLength="50" />
<Property Name="Name" Type="nvarchar" MaxLength="50" />
<Property Name="Age" Type="int" />
<Property Name="Gender" Type="int" />
<Property Name="Mobile" Type="varchar" MaxLength="50" />
<Property Name="Email" Type="varchar" MaxLength="50" />
<Property Name="Addres" Type="nvarchar" MaxLength="100" />
<Property Name="Remarks" Type="nvarchar" MaxLength="200" />
</EntityType>
<EntityContainer Name="salesModelStoreContainer">
<EntitySet Name="T_User" EntityType="Self.T_User" Schema="dbo" store:Type="Tables" />
<EntitySet Name="T_UserInfo" EntityType="Self.T_UserInfo" Schema="dbo" store:Type="Tables" />
</EntityContainer>
</Schema>
</edmx:StorageModels>
<!-- CSDL content 【第二个节点:这是一个描述实体的配置节】-->
<edmx:ConceptualModels>
<Schema Namespace="salesModel" Alias="Self" annotation:UseStrongSpatialTypes="false" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
<!--EntityType表示类 Nmae表示类的名字叫什么-->
<EntityType Name="T_User">
<!--Key表示主键,PropertyRef Name="Id"表示主键为Id-->
<Key>
<PropertyRef Name="Id" />
</Key>
<!--Property表示类中的属性,Name表示属性的名称,Type表示属性的类型,Nullable="false"表示这个字段不能为空,annotation:StoreGeneratedPattern="Identity"表示这个属性是自增的-->
<Property Name="Id" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<!--MaxLength表示UserName这个属性的最大长度值为50,FixedLength表示是否要固定长度,为true就表示要固定这个长度不变-->
<Property Name="UserName" Type="String" MaxLength="50" FixedLength="false" Unicode="true" Nullable="false" />
<Property Name="Password" Type="String" MaxLength="50" FixedLength="false" Unicode="false" Nullable="false" />
<Property Name="Email" Type="String" MaxLength="50" FixedLength="false" Unicode="false" Nullable="false" />
<Property Name="Errors" Type="Int32" Nullable="false" />
<Property Name="Session" Type="String" MaxLength="50" FixedLength="false" Unicode="true" Nullable="false" />
</EntityType>
<EntityType Name="T_UserInfo">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Name="UserName" Type="String" MaxLength="50" FixedLength="false" Unicode="true" />
<Property Name="Name" Type="String" MaxLength="50" FixedLength="false" Unicode="true" />
<Property Name="Age" Type="Int32" />
<Property Name="Gender" Type="Int32" />
<Property Name="Mobile" Type="String" MaxLength="50" FixedLength="false" Unicode="false" />
<Property Name="Email" Type="String" MaxLength="50" FixedLength="false" Unicode="false" />
<Property Name="Addres" Type="String" MaxLength="100" FixedLength="false" Unicode="true" />
<Property Name="Remarks" Type="String" MaxLength="200" FixedLength="false" Unicode="true" />
</EntityType>
<EntityContainer Name="salesEntities" annotation:LazyLoadingEnabled="true">
<EntitySet Name="T_User" EntityType="Self.T_User" />
<EntitySet Name="T_UserInfo" EntityType="Self.T_UserInfo" />
</EntityContainer>
</Schema>
</edmx:ConceptualModels>
<!-- C-S mapping content 【第三个节点:这是一个描述数据库与实体之间的关系映射的配置节】-->
<edmx:Mappings>
<Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2009/11/mapping/cs">
<EntityContainerMapping StorageEntityContainer="salesModelStoreContainer" CdmEntityContainer="salesEntities">
<EntitySetMapping Name="T_User">
<EntityTypeMapping TypeName="salesModel.T_User">
<MappingFragment StoreEntitySet="T_User">
<!--Name="Id"表示实体里面的属性名Id ColumnName="Id"表示数据库表中的Id属性,即实体中的Id对应数据库表中的Id列-->
<ScalarProperty Name="Id" ColumnName="Id" />
<ScalarProperty Name="UserName" ColumnName="UserName" />
<ScalarProperty Name="Password" ColumnName="Password" />
<ScalarProperty Name="Email" ColumnName="Email" />
<ScalarProperty Name="Errors" ColumnName="Errors" />
<ScalarProperty Name="Session" ColumnName="Session" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<EntitySetMapping Name="T_UserInfo">
<EntityTypeMapping TypeName="salesModel.T_UserInfo">
<MappingFragment StoreEntitySet="T_UserInfo">
<ScalarProperty Name="Id" ColumnName="Id" />
<ScalarProperty Name="UserName" ColumnName="UserName" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="Age" ColumnName="Age" />
<ScalarProperty Name="Gender" ColumnName="Gender" />
<ScalarProperty Name="Mobile" ColumnName="Mobile" />
<ScalarProperty Name="Email" ColumnName="Email" />
<ScalarProperty Name="Addres" ColumnName="Addres" />
<ScalarProperty Name="Remarks" ColumnName="Remarks" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
</EntityContainerMapping>
</Mapping>
</edmx:Mappings>
</edmx:Runtime>
<!-- EF Designer content (DO NOT EDIT MANUALLY BELOW HERE) -->
<Designer xmlns="http://schemas.microsoft.com/ado/2009/11/edmx">
<Connection>
<DesignerInfoPropertySet>
<DesignerProperty Name="MetadataArtifactProcessing" Value="EmbedInOutputAssembly" />
</DesignerInfoPropertySet>
</Connection>
<Options>
<DesignerInfoPropertySet>
<DesignerProperty Name="ValidateOnBuild" Value="true" />
<DesignerProperty Name="EnablePluralization" Value="false" />
<DesignerProperty Name="IncludeForeignKeysInModel" Value="true" />
<DesignerProperty Name="UseLegacyProvider" Value="true" />
<DesignerProperty Name="CodeGenerationStrategy" Value="无" />
</DesignerInfoPropertySet>
</Options>
<!-- Diagram content (shape and connector positions) -->
<Diagrams></Diagrams>
</Designer>
</edmx:Edmx>
我们在来看看Model1.Context.cs这个上下文容器类
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码是根据模板生成的。
//
// 手动更改此文件可能会导致应用程序中发生异常行为。
// 如果重新生成代码,则将覆盖对此文件的手动更改。
// </auto-generated>
//------------------------------------------------------------------------------
namespace WebAppEF
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class salesEntities : DbContext
{
/// <summary>
/// 用来初始化ado.net连接对象
/// </summary>
public salesEntities()
: base("name=salesEntities")//这里调用父类的带一个参数的构造函数,传递的"name=salesEntities"值就表示告诉我们 去web.config配置文件中 找到<connectionStrings>这个节点下name为salesEntities的这个配置节点,用它来初始化我们的ado.net连接对象
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
//下面我们来看看这个DbSet<T> 这个泛型类。以下是这个类的定义:
// public class DbSet<TEntity> : DbQuery<TEntity>, IDbSet<TEntity>, IQueryable<TEntity>, IEnumerable<TEntity>, IQueryable, IEnumerable, IInternalSetAdapter where TEntity: class
//根据DbSet<T>的定义我们可以看到这个泛型类是继承了 IQueryable<TEntity>, IEnumerable<TEntity>, IQueryable, IEnumerable 等接口的
//而在System.Linq命名空间下的Queryable类中,为IQueryable<TEntity> 这个泛型接口扩展了很多方法,如:Where(),Any(),Count(),OrderBy(),,ThenBy(),Groupby(),Join(),Select(),Find(),First(),Last(),FirstOrDefault(),First(),Find(),Single(),Skip(),Take()等方法。而DbSet<T>这个泛型类又是实现了这个IQueryable<TEntity> 接口的,所以DbSet<T>这个泛型类的对象是可以使用IQueryable<TEntity> 接口中的这些扩展方法的。通过这些泛型方法,我们进行数据的相关查询。
//我们在看看这个DbSet<T>泛型类的定义,我们看到它是继承一个 DbQuery<TEntity>的类。而这个DbQuery<TEntity>类也同样实现了Queryable<TEntity>这个泛型接口,所以DbQuery<TEntity>这个泛型类的对象同样也是可以使用Queryable<TEntity>这个泛型接口中的扩展方法的。
//所以综上所述:DbSet<T>这个泛型类的作用就是:用于提供SQO方法,结合程序员写Lambda表达式进行数据的相关查询
public DbSet<T_User> T_User { get; set; }
public DbSet<T_UserInfo> T_UserInfo { get; set; }
}
}
EF的查询原理
我们要特别留意这个代理类对象中的State属性的值,它的值是一个枚举类型的值,它的值的作用就告诉EF,生成什么类型的SQL语句(如:insert,update,Delete)
当我们调用db.SaveChanges()方法的时候,就会通知EF容器遍历当前EF容器中所有State为非Detached的代理类对象
再根据代理类对象的State的值,生成相应的Sql语句发送给DB执行,例如
当State=EntityState.Added 的时候 ,则生成 Insert 的SQL语句
当State=EntityState.Deleted 的时候 ,则生成 Delete的SQL语句
当State=EntityState.Modified 的时候 ,则生成 Update的SQL语句
当数据从数据库表中刚查询出来的时候State的状态为 State=EntityState.Unchanged 表示没有改动过。
当State=EntityState.Detached 的时候,表示 虽然代理类在EF容器的内部,但是你不需要管我,直接忽略我
我们在来了解一下在EF中,做新增数据的一些注意知识点(获取新增数据的Id)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebAppEF
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
salesEntities db = new salesEntities();
T_UserInfo model=new T_UserInfo(){ Addres="123", Age=25, Name="张三", Gender=1, Email="123@qq.com", Mobile="18620088006", UserName="小张", Remarks="ddd"};
db.T_UserInfo.Add(model);
db.SaveChanges();
Response.Write(model.Id) //这里会打印出插入的那条数据的最新Id
}
}
}
当用户在做这一条数据插入的时候,
当 salesEntities db = new salesEntities();就表示创建了EF容器类的对象。
当写完 T_UserInfo model=new T_UserInfo(){ Addres="123", Age=25, Name="张三", Gender=1, Email="123@qq.com", Mobile="18620088006", UserName="小张", Remarks="ddd"}; 这条语句的时候,这个对象还是在应用程序的内存中,与EF还没有联系,
当执行db.T_UserInfo.Add(model);的时候, 这条model对象已经加入到EF容器类中。
在EF容器类中,将这个对象包装成一个代理类对象 (注意此时这个代理类对象里面有一个State=Addend属性,而刚刚我创建model 这条对象的时候并没有写Id这个属性(因为Id是一个自增值),可是它这里将自己添加了一个Id,并初始值为0)
当我们调用db.SaveChanges();的时候,就会通知EF容器遍历当前EF容器中所有State为非Detached的代理类对象,正好在这里找到一个State=Added的代理类对象,然后就生成了Insert的SQL语句,发给DB执行。
我们同过SQL server Profiler监控工具,发现这条SQL语句是这样的
exec sp_executesql N'insert [dbo].[T_UserInfo]([UserName], [Name], [Age], [Gender], [Mobile], [Email], [Addres], [Remarks])
values (@0, @1, @2, @3, @4, @5, @6, @7)
select [Id]
from [dbo].[T_UserInfo]
where @@ROWCOUNT > 0 and [Id] = scope_identity()',N'@0 nvarchar(50),@1 nvarchar(50),@2 int,@3 int,@4 varchar(50),@5 varchar(50),@6 nvarchar(100),@7 nvarchar(200)',@0=N'小张',@1=N'张三',@2=25,@3=1,@4='18620997006',@5='123@qq.com',@6=N'123',@7=N'ddd'
注意,这条SQL 存储过程执行插入语句,语句中并没有包含代理类中的Id属性的。(因为T_UserInfo表中的Id字段为自增字段,不需要赋值。所以这里不要包含),但是我们插入了一条新的数据后,我们怎么知道插入的那条数据的Id是多少呢?
当DB执行完这条插入语句的存储过程的时候,马上又执行了以下查询语句:
select [Id]
from [dbo].[T_UserInfo]where @@ROWCOUNT > 0 and [Id] = sscope_identity()....
其中scope_identity()表示获取最后一条数据的标识值。 通过这条语句 就将刚刚插入进去的那条数据的Id 就拿到了。然后在通过Ado.net将这个Id交给EF,EF在将这个代理类中的Id的值从0改为从数据库返回的新值。
所以当一条数据成功插入后,我们就可以用model.Id 来得到这条新增的数据的Id值。
注意点
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="salesEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=sales;persist security info=True;user id=sa;password=123456;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
</connectionStrings>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>