Windows Communication Foundation Unleashed (WCF) 摘录





2.00/5 (5投票s)
2007年9月10日
12分钟阅读

24684
本文介绍了 .NET 2.0 Framework 的部分类型、泛型、可空值类型、轻量级事务管理器和角色提供程序等功能,以帮助您更好地理解如何使用 Windows Presentation Foundation。
第一章:先决条件
作者:Craig McMurtry, Marc Mercuri, Nigel Watling, and Matt Winkler
本章内容
- 引言
- 部分类型
- Generics
- 可空值类型
- 轻量级事务管理器
- 角色提供程序
- 参考文献
引言
为了能够正确理解和有效地使用 Windows Communication Foundation,读者应该熟悉 .NET Framework 2.0 版本和 .NET 公共语言运行时的一些功能。本章将介绍它们:部分类型、泛型、可空值类型、轻量级事务管理器和角色提供程序。这些功能的介绍并非旨在详尽,而仅仅是为澄清其在后续章节中的用法而足够。
部分类型
Microsoft Visual C# 2005 允许通过多个部分定义来组成一个类型的定义,这些定义分布在同一个模块的任意数量的源代码文件中。这个选项是通过修饰符 partial
提供的,它可以添加到类、接口或结构体的定义中。因此,类的定义中的这部分
public partial MyClass
{
private string myField = null;
public string MyProperty
{
get
{
return this.myField;
}
}
}
以及这另一部分
public partial MyClass
{
public MyClass()
{
}
public void MyMethod()
{
this.myField = "Modified by my method.";
}
}
可以共同构成 MyClass
类型的定义。此示例仅说明了部分类型的一个用途,即在单独的源代码文件中组织类的行为及其数据。
Generics
“泛型是类、结构体、接口和方法,它们具有占位符,用于表示它们存储或使用的一个或多个类型”(Microsoft 2006)。下面是 .NET Framework 2.0 类库 System.Collections.Generic
命名空间中引入的泛型类的一个示例
public class List<T>
该类的方法中包含这个方法
public Add(T item)
在此,T 是类型占位符,它将被泛型类 System.Collections.Generic.List<
T>
的实例存储。在定义泛型类的实例时,需要指定实例实际存储的类型
List<string> myListOfStrings = new List<string>();
然后就可以像这样使用泛型类实例的 Add()
方法了
myListOfStrings.Add("Hello, World");
显然,泛型使得 List<
T>
类的设计者能够定义一个同类型未指定实例的集合;换句话说,提供了一个类型安全集合的模板。List<
T>
的用户可以使用它来包含用户选择的任何类型的实例,而无需 List<
T>
的设计者知道用户可能选择的类型。还要注意,虽然派生自基类型的类型旨在从基类型派生其所需的部分功能,而其余部分仍需自行编程,但 List<
string>
完全继承自 List<
T>
。
类 System.Collections.Generic.List<
T>
被称为泛型类型定义。占位符 T 被称为泛型类型参数。声明
List<string> myListOfStrings;
得到 System.Collections.Generic.List<
string>
作为构造类型,以及 string 作为泛型类型参数。
泛型可以拥有任意数量的泛型类型参数。例如,System.Collections.Generic.Dictionary<
TKey,
TValue>
有两个。
泛型设计者可以使用约束来限制可以作为泛型类型参数使用的类型。这个泛型类型定义
public class MyGenericType<T> where T: new(), IComparable
将泛型类型参数限制为具有实现 IComparable
接口的公共无参数构造函数的类型。这个限制性较小的泛型类型定义
public class MyGenericType<T> where T: class
仅将泛型类型参数限制为引用类型。
泛型和非泛型类型都可以拥有泛型方法。下面是一个非泛型类型带泛型方法的示例
using System;
public class Printer
{
public void Print<T>(T argument)
{
Console.WriteLine(argument.ToString());
}
static void Main(string[] arguments)
{
Printer printer = new Printer();
printer.Print<string>("Hello, World");
Console.WriteLine("Done");
Console.ReadKey();
}
}
在编程泛型时,通常需要确定已为泛型类型参数替换的泛型参数类型。对前面示例的此修订显示了如何进行这种确定
public class Printer
{
public void Print<T>(T argument)
{
if(typeof(T) == typeof(string))
{
Console.WriteLine(argument);
}
else
{
Console.WriteLine(argument.ToString());
}
}
static void Main(string[] arguments)
{
Printer printer = new Printer();
printer.Print<string>("Hello, World");
Console.WriteLine("Done");
Console.ReadKey();
}
}
泛型接口可以由泛型类型或非泛型类型实现。此外,泛型和非泛型类型都可以继承自泛型基类型。
public interface IMyGenericInterface<T>
{
void MyMethod<T>();
}
public class MyGenericImplementation<T>: IMyGenericInterface<T>
{
public void MyMethod<T>()
{
}
}
public class MyGenericDescendant<T> : MyGenericImplementation<T>
{
}
public class MyNonGenericImplementation : IMyGenericInterface<string>
{
public void MyMethod<T>()
{
}
}
public class MyNonGenericDescendant : MyGenericImplementation<string>
{
}
可空值类型
根据 Common Language Infrastructure 规范,.NET 中有两种表示数据的方式:值类型或引用类型(Ecma 2006, 18)。虽然值类型的实例通常分配在线程堆栈上,但引用类型的实例从托管堆分配,其值是已分配内存的地址(Richter 2002, 134–5)。
引用类型变量的默认值是 null
,表示尚未为其分配任何已分配内存的地址,而值类型变量始终具有所讨论类型的某个值,并且永远不能具有 null
值。因此,虽然可以通过检查引用类型的值是否为 null
来确定它是否已初始化,但不能对值类型执行相同的操作。
然而,在两种常见情况下,您会希望知道是否已为值类型实例分配了值。第一种情况是当实例代表数据库中的一个值时。在这种情况下,您会希望能够检查实例以确定数据库中是否确实存在一个值。另一种情况(与本书的主题更相关)是当实例代表从某个远程源接收的数据项时。同样,您会希望从实例中确定是否接收了该数据项的值。
.NET Framework 2.0 包含一个泛型类型定义,用于处理您想将 null
分配给值类型实例并测试该实例的值是否为 null
的情况。该泛型类型定义是 System.Nullable<
T>
,它将可用于 T 的泛型类型参数限制为值类型。从 System.Nullable<
T>
构造的类型的实例可以被赋值为 null
;事实上,它们的默认值就是 null
。因此,从 System.Nullable<
T>
构造的类型可以称为可空值类型。
System.Nullable<
T>
有一个属性 Value
,如果实例的值不是 null
,则可以通过它获取分配给从它构造的类型实例的值。因此,您可以编写
System.Nullable<int> myNullableInteger = null;
myNullableInteger = 1;
if (myNullableInteger != null)
{
Console.WriteLine(myNullableInteger.Value);
}
C# 编程语言为声明从 System.Nullable<
T>
构造的类型提供了简写语法。该语法允许您将以下内容缩写为
System.Nullable<int> myNullableInteger;
to
int? myNullableInteger;
编译器将阻止您尝试以这种方式将可空值类型的值分配给普通值类型
int? myNullableInteger = null;
int myInteger = myNullableInteger;
它阻止您这样做是因为可空值类型可能具有 null
值(在这种情况下它确实具有),而该值不能分配给普通值类型。尽管编译器允许此代码,
int? myNullableInteger = null;
int myInteger = myNullableInteger.Value;
但第二条语句将导致抛出异常,因为任何试图访问 System.Nullable<
T>.Value
属性的操作都是无效的,如果从 System.Nullable<
T>
构造的类型未被分配一个有效的 T 值(在这种情况下尚未发生)。
将可空值类型的值分配给普通值类型的一个正确方法是使用 System.Nullable<
T>.HasValue
属性来确定是否已为可空值类型分配了一个有效的 T 值
int? myNullableInteger = null;
if (myNullableInteger.HasValue)
{
int myInteger = myNullableInteger.Value;
}
另一个选项是使用此语法
int? myNullableInteger = null;
int myInteger = myNullableInteger ?? -1;
通过它,如果可空整数 myNullableInteger
已被分配了一个有效的整数值,则普通整数 myInteger
将被赋值为 myNullableInteger
的值;否则,myInteger
将被赋值为 -1
。
轻量级事务管理器
在计算中,事务是一项离散活动——一项活动要么完全完成,要么完全不完成。资源管理器确保如果在某个资源上启动了事务,则在事务未完全完成时,该资源将恢复到其原始状态。分布式事务是跨越多个资源的事务,因此涉及多个资源管理器。多年来,Windows 操作系统中已集成了一个分布式事务管理器。它就是 Microsoft Distributed Transaction Coordinator。
.NET Framework 1.0 和 1.1 版本提供了两种事务编程方式。一种方式由 ADO.NET 提供。该技术中的抽象类 System.Data.Common.DbConnection
定义了一个 BeginTransaction()
方法,通过该方法可以显式启动由 DbConnection
具体实现提供的特定资源管理器控制的事务。另一种事务编程方式由 Enterprise Services 提供。它提供了 System.EnterpriseServices.Transaction
属性,可以将其添加到 System.EnterpriseServices.ServicedComponent
的任何子类中,以便将该类任何方法中执行的任何代码隐式地注册到一个由 Microsoft Distributed Transaction Coordinator 管理的事务中。
ADO.NET 提供了一种显式编程事务的方式,而 Enterprise Services 则允许您以声明式方式进行。然而,在选择 ADO.NET 提供的显式事务编程风格和 Enterprise Services 提供的声明式风格时,您还必须选择事务将如何处理。使用 ADO.NET 时,事务由单个资源管理器处理,而使用 Enterprise Services 时,事务会产生涉及 Microsoft Distributed Transaction Coordinator 的开销,无论事务是否实际分布。
.NET 2.0 引入了轻量级事务管理器 System.Transactions.TransactionManager
。顾名思义,轻量级事务管理器具有最小的开销:“...[Microsoft 对 SQL Server 2005 进行的性能基准测试,将 [轻量级事务管理器事务] 与直接使用原生事务进行比较,未发现使用这两种方法之间存在统计学上的差异]”(Lowy 2005, 12)。如果事务中只涉及一个资源管理器,则轻量级事务管理器允许该资源管理器管理事务,而轻量级事务管理器仅对其进行监控。但是,如果轻量级事务管理器检测到第二个资源管理器已参与到事务中,则轻量级事务管理器会让原始资源管理器放弃事务控制,并将该控制转移给分布式事务协调器。将进行中的事务控制转移给分布式事务协调器称为事务的提升。
System.Transactions
命名空间允许您使用轻量级事务管理器显式或隐式地编程事务。显式风格使用 System.Transactions.CommitableTransaction
类
CommitableTransaction transaction = new CommittableTransaction();
using(SqlConnection myConnection = new SqlConnection(myConnectionString))
{
myConnection.Open();
myConnection.EnlistTransaction(tx);
//Do transactional work
//Commit the transaction:
transaction.Close();
}
替代的隐式编程风格(由于更灵活而更可取)使用 System.Transactions.TransactionScope
类
using(TransactionScope scope = new TransactionScope)
{
//Do transactional work:
//...
//Since no errors have occurred, commit the transaction:
scope.Complete();
}
这种事务编程风格是隐式的,因为在 System.Transactions.TransactionScope
实例的 using
块内执行的代码会自动注册到一个事务中。System.Transactions.TransactionScope
实例的 Complete()
方法只能调用一次,如果调用了,则事务将提交。
System.Transactions
命名空间还提供了编程自己的资源管理器的机制。但是,了解轻量级事务管理器的目的以及 System.Transactions.TransactionScope
类提供的隐式事务编程风格,将足以帮助您学习 Windows Communication Foundation。
角色提供程序
角色提供程序是派生自抽象类 System.Web.Security.RoleProvider
的类。该类具有清单 1.1 中所示的接口。显然,它定义了十个用于管理角色的简单方法,包括确定给定用户是否已被分配了特定角色。角色提供程序在实现这些抽象方法时,将读取和写入特定的角色信息存储。例如,.NET Framework 2.0 中包含的 System.Web.Security.RoleProvider
的一个具体实现是 System.Web.Security.AuthorizationStoreRoleProvider
,它使用 Authorization Manager Authorization Store 作为其角色信息存储库。另一个具体实现 System.Web.Security.SqlRoleProvider
使用 SQL Server 数据库作为其存储。但是,由于 System.Web.Security.RoleProvider
具有一套非常简单的角色管理方法,因此,如果 .NET Framework 2.0 中包含的角色提供程序都不适合,您可以轻松地提供自己的实现,以使用您偏好的任何角色信息存储。角色提供程序将角色数据存储的细节隐藏在一个简单的标准接口后面,用于查询和更新该信息。尽管 System.Web.Security.RoleProvider
包含在 ASP.NET 的 System.Web
命名空间中,但角色提供程序可以在任何 .NET 2.0 应用程序中使用。
清单 1.1 System.Web.Security.RoleProvider
public abstract class RoleProvider : ProviderBase
{
protected RoleProvider();
public abstract string ApplicationName { get; set; }
public abstract void AddUsersToRoles(
string[] usernames, string[] roleNames);
public abstract void CreateRole(
string roleName);
public abstract bool DeleteRole(
string roleName, bool throwOnPopulatedRole);
public abstract string[] FindUsersInRole(
string roleName, string usernameToMatch);
public abstract string[] GetAllRoles();
public abstract string[] GetRolesForUser(
string username);
public abstract string[] GetUsersInRole(
string roleName);
public abstract bool IsUserInRole(
string username, string roleName);
public abstract void RemoveUsersFromRoles(
string[] usernames, string[] roleNames);
public abstract bool RoleExists(string roleName);
}
静态类 System.Web.Security.Roles
为角色管理提供了另一层封装。考虑此代码片段
if (!Roles.IsUserInRole(userName, "Administrator"))
{
[...]
}
在此,使用静态 System.Web.Security.Roles
类来查询给定用户是否已被分配到 Administrator 角色。此代码段有趣之处在于,查询的进行无需先创建特定角色提供程序的实例。静态 System.Web.Security.Roles
类隐藏了与角色提供程序的交互。它使用的角色提供程序是应用程序配置中指定的默认角色提供程序。清单 1.2 是一个示例配置,它将名为 MyRoleProvider
的角色提供程序(它是 System.Web.Security.AuthorizationStoreRoleProvider
类的实例)标识为默认角色提供程序。
清单 1.2 角色提供程序配置
<configuration>
<connectionStrings>
<add name="AuthorizationServices"
´connectionString="msxml://~\App_Data\SampleStore.xml" />
</connectionStrings>
<system.web>
<roleManager defaultProvider="MyRoleProvider"
enabled="true"
cacheRolesInCookie="true"
cookieName=".ASPROLES"
cookieTimeout="30"
cookiePath="/"
cookieRequireSSL="false"
cookieSlidingExpiration="true"
cookieProtection="All" >
<providers>
<clear />
<add
name="MyRoleProvider"
type="System.Web.Security.AuthorizationStoreRoleProvider"
connectionStringName="AuthorizationServices"
applicationName="SampleApplication"
cacheRefreshInterval="60"
scopeName="" />
</providers>
</roleManager>
</system.web>
</configuration>
摘要
本章介绍了一些 .NET 2.0 中新增的编程工具,这些工具是理解和有效使用 Windows Communication Foundation 的先决条件
- C# 中的新
partial
关键字允许通过分布在单个模块的源代码文件中的任意数量的部分来组合类型的定义。 - 泛型是从中创建任意数量的完全预编程类的模板。
- 可空值类型是值类型,它们可以被赋值为
null
,并且可以检查null
值。 - 轻量级事务管理器确保事务尽可能高效地进行管理。已提供了一种优雅的新语法来使用它。
- 角色提供程序实现了一个简单、标准的接口,用于管理用户被分配到的角色,该接口独立于角色信息的存储方式。
参考文献
Ecma International. 2006. ECMA-335: Common Language Infrastructure (CLI) Partitions I–VI. Geneva: Ecma.
Lowy, Juval. Introducing System.Transactions. http://www.microsoft.com/downloads/details.aspx?familyid=aac3d722-444c-4e27-8b2e-c6157ed16b15&displaylang=en. 访问于 2006 年 8 月 20 日。
Microsoft 2006. Overview of Generics in the .NET Framework. http://msdn2.microsoft.com/en-us/library/ms172193.aspx. 访问于 2006 年 8 月 20 日。
Richter, Jeffrey. 2002. Applied Microsoft .NET Framework Programming. Redmond, WA: Microsoft Press.
版权所有 © 2007 Pearson Education。保留所有权利。