Entity Framework in Action摘要

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的实体是不是已经存在。如果已经存在就不会在此创建新的实体。

publiceventObjectMaterializedEventHandler ObjectMaterialized;

 

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调用之间的事务处理。

Summary Entity Framework Core in Action teaches you how to access and update relational data from .NET applications. Following the crystal-clear explanations, real-world examples, and around 100 diagrams, you'll discover time-saving patterns and best practices for security, performance tuning, and unit testing. Purchase of the print book includes a free eBook in PDF, Kindle, and ePub formats from Manning Publications. About the Technology There's a mismatch in the way OO programs and relational databases represent data. Enti ty Framework is an object-relational mapper (ORM) that bridges this gap, making it radically easier to query and write to databases from a .NET application. EF creates a data model that matches the structure of your OO code so you can query and write to your database using standard LINQ commands. It will even automatically generate the model from your database schema. About the Book Using crystal-clear explanations, real-world examples, and around 100 diagrams, Entity Framework Core in Action teaches you how to access and update relational data from .NET applications. You'l start with a clear breakdown of Entity Framework, long with the mental model behind ORM. Then you'll discover time-saving patterns and best practices for security, performance tuning, and even unit testing. As you go, you'll address common data access challenges and learn how to handle them with Entity Framework. What's Inside Querying a relational database with LINQ Using EF Core in business logic Integrating EF with existing C# applications Applying domain-driven design to EF Core Getting the best performance out of EF Core Covers EF Core 2.0 and 2.1
Entity Framework Core实战,第二版》是一本关于Entity Framework Core的书籍。Entity Framework Core是用于.NET平台的对象关系映射(ORM)工具,它提供了一种简单、高效的方式来访问和操作数据库。 这本书第二版的主要目标是帮助读者更好地理解和使用Entity Framework Core。书中介绍了Entity Framework Core的基本概念和架构,并提供了丰富的实例和案例来演示各种用法和技巧。 书中的内容主要涵盖以下方面: 1. 数据模型的创建和映射:介绍如何使用Entity Framework Core创建数据库表和实体类之间的映射关系。涵盖了使用数据注解和Fluent API两种方式进行映射的方法。 2. 查询和过滤数据:介绍如何使用LINQ查询语言和Entity Framework Core提供的查询操作来从数据库中检索和过滤数据。 3. 数据操作和事务管理:讲解使用Entity Framework Core进行数据的增删改操作,以及如何管理和处理数据库事务。 4. 性能优化和调优:介绍如何使用各种技巧和策略来提高Entity Framework Core的性能,并减少数据库的压力。 5. 并发控制和数据缓存:探讨如何使用Entity Framework Core提供的并发控制机制来保证多线程环境下数据的一致性,并介绍了如何使用缓存机制来提高数据访问的效率。 通过学习《Entity Framework Core实战,第二版》,读者可以掌握Entity Framework Core的核心概念和用法,提高开发效率,优化数据访问性能,并了解如何处理数据库操作中的各种复杂情况。无论是初学者还是有经验的开发者,都可以从这本书中获得实用的知识和技巧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值