2010年1月24日星期日

WCF RIA 服务 (十七)- 数据 7

数据模型中的继承
WCF RIA Services允许我们使用做为继承体系中的一部分的实体。一个继承模型包含了一个从其他数据类派生的数据类。例如,一个多态继承模型可以包含一个Customer实体和两个从Customer派生的实体(PublicSectorCustomer和PrivateSectorCustomer)。通过RIA Services,我们可以在domain Services中写一个返回一个根类型的集合和从根类型派生的类型的查询方法。或者,可以写一个仅返回派生类型集合的查询方法。还可以写修改根类型或任何派生类的操作方法。
注:只在VS2010和SL4中使用RIA Services时支持继承,在VS2008和SL3中不支持。

数据模型
在服务端项目中,我们可以像定义其他数据类那样来为继承模型定义数据类。使用的这个对象模型既可以是从数据层中自动生成的类也可以是手动创建的数据类。
我们不必非要通过domain service来公开整个层次。相反,在domain service中公开的层次中最后派生的类,是与客户端交互的根类。从根类型派生出的类型可以向客户端公开,但父类型不必被公开。在根类中,必须把想要公开的的任意派生类型包含在KnownTypeAttribute属性中。如果想要忽略派生类型,可以不把它包含在KnownTypeAttribute属性中。下面示例了一个包含基类Customer,和两个派生类PrivateSectorCustomer,PublicSectorCustomer的手工创建的数据模型。两个派生类会包含在Customer类的KnownTypeAttribute属性下,因为Customer是数据操作的根类型。

[KnownType(typeof(PublicSectorCustomer)), KnownType(typeof(PrivateSectorCustomer))]
public class Customer
{
[Key]
public int CustomerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string StateProvince { get; set; }
public string PostalCode { get; set; }
[Association("CustomerOrders", "CustomerID", "CustomerID")]
public List Orders { get; set; }
}

public class PublicSectorCustomer : Customer
{
public string GSARegion { get; set; }
}

public class PrivateSectorCustomer : Customer
{
public string CompanyName { get; set; }
}

多态查询
定义了数据模型后,我们创建一个对客户端公开类型的domain service。当在一个查询方法中公开一个类型时,可以返回这个类型和任意派生的类型。例如,一个返回Customer实体集合的查询可以包含一个PrivateSectorCustomer对象和PublicSectorCustomer对象。还可以指定一个只返回一个派生类型的查询方法。下面示例了返回不同类型的查询方法。

public IQueryable GetCustomers()
{
return context.Customers;
}

public IQueryable GetCustomersByState(string state)
{
return context.Customers.Where(c => c.StateProvince == state);
}

public IQueryable GetCustomersByGSARegion(string region)
{
return context.Customers.OfType().Where(c => c.GSARegion == region);
}

public IQueryable GetPrivateSectorByPostalCode(string postalcode)
{
return context.Customers.OfType().Where(c => c.PostalCode == postalcode);
}

为客户端项目生成代码
当生成解决方案时,在客户端会为已经在domain service中公开的继承体系生成代码。体系的根类被生成并派生于Entity类。每个派生的类都是生成和派生于根类。在DomainContext类中,只生成一个Entity(TEntity)成员属性,并且它需要根类型的对象。为每一个查询都生成了EntityQuery对象,它返回在domain service操作中指定的类型。
下面示例了一个在客户端为查询方法生成的简单版本的代码。它并没有包含所有生成类中的代码,只是想强调一些重要的成员属性和方法。

[DataContract(Namespace="http://schemas.datacontract.org/2004/07/ExampleApplication.Web")]
[KnownType(typeof(PrivateSectorCustomer))]
[KnownType(typeof(PublicSectorCustomer))]
public partial class Customer : Entity
{
public string Address { get; set; }
public string City { get; set; }
[Key()]
public int CustomerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PostalCode { get; set; }
public string StateProvince { get; set; }

public override object GetIdentity();

}

[DataContract(Namespace="http://schemas.datacontract.org/2004/07/ExampleApplication.Web")]
public sealed partial class PrivateSectorCustomer : Customer
{
public string CompanyName { get; set; }
}

[DataContract(Namespace="http://schemas.datacontract.org/2004/07/ExampleApplication.Web")]
public sealed partial class PublicSectorCustomer : Customer
{
public string GSARegion { get; set; }
}

public sealed partial class CustomerDomainContext : DomainContext
{
public CustomerDomainContext();
public CustomerDomainContext(Uri serviceUri);
public CustomerDomainContext(DomainClient domainClient);

public EntitySet Customers { get; }

public EntityQuery GetCustomersQuery();
public EntityQuery GetCustomersByGSARegionQuery(string region);
public EntityQuery GetCustomersByStateQuery(string state);
public EntityQuery GetPrivateSectorByPostalCodeQuery(string postalcode);
}

数据修改
可以为继承体系中的更新、插入和删除对象添加domain service方法。如同查询方法,可以为操作指定一个根类型或一个派生类型。然而,任何对派生类的更新、插入或删除操作也都应该可以在根类型中执行。可以在体系中为任何类型添加named updat
方法。会为在方法中指定的类型在客户端生成相应的named update方法。
下面的代码示例了两个更新方法和一个named update方法的签名。

public void UpdateCustomer(Customer customer) { /* implement */ }
public void UpdatePublicSectorCustomer(PublicSectorCustomer customer) { /* implement */ }
public void EnrollInRewardsProgram(PrivateSectorCustomer customer) { /* implement */ }

关联
可以在根类或派生类中定义关联。我们在练歌数据类之间应用AssociationAttribute属性来定义一个关联。在数据模型的例子中,在Customer和Order之间定义了一个关联。如果对根类型应用了关联,那么所有的派生类型也包含这个关联。
使用继承的基本规则


  • 仅对实体类型支持继承,非实体类型被当做在domain service操作签名中指定的类型。
  • 在domain service操作中的返回值和参数,不支持接口类型。
  • 在代码生成的时候必须知道继承体系中的类型集。在生成代码的时候没有指定返回类型的行为是未指明和实现相关的。
  • 允许对实体类型的公共成员属性或字段使用virtual或new。但在客户端对应生成的实体类型中会被忽略。
  • 不允许对domain service操作方法重载。
  • 与继承相关的LINQ查询功能不能用来运行domain service方法。特别地,不支持OfType>T<,is,as和GetType()操作符和方法。然而,在LINQ to Objects查询中的EntitySet或EntityCollection(TEntity)上直接使用这些操作符。

实体继承体系

定义继承体系使用下面的规则:

  • 使用System.Runtime.Serialization.KnownTypeAttribute属性来指定已知的类型。对RIA Services来说,需要使用包含一个System.Type参数并具有KnownTypeAttribute的构造函数。
  • 体系中的已知类型,必须在domain service公开的体系中的根类型中指定。RIA Services 不支持在派生类型上声明类型为已知。
  • 每个在已知类型集中的类都必须是public。
  • 在体系中的一个或多个类可以是abstract的。
  • 当声明已知类型时,可以在体系中省略一个或多个类。
  • 必须在根类中指定关键成员。
  • 关联的声明和使用是不可改变的。

DomainService操作

在继承体系中使用下面规则定义domain service 操作

  • 在体系中至少有一个对应于根类型的查询方法。其他的查询操作可以使用派生类型作为返回值。
  • 对返回多态结果的方法,查询方法可以返回一个根类型。
  • 如果对系统内的类型定义了更新、插入、或删除操作,那么对根类型也要定义相同的操作。不可能只对某些类型选择操作。
  • 自定义操作可以使用根类型或派生类型作为实体参数。当实例的实际类型是从自定义操作中的类型派生的时候,操作是可行的。
  • DomainServiceDescription类返回对给定类型最适用的更新、插入、删除和查询方法。

TpyeDescriptionProvider

下面的规则应用于TypeDescriptionProvider(TDP)

  • 当体系中的根类型通过一个查询方法或IncludeAttribute属性公开时,会对LINQ to SQL应用TpyeDescriptionProvider,并且实体框架会自动对实体使用KnownType属性声明。而对派生类型则不会如此。
  • 在Add New Domain Service Class对话框中不会提供任何继承体系的功能。用户可以在体系中选择一些、全部或不选择类型。

生成的代码

对体系中的实体,以下的规则应用于客户端的生成代码

  • 对每个继承体系,只生成一个EntitySet(TEntity)类。在已知的体系中EntitySet(TEntity)的类型参数是最基本的类型。
  • 对体系中每个已知的类型,都对应生成一个实体类型。即使没有domain service方法公开这个类型,这个实体类型也会生成。
  • 对指定的类型可生成自定义方法,并这些方法可用于任何派生类型。
  • 构造函数是根据继承体系来绑定的。每个类型都可以调用OnCreated方法并可以自定义此方法。
  • 如果服务端的实体体系中的类被跳过,那么在客户端生成的实体体系中也会被跳过。

没有评论:

发表评论