基于反射的注入与动态代理:实际考虑因素?
我正在开发一些框架式代码,旨在执行大量操作(数十万个),所有这些代码都使用相同的基本组件,但需要从外部源接受特定于操作的配置数据。
目前假设有一个配置存储库,在给定适当的设置名称列表的情况下,它知道如何有效地加载这些设置并将它们存储在如下类型中:
public interface IConfiguration
{
dynamic Get(string key);
void Set(string key, dynamic value);
}
我打算做的是实现一些流畅的映射语法或者只是用这样的属性来装饰组件类:
public class MyComponent : IActivity
{
[Configuration("Threshold")]
public virtual int Threshold { get; set; }
[Configuration("SomeKey", Persistence = ConfigPersistence.Save)]
public virtual string SomeSetting { get; set; }
}
您明白了......希望如此。需要注意的是,某些属性实际上需要保存回到存储库,因此传统的 DI 库在这里不起作用;即使它们这样做了,它们也是迟钝的工具,无法旋转数十万个组件并加载/保存数百万个属性。换句话说,我不认为我在重新发明轮子,但如果有人想尝试说服我,请随意。
无论如何,我正在考虑两种可能的选项来处理将配置数据“注入”到这些组件实例中:
Plain vanilla Reflection - 扫描配置属性的类型并将成员信息(以及配置密钥)保存在静态字典。然后使用诸如
PropertyInfo.SetValue
和PropertyInfo.GetValue
之类的反射方法进行注入和提取(由于缺乏更好的术语)。这与大多数 DI 库使用的方法类似。使用诸如 Castle 之类的动态代理,并将拦截器连接到修饰属性,这样它们就不会引用私有/自动生成的字段,而是引用
IConfiguration
实例(即get< /code> 方法调用
IConfiguration.Get
,set
方法调用IConfiguration.Set
)。这类似于 NHibernate 和其他 ORM 使用的方法。
完整的实现最终可能会需要大量的工作,所以我不想在意识到自己错过了什么之前在错误的道路上走得太远。
所以我的问题是,两种方法的优缺点是什么,我需要避免哪些陷阱?我从性能、可维护性、防白痴等方面考虑。
或者,是否还有其他更快的途径来实现这一目标,最好是没有陡峭的学习曲线?
I'm working on some framework-ish code designed to execute a huge number of operations (hundreds of thousands), all of which use the same basic components, but need to accept operation-specific configuration data from an external source.
Assume for the moment that there's a configuration repository which, given the appropriate list of setting names, knows how to load these settings efficiently and store them in a type like the following:
public interface IConfiguration
{
dynamic Get(string key);
void Set(string key, dynamic value);
}
What I'm planning to do is implement either some fluent mapping syntax or just decorate the component classes with attributes like so:
public class MyComponent : IActivity
{
[Configuration("Threshold")]
public virtual int Threshold { get; set; }
[Configuration("SomeKey", Persistence = ConfigPersistence.Save)]
public virtual string SomeSetting { get; set; }
}
You get the picture... hopefully. What's important to note is that some properties actually need to be saved back to the repository, so conventional DI libraries don't work here; and even if they did, they're blunt instruments not designed to be spinning up hundreds of thousands of components and loading/saving millions of attributes. In other words, I don't think I'm reinventing the wheel, but if somebody wants to try to convince me otherwise, feel free.
Anyway, I'm considering two possible options to handle the "injection" of configuration data into these component instances:
Plain vanilla Reflection - scan the type for configuration attributes and save the member info (along with the config key) in a static dictionary. Then use reflection methods such as
PropertyInfo.SetValue
andPropertyInfo.GetValue
for the injection and extraction (for lack of a better term). This is similar to the approach used by most DI libraries.Use a dynamic proxy such as Castle and hook up an interceptor to the decorated properties, such that instead of referencing private/autogenerated fields, they reference the
IConfiguration
instance (i.e. theget
method callsIConfiguration.Get
and theset
method callsIConfiguration.Set
). This is similar to the approach used by NHibernate and other ORMs.
The full implementation may end up being a fair amount of work, so I don't want to go too far down the wrong path before realizing I missed something.
So my question is, what are the pros/cons of either approach, and what are the pitfalls I need to avoid? I'm thinking in broad terms of performance, maintainability, idiot-proofing, etc.
Or, alternatively, are there other, quicker paths to this goal, preferably which don't have steep learning curves?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
动态代理是更好的方法。定义一个“配置”拦截器,将配置中的值注入到组件中(最好是延迟注入)。使用动态代理,我还会为您的代理组件实现一个通用的 IDisposable 接口,这样当对象被处置或 GC 时,它将根据您的属性中设置的 Peristence 标志来保留配置值。
Dynamic proxy is much better approach. Define a "configuration" interceptor that injects the value from the configuration into your component (preferably lazily). Using Dynamic proxy, I'd also implement a generic IDisposable interface to your proxied Component, so that when the object is disposed or GC'd, it will persist configuration values based on the Peristence flag set in your attribute.