在使用 Membership 的时候可以为同一种操作方法定义多种行为,而具体使用哪种行为只需要在 Web.Config 中定义即可。
这样可以极大的促进了系统的灵活性,可是 Membership 这种 Provider 服务是怎么设计的呢?查了一些资料,也查看了 .Framework 2.0 的反编译源码,最终还是在 MSDN 上的一篇英文资料中找到了答案。
设计这种模式,似乎并不是那么容易,需要设计许多类方可。
构建基于Provider的自定义服务
下面是一个基本Provider的自定义服务的示例,它公开了两个操作方法“RetrieveImage”和“SaveImage”。它有可以会使用不同的数据库,这样可以定义多种处理方法。只需要在 Web.Config 中进行配置,就可以让系统调用相应的行为来进行处理。
1、 首先构建一个 ImageProvider 它继承了 ProviderBase 类。
public abstract class ImageProvider : ProviderBase
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
// Properties
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public abstract string ApplicationName
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public abstract bool CanSaveImages
{ get; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// Methods
public abstract Image RetrieveImage (string id);
public abstract void SaveImage (string id, Image image);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
public class ImageProviderCollection : ProviderCollection
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public new ImageProvider this[string name]
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return (ImageProvider) base[name]; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public override void Add(ProviderBase provider)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (provider == null)
throw new ArgumentNullException("provider");
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (!(provider is ImageProvider))
throw new ArgumentException
("Invalid provider type", "provider");
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
base.Add(provider);
}
}
2、 我们先定义一个使用 SQL Server 的处理方法。
1
[SqlClientPermission (SecurityAction.Demand, Unrestricted=true)]
2
public class SqlImageProvider : ImageProvider
3![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
4
private string _applicationName;
5
private string _connectionString;
6![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7
public override string ApplicationName
8![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
9![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _applicationName; }
10![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _applicationName = value; }
11
}
12![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
13
public override bool CanSaveImages
14![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
15![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return false; }
16
}
17![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18
public string ConnectionStringName
19![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _connectionStringName; }
21![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _connectionStringName = value; }
22
}
23![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
24
public override void Initialize (string name,
25
NameValueCollection config)
26![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
27
// Verify that config isn't null
28
if (config == null)
29
throw new ArgumentNullException ("config");
30![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
31
// Assign the provider a default name if it doesn't have one
32
if (String.IsNullOrEmpty (name))
33
name = "SqlImageProvider";
34![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
35
// Add a default "description" attribute to config if the
36
// attribute doesn't exist or is empty
37![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (string.IsNullOrEmpty (config["description"]))
{
38
config.Remove ("description");
39
config.Add ("description",
40
"SQL image provider");
41
}
42![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
43
// Call the base class's Initialize method
44
base.Initialize(name, config);
45![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
46
// Initialize _applicationName
47
_applicationName = config["applicationName"];
48![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
49
if (string.IsNullOrEmpty(_applicationName))
50
_applicationName = "/";
51![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
52
config.Remove["applicationName"];
53![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
54
// Initialize _connectionString
55
string connect = config["connectionStringName"];
56![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
57
if (String.IsNullOrEmpty (connect))
58
throw new ProviderException
59
("Empty or missing connectionStringName");
60![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
61
config.Remove ("connectionStringName");
62![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
63
if (WebConfigurationManager.ConnectionStrings[connect] == null)
64
throw new ProviderException ("Missing connection string");
65![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
66
_connectionString = WebConfigurationManager.ConnectionStrings
67
[connect].ConnectionString;
68![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
69
if (String.IsNullOrEmpty (_connectionString))
70
throw new ProviderException ("Empty connection string");
71![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
72
// Throw an exception if unrecognized attributes remain
73![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
81
public override Image RetrieveImage (string id)
82![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
83
// TODO: Retrieve an image from the database using
84
// _connectionString to open a database connection
85
}
86![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
87
public override void SaveImage (string id, Image image)
88![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
89
throw new NotSupportedException ();
90
}
91
}
配置基于Provider的自定义服务
现在可以看看如何在 Web.Config 中配置它所需的节点。这里在 <System.Web> 节中添加了 <ImageService> 节,在属性 defaultProvider 中指定了它使用的默认 Provider 服务。
3. Web.Config 文件中配置 Image Service
1
<configuration >
2
![](https://www.cnblogs.com/Images/dot.gif)
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![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
public class ImageServiceSection : ConfigurationSection
5
{
6
[ConfigurationProperty("providers")]
7
public ProviderSettingsCollection Providers
8
{
9
get { return (ProviderSettingsCollection) base["providers"]; }
10
}
11![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
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![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
22![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
这一下可以在 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![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
23![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
现在可以加载并初始化自定义的 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![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
8
public class ImageService
9![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
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![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
16![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _provider; }
17
}
18![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
19
public ImageProviderCollection Providers
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
21![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _providers; }
22
}
23![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
24
public static Image RetrieveImage(int imageID)
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
26
// Make sure a provider is loaded
27
LoadProviders();
28![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
29
// Delegate to the provider
30
return _provider.RetrieveImage(imageID);
31
}
32![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
33
public static void SaveImage(Image image)
34![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
35
// Make sure a provider is loaded
36
LoadProviders();
37![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
38
// Delegate to the provider
39
_provider.SaveImage(image);
40
}
41![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
42
private static void LoadProviders()
43![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
44
// Avoid claiming lock if providers are already loaded
45
if (_provider == null)
46![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
47
lock (_lock)
48![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
49
// Do this again to make sure _provider is still null
50
if (_provider == null)
51![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
52
// Get a reference to the <imageService> section
53
ImageServiceSection section = (ImageServiceSection)
54
WebConfigurationManager.GetSection
55
("system.web/imageService");
56![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
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![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
65
if (_provider == null)
66
throw new ProviderException
67
("Unable to load default ImageProvider");
68
}
69
}
70
}
71
}
72
}
73![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
74![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
这些在 Asp.NET 2.0 中被支持。