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