管理数据库中持久的动态网站设置

Posted

技术标签:

【中文标题】管理数据库中持久的动态网站设置【英文标题】:Managing Dynamic Website Settings Persisted in a Database 【发布时间】:2012-04-18 11:20:38 【问题描述】:

我正在尝试创建一些东西来在我们的 ASP.NET 网站中保存全球站点范围的设置 - 例如站点名称、谷歌分析帐号、facebook url 等...该站点可以有多个“品牌”或子- 与之关联的站点,因此是 sitguid 列,我们还希望可以选择将它们分组,因此是组列 - 例如测试和生产(通过 web.config appsetting 设置)。

我不希望在任何地方对任何 KEY 进行硬编码,但我希望能够在代码中尽可能简单地引用它们,例如SiteSetting.getSetting(“SiteName”)(这些可以在更多初级开发人员将创建的模板(母版页等)中使用)

我还希望能够在我们的管理控制台中管理现有设置并能够创建新设置。

数据类型列用于编辑表单,以便可以使用正确的输入元素,例如位类型的复选框,varchar 的文本框等...

SiteSettings 数据库表当前:

  [sts_sitGuid] [uniqueidentifier] NOT NULL, -- tells us which site the setting is for
  [sts_group] [nvarchar](50) NOT NULL, -- used to group settings e.g. test/live
  [sts_name] [nvarchar](max) NULL, -- the display name of the setting, for edit forms
  [sts_alias] [nvarchar](50) NOT NULL, -- the name for the setting
  [sts_value] [nvarchar](max) NOT NULL, -- the settings value
  [sts_dataType] [nvarchar](50) NULL, -- indicates the control to render on edit form
  [sts_ord] [tinyint] NULL,  -- The order they will appear in on the admin form

我目前正在完成这项工作,但我对自己的工作方式并不满意,希望这里的任何人提供任何可能有助于找到“正确”解决方案的建议!我敢肯定,在我之前人们已经这样做了。 (我会分享我到目前为止所拥有的,但不想以任何特定方式扭曲答案)我正在寻找的只是一个关于如何最好地完成它的概述,而不是寻找任何人为我写它; )

我在这里和 Google 都进行了大量搜索,但并没有真正找到我想要的东西,尤其是添加新设置“定义”以及编辑现有设置的能力。

系统在 ASP.NET 上使用 webforms 运行,全部用 c# 编写并使用 MSSQL 2008。

一如既往,非常感谢任何帮助!

编辑:为了澄清,我将解释我到目前为止所构建的内容。我决定将所有这些存储在 SQL 中,因为我们不希望 web.config 或其他 xml 文件或其他数据库浮动,因为当我们向其他客户推出应用程序时,它会给我们更多工作。

到目前为止,我有一个 SiteSettings 类,它有一个 GetSetting 方法,我可以使用 GetSetting("SettingAlias") 调用它来获取数据库中“SettingAlias”的值。此类的构造函数从数据库中获取当前站点的所有设置并将其存储在字典中,GetSetting 从该字典中读取。到目前为止,我对这一切都很满意。

我正在努力的部分是生成编辑表单。以前的版本使用网络服务来获取/设置设置,我正在尝试继续使用类似的东西来保存工作,但它们都在代码中定义,例如 GoogleAnalyticsID、Sitename 等......并且每个都有一个列在数据库中,我所做的更改是将这些设置存储为 ROWS(从那时起添加更多更容易,无需更改架构和所有 sitesettings 类)目前我的 SiteSettings 类有一个 SiteSettingsEditForm 方法,它可以获取所有来自数据库的信息,为表单元素创建一堆控件,将其放入临时页面并执行,然后通过 ajax 将生成的 html 传递给我们的管理系统。这感觉不对,有点笨拙,是在这里发布它的原因,我无法弄清楚如何通过 web 服务将这些东西保存回来,但更重要的是通过执行包含负载的页面生成一堆 HTML表单控件感觉像是错误的做法。

所以总而言之,我(我认为我)想要编写一个类,以便能够缓存和读取数据库表中的少量行,并给我一个编辑表单(或将数据提供给其他东西以生成表单) 基于同一个数据库表的内容是动态的(例如,我的类型列是“位”我想要一个复选框,它是“文本”我想要一个文本输入)

【问题讨论】:

您是否有不想在项目中使用设置文件的原因?一切都将存储在配置文件中,配置转换将处理您的不同环境。它可以在没有数据库的情况下满足您的所有要求,所以我很好奇您为什么要付出如此多的额外努力。 我们一直在推出这些系统,并希望能够在在线管理区域中添加我们想要的任何设置 - 设置将由开发人员定义和设置,但可能由用户/站点管理员修改。认为数据库将是显而易见的方式。 【参考方案1】:

如果我正确理解您的问题,您正在寻找一个集中的配置框架。对于配置和服务器管理,我通常会推荐 Chef 或 Puppet 但是对于 ASP.NET,我做了一些快速的谷歌搜索,似乎基于 WCF 的 Configuration Service 可能会为您解决问题。我链接的文档是.NET StockTrader 5 Sample Application中使用的配置服务的分步教程。

【讨论】:

我并不是真的在寻找可以为我做这件事的东西,我只是对其他人是如何做到的感兴趣。这被设置为获取/设置配置选项的网络服务,但现在这些选项不再在应用程序中硬编码了我无法为不同的设置生成表单,因为我无法将网络控件从网络服务传递到我的应用程序。我想我应该把这个添加到我原来的问题中,而不是忽略我到目前为止所构建的内容。不过,我会给 PDF 一个阅读,听起来很有趣(而且是硬核!)(如果这是答案,那么我会相应地标记这个响应)【参考方案2】:

您可能已经发现,有多种方法可以做到这一点,从 Microsoft 推荐的到 100% 自己动手。您目前处于自己滚动的范围内,我认为这通常会更好。

不管怎样,基本上你要找的是一个 StateBag。一些松散类型和高度可配置的东西,它并不适合 RDBMS,但如果您已经有 SQL Server,那么使用它可能是最容易的。

选项 1:Redis

但是,另一种选择是为站点配置运行类似 Redis 的东西。您可以将其作为键/值对的集合存储在 Redis 对象中,将其拉入 JSON 对象,然后将其转换为 Hashtable(查找速度非常快)或其他一些可散列集合。一个简单的围绕集合的包装类就足够了。

Redis 非常轻巧,在您的情况下不需要大量配置或维护。

选项 2:SQL Server

你提到的方式其实是一个很好的实现方式,但不是每次都查询数据库,而是将强类型集合或哈希表放入缓存中,让它每隔几个小时过期一次。

如果你基本上使用你现在拥有的东西,你可以像这样存储它:

Setting
    SettingId Int PK,
    Key NVarchar(50),
    Name NVarchar(50),
    Description NVarchar(1000),
    Type NVarchar(50),
    Priority Int

Configuration
    SiteId Int,
    SettingId Int FK(Setting),
    SettingValue NVarchar(1000)

不要在数据库中存储诸如 live/test/dev 之类的内容。您需要拥有完全独立的数据库和服务器来区分 live/test/dev/stage/QA 等。

【讨论】:

+1 表示不在同一个数据库中存储实时/测试,但是我选择了一个不好的例子是不好的,这里的同一个系统可以托管多个站点,所以它会更喜欢 BRAND-A/ BRAND-B 类型的东西。再次感谢您的回答,尽管我并不热衷于在我们已经拥有的基础上添加 redis,因为这意味着为我们推出更多网站需要做更多的工作。 Redis 当然不是必需的。它的第二部分是如何将它存储在 SQL Server 中。我将编辑我的答案以使其更清楚。【参考方案3】:

我目前通过创建一个将所有设置作为属性的类来做到这一点,例如

class Setting

GUID siteGuidget; set;
//other settings

然后我像这样创建了一个静态类SettingManager

public static class SettingManager


private ConcurrentDictionary<GUID,Setting> settings= new ConcurrentDictionary<GUID,Setting>;
GetSetting(Guid siteGUID)

settings.TryGet(siteGuid);
Lastrefreshed = DateTime.Now;
//other code

Private DateTime LastRefreshedOn = DateTime.Now;
public void PopulateSetingsDic()

//populate the settings dictionary by getting the values from the database


现在,您的代码中的任何位置都只需包含命名空间并使用设置。 您可以使用lastRefreshedOn 变量在application_start 中填充设置一次或每个间隔 它会很快,因为您拥有内存中的所有设置。 另外,如果您希望您应该能够动态添加设置,那么您可以使用ExpandoObject 并使用数据库中的列名或仅使用映射动态添加设置 然后你就可以通过将设置 ExpandoObject 转换为 IDictionary&lt;string,object&gt; 来使用这些设置,这里的字符串可以是转换后的 GUID 字符串

编辑:- http://zippedtech.blogspot.in/2012/04/dynaminism-in-net-40.html 检查链接.. 我添加了一个新帖子来解决此类问题。

【讨论】:

这与我们最初的实现非常相似,但我们现在希望避免将这些东西硬编码到源代码中,因为应用程序的用户无法访问源代码,他们需要能够在后端定义新设置,然后在他们有权访问的模板/母版页中使用它们。 阅读我的回答的最后一部分。我实际上使用上面的代码动态创建的设置。给我一些时间。我贴一个例子。 在运行时动态地将道具添加到类中听起来非常棒,如果这确实是你在谈论的话。我将对此进行更深入的研究。 是的,我说的很简单。。道具名称可以依赖或与数据库列名称相同,因此创建它的用户将知道名称并按原样使用它们. 我已添加代码.. 完整代码的链接可在帖子中找到【参考方案4】:

我会使用 xml,创建一个可以将 xpath 作为“关键”的类Ex. MySett.get("//level1/mysetting") or even MySett.get("//mysetting")

每个人都可以返回一个集合,只是第一个,或者你想要的任何东西。 你甚至可以超载。 我喜欢 xml 是因为它具有很好的灵活性,并且为了减少其他代码,只需编写一个类。 不利的一面是,您需要在应用程序启动时加载文档并在关闭时保存。 这是vb代码中的示例类。 (c代码还是很相似的,我只是用vb,因为当时它已经上线了

Imports System.Xml
Public Class XSett
Public xml As XmlDocument
Public Overloads Function gett(ByVal xp As String)
    Return CType(xml.SelectSingleNode(xp), XmlElement).InnerXml
    'by using inner xml, you can have either text setting 
    'or more markup that you might need for another function
    'your choice.  you could even cast it into another instance
    'of this class
End Function
Public Overloads Function gett(ByVal xp As String, ByVal sel As Integer)
    Return CType(xml.SelectNodes(xp)(sel), XmlElement).InnerXml
    'here, you can have multiple and choose the one you want
End Function
Public Overloads Sub gett(ByVal xp As String, ByRef col As Collection)
    Dim i As Integer
    Dim nds = xml.SelectNodes(xp)
    For i = 0 To nds.Count - 1
        col.Add(CType(nds(i), XmlElement).InnerXml)
    Next
    'Creted an entire collection of elemens.
    'i used vb's "collection" object, but any collection would do
End Sub
Public Overloads Sub sett(ByVal ap As String, ByVal name As String, ByVal data As String)
    'assume add here.
    'ap asks for existing parent element. eg: //guids
    'name ask for name of setting element
    Dim ts = xml.CreateElement(name)
    ts.InnerXml = data
    If ap = "" Then 'we assume document element
        xml.DocumentElement.AppendChild(ts)
    Else
        Dim p = CType(xml.SelectSingleNode(ap), XmlElement)
        p.AppendChild(ts)
    End If
End Sub
Public Overloads Sub sett(ByVal xp As String, ByVal sel As Integer, ByVal data As String)
    'just change existing setting
    CType(xml.SelectNodes(xp)(sel), XmlElement).InnerXml = data
End Sub
'naturally you can expand infinitely if needed
End Class

【讨论】:

好主意,但我们并不热衷于添加更多文件来维护,我们也有一些默认值,我们包含在部署数据库中,我们必须在部署时以 XML 存储和传输/install 我们宁愿避免的时间。【参考方案5】:

如果您从数据模型开始,有时这种问题更容易可视化。如果您想要每行设置一个设置,那么两个表可能是存储它的最佳方式:

网站:

站点 ID 站点密钥 站点名称 1 XYGZ4345 客户端站点 1 2 AZT43752 客户端站点 2

这将定义您为其配置的站点列表。我会使用 SiteKey,就像您将其放在 web.config 中一样,最好将其抽象为随机字符串或 GUID(以使其更难意外加载其他人的配置),客户端可以更改其名称和您以后不会感到困惑,因为您没有使用他们的旧名称作为密钥等。

配置表本身也很简单,如果我们将每个设置都视为一个字符串:

网站设置:

SettingId SiteId SettingName SettingValue 1 1 品牌 KrustyBrand 2 1 GoogleId MSFTSUX0R 3 2 品牌 ConfigMasters(TM)

然后您可以非常简单地加载所有配置:

SELECT * FROM SiteSetting INNER JOIN Site ON (SiteSetting.SiteId = Site.SiteId) WHERE Site.SiteKey = 'XYGZ4345'

现在我们有了一个键值对列表,您可以将其存储在如下类中:

public class SiteSetting

    public Site Site 
        get; set; //Site would just be a simple class consisiting of Id, Key and Name to match the database table
    

    protected Dictionary<String, String> Settings  get; set;  //Simple key value pairs


所以这是一个非常简单的解决方案。但是,我们可以更进一步 - 需要考虑的事项:

1) 我们可以在某处添加环境吗?

我们可以为每个环境添加一个站点

将环境添加到 SiteSetting 表。这样做的好处是您可以定义 enironment = 'ALL' 以避免重复。

加载配置的数据库定义环境;所以你在应用程序配置中更改配置连接字符串。当然,要连接到不同的环境,您必须更改 app.config,但无论如何您都可能必须这样做才能更改客户端密钥和/或环境。

2) 添加用户可定义设置的概念 - 一些您想要更改的设置,一些您想要锁定的设置。包含“UserDefinable”的位列将允许您对此进行排序

3) 输入设置。

这有点困难。你可能有类似的东西:

PropertyId PropertyName PropertyType 格式 UserDefined 1 品牌字符串 NULL 1 2 DatePresentation DateTime "yyyy-MM-dd" 1

设置表然后只定义一个值和一个 PropertyId。这样做的好处是您可以开始增加有关您存储的每个设置的信息,并在设计更加规范化时重用这些信息。然后 Settings 类改变如下:

public List<PropertyValue>  get; set;  //replacing the dictionary

PropertyValue 然后看起来像:

public class PropertyValue

    int Id  get; set; 

    public string Name  get; set; 

    public string Value  get; set; 

    public string PVType  get; set;  //Could be an enum

    public string DisplayFormat  get; set;

    private string _RawValue;

    public string Value
        get
          switch(PVType)
            case "DateTime":
                    return Convert.ToDateTime(_RawValue).ToString(DisplayFormat);
            break;
            case "Double":
                    return Convert.ToDouble(_RawValue).ToString(DisplayFormat);
            break;
            default:
                    return _RawValue;
          
        
        set
            _RawValue = value;
        
    


需要改进诸如 Value 方法之类的东西以支持强大的错误处理(您也可以使用 Convert.ChangeType 进行研究以简化 switch 块)

这个话题是简单的还是复杂的,你可以选择做它;-)

编辑

关于维护它们;一个非常简单的 GUI 将允许用户以表格格式查看其所有属性。您可能会考虑将 UserDefinable = 0 的行设为只读,否则用户可以编辑和添加行。您需要进行验证,尤其是对于重复的设置名称。

最简单的方法是使用 DataGrid;一个简单的模型可能看起来像这样:

更复杂的方法可能类似于this

因此,生成表单就像将一组 PropertyValue 对象数据绑定到您选择的网格解决方案一样简单。

【讨论】:

到目前为止,这是我最喜欢的答案,并且有一些改进现有架构的好主意(尤其是站点+站点设置表并将属性标记为用户可编辑或不可编辑),但我的主要症结是我如何为这些生成编辑表单。 @Treborbob 我会使用数据网格进行编辑。 UserDefinable = 0 的东西将被锁定,您可以添加新行,并且您必须验证重复的属性值名称。实际上,您正在创建一个界面,该界面代表数据库中这些表的视图。基本方法很简单; 4guysfromrolla.com/articles/071002-1.aspx

以上是关于管理数据库中持久的动态网站设置的主要内容,如果未能解决你的问题,请参考以下文章

Apsara Clouder云计算专项技能认证:网站建设-简单动态网站搭建

静态网页与动态网页区别

乐道网站建设管理系统如何开启伪静态

使用 Zend 框架的动态网站导航 - XML 与 MySQL

企业题目php mysql动态网站毕业设计成品

在动态网站中快速加载图像