Entity Framework in Action摘要

来源:互联网 发布:淘宝客地推海报 编辑:程序博客网 时间:2024/05/16 07:38

Entity Framework inAction摘要

第三章对象模型查询基础

手动创建DbSet类型的实例:

        //    为指定的类型返回System.Data.Entity.DbSet,这将允许对上下文中的给定实体执行 CRUD 操作。

        publicDbSet<TEntity> Set<TEntity>()where TEntity :class;       

        //    为指定的类型返回System.Data.Entity.DbSet,这将允许对上下文中的给定实体执行 CRUD 操作。

        publicDbSet Set(Type entityType);

 

建立连接字符串,下面是一个连接字符串的示例

<add name="ConnStringName"connectionString="

metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;

provider=System.Data.SqlClient;providerconnection string='DataSource=.\sqlexpress;database=EFInactionOrders;IntegratedSecurity=True;MultipleActiveResultSets=True'" providerName="System.Data.EntityClient"/>

 

还可以用EntityConnectionStringBuilder类在运行时建立连接字符串:

varconnStringData = proxy.GetConnectionStringData();

varbuilder = new EntityConnectionStringBuilder();

builder.Provider= connStringData.Provider;

builder.Metadata= connStringData.Metadata;

builder.ProviderConnectionString= connStringData.ProviderConnectionString;

using(var ctx = new OrderITEntities(builder.ConnectionString))

{

...

}

 

ObjectContext有一个ObjectMaterialized事件,你可以用它跟踪Entity的实例化。因为在一个DbContext中,同一个Id的实体,只能有一个。如果后续查询中有相同的数据库行要序列化为实体,EF会先检查这个Id的实体是不是已经存在。如果已经存在就不会在此创建新的实体。

publiceventObjectMaterializedEventHandlerObjectMaterialized;

 

DbContext.Database对象包含了很多操纵数据库的方法和属性,可以实现自动部署数据库。

第四章使用Linq to Entities

使用where方法

fromo in ctx.Orders

whereo.ShippingAddress.City == "New York"

selecto;

 

多个查询条件

o.ShippingAddress.City== "New York" || o.ShippingAddress.City == "Seattle"

 

使用Contains查询(对应SQLin关键字)

var cities = new[]{ "New York", "Seattle" };

from o inctx.Orders

where cities.Contains(o.ShippingAddress.City)

select o;

 

查询关联表

from order inctx.Orders

whereorder.Customer.BillingAddress.City == "New York"

select order;

 

查询关联子表

from order inctx.Orders

whereorder.OrderDetails.Any(d => d.Product.Brand == "MyBrand")

select order;

使用All,查询没有任何打折产品的订单。

from order inctx.Orders

whereorder.OrderDetails.All(d => d.Discount == 0)

select order;

在查询中使用算式

from order inctx.Orders

whereorder.OrderDetails.Sum(d => d.Discount * d.Quantity) > 5

select order;

查询超过包含多个产品的订单

from order in ctx.Orders

whereorder.OrderDetails

.Select(d =>d.Product.ProductId)

.Distinct()

.Count() > 1

select order;

 

分页查询

(from order inctx.Orders

orderbyorder.OrderDate

select order).Take(15);

 

(from o inctx.Orders

orderby o.OrderId

select o).Skip(10).Take(10);

 

获取一个Entity

(from order inctx.Orders

where order.OrderId== 1

selectorder).First();

(from order inctx.Orders

where order.OrderId== 1

select order).Single();

最好使用First,它比Single有更好的性能。该查询不能在VS中监测SQL语句。只能用SQL Profiler来监视。

还可以使用以下方法达到上述两个查询的相同的效果:

        //摘要:

        //    使用主键值尝试查找上下文跟踪的实体。如果该实体未在上下文中,则将针对数据源中的数据执行和计算查询;如果未在上下文或数据源中找到该实体,则将返回 null。请注意,Find

        //    还会返回已添加到上下文但尚未保存到数据库中的实体。

        //

        //参数:

        //  keyValues:

        //    要查找的实体的主键值。

        //

        //返回结果:

        //    返回 System.Boolean

        public TEntity Find(paramsobject[] keyValues);

 

动态创建查询

var date =DateTime.Today;

string city = null;

var result =ctx.Orders.AsQueryable();

if (date !=DateTime.MinValue)

result = result.Where(o => o.OrderDate < date);

if(String.IsNullOrEmpty(city))

result = result.Where(o => o.ShippingAddress.City ==city);

 

投影结果集,下面的LINQ只会生成查询所需列的SQL,因此提高了性能。

var result = from oin ctx.Orders

select new { o.OrderId,o.OrderDate, o.ShippingAddress };

组合新属性

from o in ctx.Orders

select new {

o.OrderId,

o.OrderDate,

ShippingAddress =String.Format("{0}-{1}-{2}-{3}",

o.ShippingAddress.Address,

o.ShippingAddress.City,

o.ShippingAddress.ZipCode

o.ShippingAddress.Country)

};

使用嵌套匿名类型:

from o in ctx.Orders

select new {

o.OrderId,

o.OrderDate,

Shipping = new

{

o.ShippingAddress.City,

o.ShippingAddress.Address

}

};

 

在投影中包含关系

from o in ctx.Orders

select new { o.OrderId,o.OrderDate, o.ShippingAddress, o.Customer };

 

投影集合关系

from o in ctx.Orders

select new { o.OrderId, o.OrderDate,o.ShippingAddress, o.OrderDetails };

或者从原始集合创建新的集合

from o in ctx.Orders

select new

{

o.OrderId,

o.OrderDate,

o.ShippingAddress,

Details = from d in o.OrderDetails

select new

{

d.OrderDetailId, d.Product.ProductId, d.Quantity

}

};

还可以对集合进行聚合操作:

from o in ctx.Orders

select new

{

o.OrderId,

o.OrderDate,

o.ShippingAddress,

Total = o.OrderDetails.Sum(d => d.Quantity *(d.UnitPrice - d.Discount))

};

 

投影和对象跟踪

不能创建一个Entity并且只赋值部分属性。如果在查询中创建一个Entity并且只赋值部分属性,那么运行时会抛出异常。这是因为只有部分属性的Entity无法正确检测数据变化。

 

分组数据

from c in ctx.Orders

group c byc.ShippingAddress.City;

从分组数据创建新的投影:

var result = from cin ctx.Orders

group c byc.ShippingAddress.City into oGroup

select new { CityName =oGroup.Key, Items = oGroup };

或者:

from o in ctx.Orders

group o by o.ShippingAddress.Cityinto g

select new

{

g.Key,

Items = g.Select(og => new { og.OrderId, og.OrderDate})

};

使用多个属性进行分组:

varresult = from o in ctx.Orders

group oby new

{

o.ShippingAddress.City,o.ShippingAddress.ZipCode

};

foreach(var key in result)

{

Console.WriteLine(key.Key.City +"-" + key.Key.ZipCode);

foreach (var item in key)

Console.WriteLine(item.OrderId);

}

 

查询聚合数据

from o in ctx.Orders

group o byo.ShippingAddress.City into g

where g.Count() >2

select g;

 

排序

升序排序

from o in ctx.Orders

orderby o.ShippingAddress.City

select o;

降序排序

from o in ctx.Orders

orderbyo.ShippingAddress.City, o.ShippingAddress.ZipCode descending

select o;

按照关联数据排序

from o in ctx.Orders

orderbyo.OrderDetails.Sum(d => d.Quantity * (d.UnitPrice - d.Discount))

select new

{

o.OrderId,

o.OrderDate,

o.ShippingAddress,

Total = o.OrderDetails.Sum(

d => d.Quantity * (d.UnitPrice - d.Discount))

};

或者

from o in ctx.Orders

orderbyo.Customer.ShippingAddress.City

select o;

 

对关联集合排序:

from o in ctx.Orders

select new

{

o.OrderId,

o.ShippingAddress.City,

Details = o.OrderDetails.OrderBy(d => d.Quantity)

};

 

连接数据

from oin ctx.Orders

join cin ctx.Companies

ono.ShippingAddress.City equals c.ShippingAddress.City

selecto;

from oin ctx.Orders

根据多个属性连接数据

join cin ctx.Companies.OfType<Customer>()

on new {o.ShippingAddress.City, o.Customer.CompanyId }

equalsnew { c.ShippingAddress.City, c.CompanyId }

select o;

实现外连接

from o in ctx.Orders

join c inctx.Companies.OfType<Customer>()

on new {o.ShippingAddress.City, o.Customer.CompanyId }

equals new {c.ShippingAddress.City, c.CompanyId }

into g

from item ing.DefaultIfEmpty()

select o;

 

查询具有继承关系的数据

IEnumerable<Product>products = from p in ctx.Products

where p is Shoe

select p;

或者

IEnumerable<Shoe>shoes = from p in ctx.Products.OfType<Shoe>()

select p;

 

使用函数

标准函数

下面的查询会出错:

from o in ctx.Orders

whereo.OrderDate.AddDays(5) < o.ActualShippingDate

select o;

需要使用下面的方法:

from o in ctx.Orders

whereEntityFunctions.DiffDays(o.OrderDate, o.ActualShippingDate) > 5

select o;

 

数据库函数,但是这些函数只能用于SQL数据库,所以下面的代码只能运行于SQL Server之上

from o in ctx.Orders

whereSqlFunctions.DateDiff("d", o.OrderDate, o.ActualShippingDate) > 5

select o;

 

执行SQL语句

var details =ctx.ExecuteStoreQuery<OrderDetail>

("Select * fromOrderDetail");

需要注意的是,映射过程不使用EMD映射文件,而直接使用列名映射。所以具有复杂类型的属性无法在这里映射。因为直接使用列名映射,所以可以使用任何类型作为泛型参数。

传递参数

传递简单值参数,下面的方法不会导致SQL注入攻击,因为EF内部还是使用了具体的IDbParameter类型。

varnames = ctx.ExecuteStoreQuery<string>

 ("SELECT name FROM company  WHERE shippingcity = {0}and billingcity = {1}",

"NewYork", "Seattle");

传递SqlParameter

var p0 =new SqlParameter("p0", DbType.String)

{ Value= "New York" };

var p1 =new SqlParameter("p1", DbType.String)

{ Value= "Seattle" };

varnames = ctx.ExecuteStoreQuery<string>

("SELECTname FROM company

WHERE shippingcity ={0}

and billingcity ={1}", p0, p1);

使用命名参数(SQL server使用“@”,OLEDB使用“?”,Oracle使用“:”):

var names =ctx.ExecuteStoreQuery<string>

("SELECT nameFROM company WHERE shippingcity = @p0 and billingcity = @p1", "NewYork", "Seattle");

 

var p0 = newSqlParameter("p0", DbType.String) { Value = "New York" };

var p1 = newSqlParameter("p1", DbType.String) { Value = "Seattle" };

var names =ctx.ExecuteStoreQuery<string>

("SELECT nameFROM company WHERE shippingcity = @p0 and billingcity = @p1", p0, p1);

 

预先加载

包含单个引用

query.Include(e => e.Level1Reference).

包含单个集合

query.Include(e => e.Level1Collection).

包含单个引用,且此引用又引用下一级引用

query.Include(e =>e.Level1Reference.Level2Reference).

包含单个引用,且此引用又包含下一级集合

query.Include(e =>e.Level1Reference.Level2Collection).

包含单个集合,且该集合又包含下一级引用

query.Include(e => e.Level1Collection.Select(l1=> l1.Level2Reference)).

包含单个集合,且该集合又引用下一级集合

query.Include(e => e.Level1Collection.Select(l1=> l1.Level2Collection)).

更多级引用:

query.Include(e => e.Level1Collection.Select(l1=> l1.Level2Reference.Level3Reference)).

query.Include(e => e.Level1Collection.Select(l1=> l1.Level2Collection.Select(l2 => l2.Level3Reference))).

所有上面的预加载语法,都可以用字符串路径代替,如:

container.Users.Include("Roles.Tasks.Operations")

Lazy Loading不需要使用上述语法,当你在访问某个导航属性的时候,数据库访问会自动发生,为你填充该属性。但前提是你打开了LazyLoading选项,而且你的Entity对象是一个代理对象;否则什么都不会发生。

设置LazyLoading选项和启动代理选项(次两个选项默认为true),如下:

publicclassDbContextConfiguration

{

        publicbool LazyLoadingEnabled {get;set; }

        publicbool ProxyCreationEnabled {get;set; }

}

第五章领域模型映射

CSDL:

Schema元素用是CSDL文件的根元素:

<Schemaxmlns="http://schemas.microsoft.com/ado/2008/09/edm"

xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"Namespace="OrderITModel" Alias="Self">

...

</Schema>

EntityContainer元素定义了实体集和关系集,上下文类的代码由它来生成。

<EntityContainerName="OrderITEntities">

<EntitySetName="Orders" EntityType="OrderIT.DomainModel.Order" />

<EntitySetName="OrderDetails" EntityType="OrderITModel.OrderDetail"/>

</EntityContainer>

 

ComplexType元素定义负责类型,其内部为Property元素,用来定义一个属性:

 

元素属性

描述

是否必须

Name

属性名

Type

属性类型(全名称)

Nullable

是否可空

FixedLength

是否固定长度

MaxLength

最大长度

Scale

小数位数(针对Decimal)

Precision

精度,数字位数(针对Decimal)

store:StoreGeneratedPattern

表示数据库列的值在插入和更新的时候如何被设置,有三种可选值:

None:表示使用来自应用程序的值。

Identity:值由数据库产生,应用程序用它来更新。

Computed:该值由数据库计算产生

ConcurrencyMode

并发模式,为了启用并发检查,设置该值为Fixed

 

 

Entity元素定义实体类

<ComplexTypeName="AddressInfo">

<PropertyType="String" Name="Address" Nullable="false"MaxLength="50" />

<PropertyType="String" Name="City" Nullable="false" MaxLength="50"/>

<PropertyType="String" Name="ZipCode" Nullable="false"MaxLength="15" />

<PropertyType="String" Name="Country" Nullable="false"MaxLength="30" />

</ComplexType>

<EntityTypeName="Order">

<Key>

<PropertyRefName="OrderId" />

</Key>

<PropertyType="Int32" Name="OrderId" Nullable="false"store:StoreGeneratedPattern="Identity" />

<PropertyName="ShippingAddress" Type="OrderITModel.AddressInfo"Nullable="false" />

<PropertyType="DateTime" Name="EstimatedShippingDate"Nullable="false" Precision="29" />

<PropertyType="DateTime" Name="ActualShippingDate"Nullable="false" Precision="29" />

</EntityType>

<EntityTypeName="OrderDetail">

<Key>

<PropertyRefName=" OrderDetail Id" />

</Key>

<PropertyType="Int32" Name="OrderDetailId"Nullable="false" store:StoreGeneratedPattern="Identity"/>

<PropertyType="Int16" Name="Quantity" Nullable="false"/>

<PropertyType="Decimal" Name="UnitPrice" Nullable="false"Precision="29" Scale="29" />

<PropertyType="Decimal" Name="Discount" Nullable="false"Precision="29" Scale="29" />

</EntityType>

EntityType有两个属性:Abstract用来制定该类是不是一个抽象类,BaseType用来指定一个基类型。

SSDL:

Schema元素定义类根节点:

<SchemaNamespace="OrderITModel.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/2009/02/edm/ssdl">

...

</Schema>

ProviderManifestToken指定了数据库的版本,对于SQLServer,它可以是2000,2005,2008等。

EntityContainer元素声明了所有数据库对象。

<EntityContainerName="OrderITModelStoreContainer">

<EntitySetName="Order" EntityType="OrderITModel.Store.Order"store:Type="Tables" Schema="dbo" />

<EntitySetName="OrderDetail"EntityType="OrderITModel.Store.OrderDetail"store:Type="Tables" Schema="dbo" />

</EntityContainer>

Table属性定义了表的名称,如果没有指定该属性,那么默认使用Name属性作为表名称。

store:Type表示该对象是表还是视图。

 

EntityType定义了数据库对象:

<EntityTypeName="Order">

<Key>

<PropertyRefName="OrderId" />

</Key>

<PropertyName="OrderId" Type="int"StoreGeneratedPattern="Identity" Nullable="false" />

<Property Name="ShippingAddress"Type="nvarchar" Nullable="false" MaxLength="50"/>

<PropertyName="ShippingCity" Type="nvarchar"Nullable="false" MaxLength="50" />

<PropertyName="ShippingZipCode" Type="nvarchar"Nullable="false" MaxLength="15" />

<Property Name="ShippingCountry"Type="nvarchar" Nullable="false" MaxLength="30"/>

<PropertyName="EstimatedShippingDate" Type="datetime"Nullable="false"/>

<PropertyName="ActualShippingDate" Type="datetime"Nullable="false" />

<PropertyName="CustomerId" Type="int" Nullable="false"/>

</EntityType>

Name属性必须与EntitySet的Name属性相同。

Property元素拥有和CSDL中的Property一样的属性,但是还有以下三个额外的属性:

Collation—指定了列的Collation

DefaultValue—设置列的默认值

Unicode—指定值是不是Unicode

MSL:

Mapping元素定义了根节点

<MappingSpace="C-S" xmlns="http://schemas.microsoft.com/ado/2008/09/mapping/cs">

<EntityContainerMappingCdmEntityContainer="OrderITEntities"StorageEntityContainer="OrderITModelStoreContainer">

...

</EntityContainerMapping>

</Mapping>

EntitySetMapping,EntityTypeMapping和MappingFragment定义类映射的详细信息:

<EntitySetMappingName="Orders">

<EntityTypeMappingTypeName="IsTypeOf(OrderITModel.Order)">

<MappingFragmentStoreEntitySet="Order">

<ScalarPropertyName="Id" ColumnName="Id" />

<ComplexPropertyName="ShippingAddress"TypeName="OrderITModel.AddressInfo">

<ScalarPropertyName="Address" ColumnName="ShippingAddress" />

<ScalarPropertyName="City" ColumnName="ShippingCity" />

<ScalarPropertyName="ZipCode" ColumnName="ShippingZipCode" />

<ScalarPropertyName="Country" ColumnName="ShippingCountry" />

</ComplexProperty>

<ScalarPropertyName="EstimatedShippingDate"ColumnName="EstimatedShippingDate" />

<ScalarPropertyName="ActualShippingDate" ColumnName="ActualShippingDate"/>

</MappingFragment>

</EntityTypeMapping>

</EntitySetMapping>

 

添加一对多关系

NavigationProperty元素定义了导航属性。

<EntityTypeName="OrderDetail">

...

<NavigationPropertyName="Order" Relationship="OrderIT.OrderOrderDetail"FromRole="OrderDetail" ToRole="Order" />

<ScalarPropertyName="OrderId" ColumnName="OrderId" />

</EntityType>

Relationship属性需要指定Association的全名称,而不是AssociationSet的全名称。

 

AssociationSet元素声明了一个关系,该元素包含在EntityContainer中。

<EntityContainer...>

<AssociationSetName="OrderOrderDetail"Association="OrderITModel.OrderOrderDetail">

<EndRole="Order" EntitySet="Orders">

<OnDeleteAction="Cascade" />

</End>

<End Role="OrderDetail"EntitySet="OrderDetails" />

</AssociationSet>

</EntityContainer>

<AssociationName="OrderOrderDetail">

<EndRole="Order" Type="OrderITModel.Order"Multiplicity="1" />

<EndRole="OrderDetail" Type="OrderITModel.OrderDetail"Multiplicity="*" />

</Association>

Association的Name属性和AssociationSet的Association属性需要保持一致(引用一个关系或者实体的时候需要使用全名称)。AssociationSet的Name属性到目前为止,还没看出有什么用。

End元素中可以指定级联删除操作。这个级联删除跟数据库的级联删除没有任何关系。在此处,如果指定了级联删除,那么当你删除一个Order的时候,所有该Order的OrderDetail都会被设置删除状态。

具有外键属性的关系在CSDL中映射,只有导航属性的关系在MSL中映射,我们只描述前者:

<AssociationName="OrderOrderDetail">

...

<ReferentialConstraint>

<PrincipalRole="Order">

<PropertyRefName="OrderId" />

</Principal>

<DependentRole="OrderDetail">

<PropertyRefName="OrderId" />

</Dependent>

</ReferentialConstraint>

</Association>

有时候,为了性能考虑,也许会在数据库里删除外键关联;所以SSDL里面不会有关于关系的描述。但是CSDL里的关系描述仍然有效。不过下面我们会讲数据库里定义了外键的情况,所以SSDL需要添加此关系定义。

<EntityContainer...>

<AssociationSetName="FK_OrderDetail_Order" Association="OrderITModel.Store.FK_OrderDetail_Order">

<End Role="Order"EntitySet="Order" />

<EndRole="OrderDetail" EntitySet="OrderDetail" />

</AssociationSet>

</EntityContainer>

<AssociationName="FK_OrderDetail_Order">

<EndRole="Order" Type="OrderITModel.Store.Order"Multiplicity="1">

<OnDeleteAction="Cascade" />

</End>

<EndRole="OrderDetail" Type="OrderITModel.Store.OrderDetail"Multiplicity="*"/>

<ReferentialConstraint>

<PrincipalRole="Order">

<PropertyRefName="OrderId" />

</Principal>

<DependentRole="OrderDetail">

<PropertyRefName="OrderId" />

</Dependent>

</ReferentialConstraint>

</Association>

最后一步是修改MSL,完成对外键属性的映射:

<EntitySetMappingName="OrderDetails">

<EntityTypeMappingTypeName="IsTypeOf(OrderITModel.OrderDetail)">

<MappingFragmentStoreEntitySet="OrderDetail">

...

<ScalarPropertyName="OrderId" ColumnName="OrderId" />

</MappingFragment>

</EntityTypeMapping>

</EntitySetMapping>

多对多的关系与1对多关系基本相同,不同的是在MSL里面需要用AssociationSetMapping元素来描述映射:

<AssociationSetMappingName="ProductsSuppliers"TypeName="OrderItModel.ProductsSuppliers" StoreEntitySet="ProductSupplier">

<EndPropertyName="Suppliers">

<ScalarProperty Name="CompanyId"ColumnName="SupplierId" />

</EndProperty>

<EndPropertyName="Products">

<ScalarProperty Name="ProductId"ColumnName="ProductId" />

</EndProperty>

</AssociationSetMapping>

使用模型描述TPH

注意Abstract属性和BaseType属性的使用。

<EntityContainer...>

<EntitySetName="Companies" EntityType="OrderIT.Domain.Company" />

</EntityContainer>

<EntityTypeName="Company" Abstract="true">

<Key>

<PropertyRefName="CompanyId" />

</Key>

<PropertyType="Int32" Name="CompanyId" Nullable="false"store:StoreGeneratedPattern="Identity" />

<PropertyType="String" Name="Name" Nullable="false"MaxLength="50" />

</EntityType>

<EntityTypeName="Customer" BaseType="OrderITModel.Company" >

<Property Name="BillingAddress"Type="OrderITModel.AddressInfo" Nullable="false" />

<PropertyName="ShippingAddress" Type="OrderITModel.AddressInfo"Nullable="false" />

<PropertyType="String" Name="WSUsername" Nullable="true"MaxLength="20" />

<PropertyType="String" Name="WSPassword" Nullable="true"/>

<PropertyType="String" Name="WSEnabled" Nullable="false"/>

</EntityType>

<EntityTypeName="Supplier" BaseType="OrderITModel.Company" >

<PropertyType="String" Name="IBAN" Nullable="false"FixedLength="true" MaxLength="26" />

<PropertyType="Int16" Name="PaymentDays" Nullable="false"/>

</EntityType>

在MSL中映射TPH

<EntitySetMappingName="Companies">

<EntityTypeMappingTypeName="IsTypeOf(OrderITModel.Company)">

<MappingFragmentStoreEntitySet="Company">

<ScalarPropertyName="CompanyId" ColumnName="CompanyId" />

<ScalarPropertyName="Name" ColumnName="Name" />

</MappingFragment>

</EntityTypeMapping>

<EntityTypeMappingTypeName="IsTypeOf(OrderITModel.Customer)">

<MappingFragmentStoreEntitySet="Company">

<ConditionColumnName="Type" Value="C" />

<ScalarPropertyName="CompanyId" ColumnName="CompanyId" />

<ScalarPropertyName="WSUsername" ColumnName="WSUsername" />

<ScalarPropertyName="WSPassword" ColumnName="WSPassword" />

...

</MappingFragment>

</EntityTypeMapping>

<EntityTypeMappingTypeName="IsTypeOf(OrderITModel.Supplier)">

<MappingFragmentStoreEntitySet="Company">

<ConditionColumnName="Type" Value="S" />

<ScalarPropertyName="CompanyId" ColumnName="CompanyId" />

...

</MappingFragment>

</EntityTypeMapping>

<EntitySetMappingName="Companies">

EntitySetMapping的Name属性需要对应到CSDL中的EntitySet的Name属性。可以看到MSL中在一个EntitySetMapping中使用多个EntityTypeMapping来达到TPH的映射关系。

在MSL中映射TPT

<EntitySetMappingName="Products">

<EntityTypeMappingTypeName="IsTypeOf(OrderITModel.Product)">

<MappingFragmentStoreEntitySet="Product">

...

</MappingFragment>

</EntityTypeMapping>

<EntityTypeMappingTypeName="IsTypeOf(OrderITModel.Shirt)">

<MappingFragmentStoreEntitySet="Shirt">

...

</MappingFragment>

</EntityTypeMapping>

<EntityTypeMapping TypeName="IsTypeOf(OrderITModel.Shoes)">

<MappingFragmentStoreEntitySet="Shoe">

...

</MappingFragment>

</EntityTypeMapping>

</EntitySetMapping>

在每个MappingFragment中只映射Entity自己对应的属性值。其中当然会包括主键映射。

第六章理解对象生命周期

下面的枚举表示了Entity的所有状态:

    [Flags]

    publicenumEntityState

    {       

        Detached = 1,       

        Unchanged = 2,

        Added = 4,

        Deleted = 8,

        Modified = 16,

    }

 

l  添加一个Entity到实体集中,对象的状态为Added

通过DbContext.Set方法获取一个DbSet或者DbSet<T>的的实例。

publicDbSet<TEntity>Set<TEntity>()where TEntity :class;

publicDbSet Set(TypeentityType);

然后调用DbSet.Add方法来添加新的实体。

public TEntityAdd(TEntity entity);

 

l   将一个Entity附加到DbContext中,对象的状态为Unchanged

public TEntityAttach(TEntity entity);

当附加一个Entity的时候,Entity的主键必须被设置,否则会抛出InvalidOperationException如果该对该Entity的更新没有影响任何数据库行,也会抛出异常。

如果要保存一个附加的Entity,需要通知DbContextEntity已经被更改,否则DbContext不会有任何对该Entity的操作。通知DbContext Entity已更改有三种方法。

1.  第一种方法

a)  调用DbSet.Attach方法把Entity对象附加到DbContext中。

b)  获取当前Entity对应的DbEntityEntry对象,使用如下方法:

   publicDbEntityEntry Entry(object entity);

publicDbEntityEntry<TEntity>Entry<TEntity>(TEntity entity)where TEntity :class;

c)  然后手动设置DbEntityEntry.State

DbEntityEntry.State = EntityState.Modified;

事实上,不Attach而直接修改DbEntityEntry对象的状态也是可以的。EF会在内部检测到该对象没有对应的DbEntityEntry而自动创建一个并把对象AttachDbContext里。

2.  第二种方法

a)  将当前Entity附加到DbContext中。

b)  从数据库中加载当前Entity的原始值。

publicDbPropertyValuesGetDatabaseValues();

c)  将原始值设置到当前Entity对应的DbEntityEntry里。

DbEntityEntry.OriginalValues.SetValues(…)

3.  第三种方法

a)  假设当前Entity对应的原始Entity已经在DbContext中,那么可以直接对原始Entity对应的DbEntityEntry对象应用当前更改。

DbEntityEntry.CurrentValues.SetValues(…)

所有以上方法都只检测标量属性,忽略导航属性。

 

l   删除一个Entity,该Entity状态为Deleted

删除一个Entity前,该Entity必须存在于DbContext中,否则将会引发异常。在保持更改之前,该Entity的状态为Deleted,之后,该Entity将从DbContext中移除。调用以下方法删除一个Entity

public TEntityRemove(TEntity entity);

 

l   调用Detach方法,将一个对象从DbContext中分离

EF5.0DbSet类中没有对应于EF4.0里的Detach方法,原因不明。

 

EF5.0中不能对多对多关系应用级联删除,除非把所有相关联的对象预先记载道导航属性里,基于性能考虑,这是不可接受的。所以级联删除只能在数据库里设置。这样EF5.0在删除一个Entity的时候,相关联的Entity或者关系都会被数据库删除,但是这样做的后果是,EF5.0不知道数据库已经删除了相关联的Entity或者关系,会导致DbContext中缓存的对象和数据库里的数据不一致。

 

第七章把对象持久化到数据库中

下面的方法将数据更改保存到数据库中:

publicvirtualint SaveChanges();

调用该方法后,添加和修改的Entity状态变为Unchanged,删除状态的Entity将会被从DbContext中移除。整个保存过程在一个事物中执行用以保持数据完整性。默认情况下:DbContext.Configuration.AutoDetectChangesEnabled的值为true,表明DbContext将会自动检测Entity变化。这个选项最好被关闭,因为它会导致DbContext遍历所有缓存的Entity,比较他们的原始值和当前值,因此这是一个非常耗费性能的操作。关闭此选项后,需要用代码显式告诉DbContext Entity的变化(只针对Modified的情况,因为新Entity和删除的Entity都需要用EF框架里的方法来实现)。

 

l  添加Entity

public TEntity Add(TEntityentity);

使用上面的方法将一个新的Entity添加到EntitySet中,然后调用SaveChanges方法。

l  修改Entity

上一章中已经讲了如何修改Entity的方法。这里需要注意的一点是,如果是显式通知DbContext关于Entity的变化,那么要注意有可能数据库数据有可能会丢失。例如:你修改了User对象的Name属性,然后去更新此User,那么User的另一个属性Description同样会被更新到数据库中,因为Description的值为null,所以数据库中的值会被更新为null

对于上述情况,要么打开自动检测变化,要么把Entity的所有属性都设置为有效值。另外复杂类型只能整体更新,不可能只把负责类型的一个属性更新到数据库。

为了方便对外键的更新,最好在Entity中同时包含外键字段,而不是只包含导航属性。只包含导航属性令更新外键变得复杂。

l  删除Entity

使用如下方法删除一个Entity并调用SaveChanges方法。被删除的Entity必须存在于DbContext中。

public TEntityRemove(TEntity entity);

如果要在删除主表条目的时候同时删除对应子表记录,最好使用数据库的级联删除功能,或者在代码中使用Entity SQL,否则EF会对每个子表记录产生一条删除语句,影响性能。

第八章处理并发和事务

如果要支持并发检查,首先要为需要被检查的表添加一个Version列,该列的类型为Timestamp,EDM设计窗口里设置对应属性的并发检查模式为Fixed

EF始终使用原始值里的Version值来做检查,所以一定要确保Version的原始值是你获取的时候值,在某些时候,可以手动修改这个值,以达到并发检查的效果。

在主/子表关系的并发检查里,如果只对主表应用了并发检查,而且修改了子表而没有对主表修改,那么并发检查不会发生。如果强制修改了主Entity的状态(Modified),并发坚持会发生,但是这么做会影响性能。

在继承关系的数据库结构里(TPT),只能在公共表上添加version列。否则会引发异常。

如果并发检查失败,OptimisticConcurrencyException会被抛出。

 

管理事务

使用TransactionScope类实现多次SaveChanges调用之间的事务处理。

原创粉丝点击