C# 构建通用应用程序-允许特定于客户的选项

C# 构建通用应用程序-允许特定于客户的选项,c#,oop,design-patterns,architecture,C#,Oop,Design Patterns,Architecture,我已经构建了一个开源应用程序,我很想知道其他人是如何处理特定于客户的请求的。对我来说,保持应用程序的简单是很重要的;我并不是想让所有人都能享受。应用程序可能会变得臃肿、复杂,而且几乎无法使用。但是,有一些特定于客户的选项是很好的(它只是不适用于所有客户)。例如 假设我们有一个名为服务器的域实体。在UI中,我们让客户从服务器列表中进行选择。对于一家公司来说,按位置(美国、德国、法国等)筛选服务器很有帮助。添加如下服务器属性非常简单: public class Server { public

我已经构建了一个开源应用程序,我很想知道其他人是如何处理特定于客户的请求的。对我来说,保持应用程序的简单是很重要的;我并不是想让所有人都能享受。应用程序可能会变得臃肿、复杂,而且几乎无法使用。但是,有一些特定于客户的选项是很好的(它只是不适用于所有客户)。例如

假设我们有一个名为服务器的域实体。在UI中,我们让客户从服务器列表中进行选择。对于一家公司来说,按位置(美国、德国、法国等)筛选服务器很有帮助。添加如下服务器属性非常简单:

public class Server
{
    public Location Location { get; set; }
    // other properties here
}
我担心的是,随着时间的推移,服务器可能会因属性而变得臃肿。即使我只添加了位置,也不是所有的客户都会关心该房产

一个选项是允许用户定义字段:

public class Server
{
    public string UserField1 { get; set; }
    public string UserField2 { get; set; }
    public string UserField3 { get; set; }
    // etc...
    // other properties here
}

这是最好的处理方法吗?我不喜欢这样一个事实,即通过将所有内容都设置为字符串来消除类型安全性。人们是否有其他/更好的方式来处理此类问题?甚至有这样的设计模式吗?

在我看来,一个好的设计模式是在数据库级别使用模式,然后在类级别使用基本继承

CREATE TABLE dbo.A (
    ColumnA INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    ColumnB VARCHAR(50),
    ColumnC INT,
    etc.
)
现在我们有了一个客户机,它需要一些特定的功能,所以让我们在不同的模式中创建此表的扩展:

CREATE TABLE CustomerA.A (
    ColumnA INT NOT NULL PRIMARY KEY,
    Location VARCHAR(50)
)
但现在我们有另一个客户需要以不同的方式进行扩展:

CREATE TABLE CustomerB.B (
    ColumnA INT NOT NULL PRIMARY KEY,
    DataCenterID INT
)
虽然这些字段可能不相关,但您已经明白了,因此现在我们需要在此处构建特定于客户的域模型:

public abstract class A
{
    public int ColumnA { get; set; }
    public string ColumnB { get; set; }
    public int ColumnC { get; set; }
}

public class CustomerA_A : A
{
    public string Location { get; set; }
}

public class CustomerB_A : A
{
    public int DataCenterID { get; set; }
}
现在,当我们需要为客户A构建一些东西时,我们将构建它们的子类,为客户B构建它们的子类,依此类推

现在,仅供参考,这是一个非常动态系统的开始。我之所以这么说,是因为缺少的部分,还不是动态的,是用户界面。有很多方法可以实现,但都超出了这个问题的范围。这是你必须考虑的问题。我这样说是因为管理接口的方式将决定您如何构建哪个子类


我希望这会有所帮助。

早期通常的方法是使用配置XML文件来完成这类工作。但是,针对客户特定需求的编程需要围绕如何编程的整体思维。请参阅类似的问题。

当然,这始终取决于您希望允许的自定义程度。在我们的产品中,我们甚至使用户能够使用属性和它们之间的关系完全定义自己的实体。基本上,每个EntityObject(我们称之为实体)最终都由一个值集合和一个对描述其中值的元模型的引用组成。我们设计了自己的查询语言,允许我们查询数据库并使用能够翻译成任何目标语言的表达式(尽管我们目前只做SQL和.net)

游戏并没有就此结束,您很快就会发现,验证规则、权限、默认值等都是必须具备的。当然,所有这些都需要UI支持,至少对于元模型的执行是如此


因此,这实际上取决于最终用户应该能够执行的调整量。我猜在大多数情况下,如您所述,简单的用户字段就足够了。在这种情况下,我将提供一个字段并在其中存储JSON文本。在UI中,您可以提供至少一个半体面的UI,允许结构和扩展性。

选项1:说“不”。-)

虽然我半开玩笑地说,但这是有道理的。开发人员经常会通过允许一到两个自定义特性来进行无休止的自定义,从而使滚雪球运动起来

当然,这必须是平衡的,听起来你可能在一定程度上做到了这一点。但如果你真的想让你的应用保持简单,那就让它保持简单,避免添加像这样的定制

选项2:继承

如果您真的需要添加定制,我会选择使用所有“标准”选项构建基类,然后构建包含特定于客户的优化的特定于客户的类

例如:

public class Server
{
    // all standard properties here
}
那么Joe's Pizza,您可以吃:

public class JoesPizzaServer : Server
{
    public Location Location { get; set; }
}
这样做的另一个好处是,它将允许您基于特定于客户机(或基本)模型的演示视图

例如,在MVC中,您可以像这样设置视图模型,然后可以为每个客户提供特定的视图

例如,Bob's Burgers对基本模型有自己的看法:

@model MyApp.Server
@* implement the base form *@
Joe's Pizza的视图将使用自定义模型:

@model MyApp.JoesPizza
@* implement the base form -- a partial view -- with addtional custom fields
MVC在支持这种模式方面做得非常好。如果您不使用MVC(可能是WPF或Web表单),仍然有一些方法可以利用部分“视图”文件来完成类似的任务


当然,您的数据库可以(也可能应该)支持类似的继承模型。实体框架甚至支持这样的各种继承模型。

这里我可能错了,但看起来您希望用相同的代码库处理不同版本的软件。我可以想出两种方法:

  • 实际上,为它定义不同的版本,并为每个客户机处理更改。从领域建模的角度来看,这不会给您带来问题,但需要一个支持基础设施,它必须根据您的客户需求进行扩展。还有一些相关的问题(例如,和)
  • 作为用户定义的配置,在域模型级别处理此问题。这种方法的优点是,您不必合并软件的多个版本,但这样做的代价是使您的模型更通用,并且可能更复杂。还有你的测试