创建自定义设置提供程序以在应用程序之间共享设置






3.50/5 (6投票s)
本文介绍如何利用 .NET 应用程序设置体系结构,为分布式应用程序提供共享设置的访问权限。
引言
在 .NET 应用程序的上下文中,设置是指程序正常运行所必需的、但又不属于程序主要输入或输出的数据。与业务数据一样,设置也会发生变化。设置的示例包括文件夹路径或 Web 服务 URI。设置通常存储在应用程序配置文件(app.config 或 web.config)中,以便管理员无需重构和重新编译即可轻松更改其值。此解决方案对单个应用程序来说通常效果良好。但是,有时需要跨多个应用程序重用设置。本文将讨论如何以最少的代码实现这一点。
背景
在上一篇文章中,我演示了如何通过设置包装器类的强类型属性,将存储在 machine.config 文件中的设置暴露给多个应用程序。虽然这种方法简单有效,但有两个局限性:
- 它仅适用于同一台计算机上的应用程序,并且
- 设置无法在运行时更新。
为了克服这些限制,我们需要一个新的计划。
计划
我们的方法将涉及创建一个自定义设置提供程序,该提供程序将读取(并在需要时写入)我们的自定义数据存储。数据存储可以是 XML 文件、关系数据库,几乎可以是任何东西。在演示中,我选择了最常见的方法——将设置存储在数据库中。因此,首先我们将创建一个数据库表并填充一些数据。然后,我们将使用 Visual Studio 快速生成一个具有匹配我们设置的属性的类。为了让设置类能够与数据存储进行通信,我们将创建一个新的提供程序,并指示设置类使用它。在此演示中,提供程序类将使用 LINQ to SQL 来执行数据库读取和更新,但您也可以使用任何数据访问技术:Entity Framework、原始 ADO.NET、第三方 ORM,或任何适合您需求的。为了强调这一点,此方法并不依赖于特定的数据访问技术或特定的后端选择,而是取决于将设置类连接到自定义设置提供程序,我们将在步骤 5-7 中完成此操作。
分步指南
1. 创建数据存储
如前所述,我的数据存储将是一个数据库表。在设计用于存储设置的架构时,我们面临几个决定。我们将所有数据存储在一个 varchar
列中,还是将数据存储在不同的字段甚至不同的表中,具体取决于类型?我喜欢我为本次演示选择的架构的几点。请看下面的 Settings 表。
为简单起见,我将名称值对存储在一个表中。如果您偏爱规范化,也可以轻松地将它们拆分到单独的表中。这里有趣的部分是 Value 字段的数据类型。如您所见,我使用的是 sql_variant
。我选择 sql_variant
是因为,与 varchar
不同,它保留了底层类型(bit、int、date 等)的信息,而无需为可能使用的每种数据类型创建一个字段或表。这是 SQL Server 的优点之一。如果您喜欢它,请使用它。否则,或者如果您使用的是不支持 sql_variant
数据类型的 RDBMS,请选择一个替代方案。
我还添加了一个 Description 字段和一个 Enabled 字段,用于根据需要启用或禁用某个设置。
在创建好表后,用一些设置填充它。这是我的设置:
2. 创建一个新的类库
如果您已经有一个作为解决方案中其他项目核心引用的库,则可以使用它并跳过此步骤。如果没有,请向您的解决方案添加一个新的类库项目。在我的示例中,该项目名为 NetRudder,它也作为其他项目的根命名空间。
3. 添加引用
将以下程序集引用添加到上一步创建的库中:
- System.Configuration
- System.Data.Linq
4. 添加数据访问层
如果您的解决方案已包含数据访问层,则可以使用它。在演示中,我只是在我的 NetRudder 项目中添加了一个新的 LINQ to SQL 数据上下文。然后,我创建了一个 Setting
类,该类映射到我们之前创建的 Settings 表。使用 LINQ to SQL,这就像将表从 Server Explorer 拖到设计器表面一样简单。新类如下所示:
请注意一个有趣的细节:Value
属性的类型是 Object
- 这是 LINQ to SQL 对 sql_variant
的默认映射,并且对我们来说效果很好。
5. 创建设置类
为了利用 .NET 应用程序设置体系结构,我们需要创建一个继承自 ApplicationSettingsBase
的类。如果您阅读了我上一篇文章,您已经有一个设置类。如果没有,您可以使用 Visual Studio Settings Designer 快速创建一个:
- 向您的项目添加一个新的 Settings 模板。我将其命名为
NetrudderSettings
。 - 打开新的 .settings 文件。Settings Designer 将会显示。
- 输入您在第一个步骤的表中输入的相同设置。确保名称完全匹配。为每个设置选择正确的类型,并将 Scope 从 User 更改为 Application。这些值现在并不关键 - 如果数据库中找不到该设置,它们将用作默认值。
- 将 Access Modifier 更改为 Public,以便我们可以在程序集外部引用生成的类。
- 保存项目,但暂时不要关闭 Designer。
此时,Visual Studio 已为我们生成了一个包装器类,其属性名称与我们的设置名称完全相同。
6. 将设置类连接到使用自定义设置提供程序
让我们回顾一下。我们现在已具备以下组件:
- 一个已填充数据的 Settings 表
- 一个映射到 Settings 表的 LINQ to SQL
Setting
实体 - 一个公开各个设置作为属性的设置类(
NetrudderSettings
)
缺少的类是介于数据访问层和设置类之间的类。所以,让我们来创建它。
在 Settings Designer 中,单击“View Code”。如您所见,NetrudderSettings
是一个部分类。如果您想手动添加属性而不是使用 Settings Designer,可以在此处进行。如果您需要属性是可写的(请参阅下面的注释),这将特别有用。现在,我们只需要用以下属性来装饰我们的类:
<SettingsProvider(GetType(LinqSettingsProvider))> _
Partial Public NotInheritable Class NetrudderSettings
通过添加属性,我们指示设置类使用我们将要编写的自定义提供程序。没有这个属性,NetrudderSettings
将使用 LocalFileSettingsProvider
,这是默认的提供程序类,它知道如何“与” .NET 配置文件(machine.config、app.config、web.config)“对话”,但不能与数据库或其他任何东西“对话”。
我们还没有创建 LinqSettingsProvider
类,因此您会在类名下看到一条波浪线。将光标放在名称上以调出错误更正菜单,选择让 Visual Studio 为您创建该类。(如果您因为使用的是 VS Express 而没有此选项,只需手动创建该类即可。)
使设置可写
默认情况下,当您使用 Settings Designer 添加设置时,Visual Studio 创建的用于公开这些设置的属性将被标记为 ReadOnly
。如果您的某些设置需要可写,您必须手动创建这些属性。没什么大不了的。使用 Settings Designer,删除您希望可写的任何设置。打开您在此步骤中修改的部分类,并按照以下模式添加您希望可写的属性:
<Global.System.Configuration.ApplicationScopedSettingAttribute()> _
Public Property Year() As Integer
Get
Return CType(Me("Year"), Integer)
End Get
Set(ByVal value As Integer)
Me("Year") = value
End Set
End Property
7. 让自定义设置提供程序发挥作用
设置提供程序是我们解决方案中唯一直接与数据访问层通信的部分。如何做到这一点取决于我们。首先,确保您的提供程序类继承自 SettingsProvider
。此时,您应该看到一个类似以下的存根:
Public Class LinqSettingsProvider
Inherits SettingsProvider
Public Overrides Property ApplicationName() As String
Get
End Get
Set(ByVal value As String)
End Set
End Property
Public Overrides Function GetPropertyValues(ByVal context As _
System.Configuration.SettingsContext, ByVal collection _
As System.Configuration.SettingsPropertyCollection) _
As System.Configuration.SettingsPropertyValueCollection
End Function
Public Overrides Sub SetPropertyValues(ByVal context As _
System.Configuration.SettingsContext, ByVal collection _
As System.Configuration.SettingsPropertyValueCollection)
End Sub
End Class
在 Initialize
方法中添加以下行:
MyBase.Initialize(Me.ApplicationName, col)
在 ApplicationName
属性的 Getter 中添加以下代码(将 Setter 留空):
ApplicationName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
向 GetPropertyValues
方法添加代码,该方法将值从数据存储读取到 SettingsPropertyValueCollection
中。为节省空间,此处不粘贴代码,您可以在包含的演示中查看所有代码。当然,如果您没有使用 LINQ to SQL,与数据访问层交互的代码部分将有所不同。
向 SetPropertyValues
方法添加代码,该方法获取更新后的设置并将其持久化到数据库(请参阅附加的项目)。
8. 添加“快捷方式”模块
我们快完成了。最后一步是创建一个方便的快捷方式,指向我们设置类的同步实例,从而更易于使用。将以下代码粘贴到一个新模块中,并用您的根命名空间替换 NetRudder
:
<Global.Microsoft.VisualBasic.HideModuleNameAttribute()> _
Public Module PublicSettingsProperty
Public ReadOnly Property Settings() As Netrudder.My.MySettings
Get
Return Netrudder.My.MySettings.Default
End Get
End Property
End Module
编译项目。完成!
访问设置
为了测试设置,我创建了一个新的控制台项目并引用了 NetRudder 程序集。您可以使用不同类型的应用程序执行相同的基本测试。以下代码从数据库读取我的设置并将其输出到控制台。它还更新了 Year
属性,该属性是我之前设置为可写的。请注意,要将更新持久化到数据存储,您需要调用设置类继承自 ApplicationSettingsBase
的 Save
方法。
With Netrudder.Settings
Console.WriteLine("Copyright {0} {1}", .Year, .Copyright)
Console.WriteLine("Lat/Lng: {0},{1}", .Lattitude, .Longitude)
.Year = 2009
'Persist the settings change
.Save()
End With
合并方法
仅仅因为您有一个自定义设置提供程序,并不意味着您不能像以前那样将某些设置存储在 .config 文件中。还记得在步骤 6 中我们是如何指示我们的设置类使用自定义设置提供程序的吗?好吧,您可以为单个属性覆盖此行为。因此,如果您有一个设置的值可能因每个应用程序而异,则可以将该属性的提供程序设置为 LocalFileSettingsProvider
,并将其值存储在 app.config/web.config 中。请查看下面的 MSDN 链接上的 SettingsProvider,了解更多关于混合使用提供程序的信息。