在使用 Membership 的时候可以为同一种操作方法定义多种行为,而具体使用哪种行为只需要在 Web.Config 中定义即可。
这样可以极大的促进了系统的灵活性,可是 Membership 这种 Provider 服务是怎么设计的呢?查了一些资料,也查看了 .Framework 2.0 的反编译源码,最终还是在 MSDN 上的一篇英文资料中找到了答案。
设计这种模式,似乎并不是那么容易,需要设计许多类方可。
构建基于Provider的自定义服务
下面是一个基本Provider的自定义服务的示例,它公开了两个操作方法“RetrieveImage”和“SaveImage”。它有可以会使用不同的数据库,这样可以定义多种处理方法。只需要在 Web.Config 中进行配置,就可以让系统调用相应的行为来进行处理。
1、 首先构建一个 ImageProvider 它继承了 ProviderBase 类。
public abstract class ImageProvider : ProviderBase


{
// Properties

public abstract string ApplicationName
{ get; set; }

public abstract bool CanSaveImages
{ get; }

// Methods
public abstract Image RetrieveImage (string id);
public abstract void SaveImage (string id, Image image);
}

public class ImageProviderCollection : ProviderCollection


{
public new ImageProvider this[string name]

{

get
{ return (ImageProvider) base[name]; }
}

public override void Add(ProviderBase provider)

{
if (provider == null)
throw new ArgumentNullException("provider");

if (!(provider is ImageProvider))
throw new ArgumentException
("Invalid provider type", "provider");

base.Add(provider);
}
}
2、 我们先定义一个使用 SQL Server 的处理方法。
1
[SqlClientPermission (SecurityAction.Demand, Unrestricted=true)]
2
public class SqlImageProvider : ImageProvider
3

{
4
private string _applicationName;
5
private string _connectionString;
6
7
public override string ApplicationName
8
{
9
get
{ return _applicationName; }
10
set
{ _applicationName = value; }
11
}
12
13
public override bool CanSaveImages
14
{
15
get
{ return false; }
16
}
17
18
public string ConnectionStringName
19
{
20
get
{ return _connectionStringName; }
21
set
{ _connectionStringName = value; }
22
}
23
24
public override void Initialize (string name,
25
NameValueCollection config)
26
{
27
// Verify that config isn't null
28
if (config == null)
29
throw new ArgumentNullException ("config");
30
31
// Assign the provider a default name if it doesn't have one
32
if (String.IsNullOrEmpty (name))
33
name = "SqlImageProvider";
34
35
// Add a default "description" attribute to config if the
36
// attribute doesn't exist or is empty
37
if (string.IsNullOrEmpty (config["description"]))
{
38
config.Remove ("description");
39
config.Add ("description",
40
"SQL image provider");
41
}
42
43
// Call the base class's Initialize method
44
base.Initialize(name, config);
45
46
// Initialize _applicationName
47
_applicationName = config["applicationName"];
48
49
if (string.IsNullOrEmpty(_applicationName))
50
_applicationName = "/";
51
52
config.Remove["applicationName"];
53
54
// Initialize _connectionString
55
string connect = config["connectionStringName"];
56
57
if (String.IsNullOrEmpty (connect))
58
throw new ProviderException
59
("Empty or missing connectionStringName");
60
61
config.Remove ("connectionStringName");
62
63
if (WebConfigurationManager.ConnectionStrings[connect] == null)
64
throw new ProviderException ("Missing connection string");
65
66
_connectionString = WebConfigurationManager.ConnectionStrings
67
[connect].ConnectionString;
68
69
if (String.IsNullOrEmpty (_connectionString))
70
throw new ProviderException ("Empty connection string");
71
72
// Throw an exception if unrecognized attributes remain
73
if (config.Count > 0)
{
74
string attr = config.GetKey (0);
75
if (!String.IsNullOrEmpty (attr))
76
throw new ProviderException
77
("Unrecognized attribute: " + attr);
78
}
79
}
80
81
public override Image RetrieveImage (string id)
82
{
83
// TODO: Retrieve an image from the database using
84
// _connectionString to open a database connection
85
}
86
87
public override void SaveImage (string id, Image image)
88
{
89
throw new NotSupportedException ();
90
}
91
}
配置基于Provider的自定义服务
现在可以看看如何在 Web.Config 中配置它所需的节点。这里在 <System.Web> 节中添加了 <ImageService> 节,在属性 defaultProvider 中指定了它使用的默认 Provider 服务。
3. Web.Config 文件中配置 Image Service
1
<configuration >
2

3
<connectionStrings>
4
<add name="ImageServiceConnectionString" connectionString="
" />
5
</connectionStrings>
6
<system.web>
7
<imageService defaultProvider="SqlImageProvider">
8
<providers>
9
<add name="SqlImageProvider" type="SqlImageProvider"
10
connectionStringName="ImageServiceConnectionString"/>
11
</providers>
12
</imageService>
13
</system.web>
14
</configuration>
结构节点<ImageServer> 现在系统是不可识别的,所有必需还要有一个相应的类用来描述 <ImageServer> 配置节。
4. <imageServer> 配置节的描述类
1
using System;
2
using System.Configuration;
3
4
public class ImageServiceSection : ConfigurationSection
5
{
6
[ConfigurationProperty("providers")]
7
public ProviderSettingsCollection Providers
8
{
9
get { return (ProviderSettingsCollection) base["providers"]; }
10
}
11
12
[StringValidator(MinLength = 1)]
13
[ConfigurationProperty("defaultProvider",
14
DefaultValue = "SqlImageProvider")]
15
public string DefaultProvider
16
{
17
get { return (string) base["defaultProvider"]; }
18
set { base["defaultProvider"] = value; }
19
}
20
}
21
22
这一下可以在 Web.Config 中注册 <imageService> 节了,并且它会被系统识别。
5. 创建 <imageService> 这个配置节的处理类
1
<configuration >
2
<configSections>
3
<sectionGroup name="system.web">
4
<section name="imageService"
5
type="ImageServiceSection, CustomSections"
6
allowDefinition="MachineToApplication"
7
restartOnExternalChanges="true" />
8
</sectionGroup>
9
</configSections>
10
<connectionStrings>
11
<add name="ImageServiceConnectionString" connectionString="
" />
12
</connectionStrings>
13
<system.web>
14
<imageService defaultProvider="SqlImageProvider">
15
<providers>
16
<add name="SqlImageProvider" type="SqlImageProvider"
17
connectionStringName="ImageServiceConnectionString"/>
18
</providers>
19
</imageService>
20
</system.web>
21
</configuration>
22
23
现在可以加载并初始化自定义的 Providers
上面的事情都完成后,就可以实现这个 ImageService 了,它将根据 Web.Config 加载配置中默认的ImageProvider ,可以在 ImageService 类中直接使用它。
6、创建 ImageService 类,它将使用配置中的实例来处理
1
using System;
2
using System.Drawing;
3
using System.Configuration;
4
using System.Configuration.Provider;
5
using System.Web.Configuration;
6
using System.Web;
7
8
public class ImageService
9

{
10
private static ImageProvider _provider = null;
11
private static ImageProviderCollection _providers = null;
12
private static object _lock = new object();
13
14
public ImageProvider Provider
15
{
16
get
{ return _provider; }
17
}
18
19
public ImageProviderCollection Providers
20
{
21
get
{ return _providers; }
22
}
23
24
public static Image RetrieveImage(int imageID)
25
{
26
// Make sure a provider is loaded
27
LoadProviders();
28
29
// Delegate to the provider
30
return _provider.RetrieveImage(imageID);
31
}
32
33
public static void SaveImage(Image image)
34
{
35
// Make sure a provider is loaded
36
LoadProviders();
37
38
// Delegate to the provider
39
_provider.SaveImage(image);
40
}
41
42
private static void LoadProviders()
43
{
44
// Avoid claiming lock if providers are already loaded
45
if (_provider == null)
46
{
47
lock (_lock)
48
{
49
// Do this again to make sure _provider is still null
50
if (_provider == null)
51
{
52
// Get a reference to the <imageService> section
53
ImageServiceSection section = (ImageServiceSection)
54
WebConfigurationManager.GetSection
55
("system.web/imageService");
56
57
// Load registered providers and point _provider
58
// to the default provider
59
_providers = new ImageProviderCollection();
60
ProvidersHelper.InstantiateProviders
61
(section.Providers, _providers,
62
typeof(ImageProvider));
63
_provider = _providers[section.DefaultProvider];
64
65
if (_provider == null)
66
throw new ProviderException
67
("Unable to load default ImageProvider");
68
}
69
}
70
}
71
}
72
}
73
74
这些在 Asp.NET 2.0 中被支持。