8

我正在尝试创建一些东西来在我们的 ASP.NET 网站中保存全球站点范围的设置 - 例如站点名称、谷歌分析帐号、facebook url 等......该站点可以有多个“品牌”或关联的子站点有了它,因此有了sitguid 列,我们还希望可以选择将它们分组,因此也可以选择组列——例如TEST 和PRODUCTION(通过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

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

我在这里和谷歌都做了很多搜索,并没有真正找到我想要的东西,尤其是添加新设置“定义”以及编辑现有设置的能力。

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

与往常一样,非常感谢任何帮助!

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

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

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

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

4

5 回答 5

4

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

地点:

站点 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;一个简单的模型可能看起来像这样:

网格编辑模型

更复杂的方法可能看起来像这样

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

于 2012-04-11T09:29:47.330 回答
1

正如您可能发现的那样,您可以通过多种方式执行此操作,从 Microsoft 推荐的到 100% 自己动手。你目前处于自己动手的一端,我认为这通常会更好。

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

选项 1:Redis

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

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

选项 2:SQL 服务器

您提到的方法实际上是一种很好的方法,但不是每次都查询数据库,而是将强类型集合或 Hashtable 放入缓存中,让它每隔几个小时过期一次。

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

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 等。

于 2012-04-10T13:03:07.593 回答
1

我目前通过创建一个具有所有设置的类来做到这一点,例如

class Setting
{
GUID siteGuid{get; 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
}
}


现在在您的代码中的任何地方只包含命名空间并使用设置。您可以在使用变量时
填充一次或每个间隔填充设置,这将很快,因为您将所有设置都保存在内存中。 另外,如果您希望您应该能够动态添加设置,那么您可以使用数据库中的列名或仅使用映射动态添加设置, 然后您可以通过将设置 ExpandoObject 转换为此处来使用设置string 可以是字符串转换后的 GUIDapplication_startlastRefreshedOn

ExpandoObject
IDictionary<string,object>

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

于 2012-04-10T13:13:32.353 回答
0

我会使用 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
于 2012-04-10T16:57:44.843 回答
-1

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

于 2012-04-10T11:58:45.077 回答