在运行时将属性添加到POCO类

我为我的项目选择了ServiceStack OrmLite ,这是一个纯数据导向的应用程序。 我愿意允许最终用户创建自己的XML格式定义的对象类型,这些格式将用于在运行时使用CodeDOM生成类。

我还将定义应用程序(即User )所需的一些“系统”对象,但我无法预见最终用户将使用的所有属性,因此我正在寻找一种方法来允许扩展我在设计时创建的类。 样品波纹管

 public class User { public Guid Uid { get; set; } public String Username { get; set; } public String Password { get; set; } } 

最终用户希望拥有EmailAddress 。 他应该能够将2个属性添加到上层类,并且整个类将是(OrmLite仍然可以使用它,因为它允许覆盖:

 public class User { public Guid Uid { get; set; } public String Username { get; set; } public String Password { get; set; } public String Email{ get; set; } public String Address { get; set; } } 

我知道可能存在崩溃系统的风险(如果类已经实例化),所以我正在寻找避免这个问题并模仿我需要的最佳方法。

看来你在这里做的事情有两个部分。 您需要动态创建类型以支持其他属性。 您还需要确保在AppDomain中永远不会出现重复类型,即两个不同的User定义。

运行时类型生成

已经给出的各种建议处理如何创建类型。 在一个项目中,我们有类似的东西。 我们创建了一个具有核心属性的基类和一个用于存储“扩展”属性的字典。 然后我们使用Reflection.Emit创建具有所需属性的派生类型。 每个属性定义只是从基类中的字典读取或写入。 由于Reflection.Emit需要编写低级IL代码,因此最初看起来很复杂。 我们在另一个类库中编写了一些示例派生类并对其进行了编译。 这些是我们在运行时实际需要实现的示例。 然后我们使用ildasm.exe来查看编译器生成的代码。 这使我们很容易弄清楚如何在运行时生成相同的代码。

避免命名空间冲突

您的第二个挑战是避免重复的类型名称。 我们将guid(删除了无效字符)附加到每个生成类型的名称,以确保从未发生过。 很容易解决,但我不知道你是否可以通过你的ORM逃脱。

如果这是服务器代码,您还需要考虑在.NET中永远不卸载程序集的事实。 因此,如果您在运行时重复生成新类型,您的流程将继续增长。 在客户端代码中也会发生同样的情况,但如果您不希望该进程长时间运行,则可能不会出现问题。

我说assembly没有卸下; 但是,您可以卸载整个AppDomain 。 因此,如果这是服务器代码,您可以在其自己的appdomain中运行整个操作,然后将其拆除以确保卸载动态创建的类型。

为什么不为其所有属性使用键值对,或者至少使用动态值?

http://msdn.microsoft.com/en-us/library/system.collections.hashtable.aspx

您可以按照您使用Reflection进行描述的方式执行此操作,但这会影响性能,这样也可以删除属性。

我目前正在进行的项目也有类似的要求。 我们有一个系统已经投入生产,并有一个客户请求添加字段。

我们通过简单地向我们的模型添加CustomFields属性来解决这个问题。

 public class Model: IHasId { [PrimaryKey] [Index(Unique = true)] public Guid Id { get; set; } // Other Fields... ///  /// A store of extra fields not required by the data model. ///  public Dictionary CustomFields { get; set; } } 

我们已经使用了几个星期而没有任何问题。

我们从中发现的另一个好处是每行可以有自己的自定义字段,因此我们可以按记录处理它们,而不是每个记录都需要它们。

查看ExpandoObject ,它为执行此类操作提供动态语言支持。 您可以使用它在运行时向POCO添加其他属性。 以下是使用.NET的DLRfunction的链接: http : //msdn.microsoft.com/en-us/library/system.dynamic.expandoobject%28v=vs.100%29.aspx