我正在为OrientDB .Net库编写一些扩展方法,在C#中的模型类和数据库中的图之间进行映射(反之亦然)。这必然需要一些反思和动态编程。
以下方法用于设置模型对象上属性的值,该值表示顶点之间的边。例如,如果顶点A链接到具有边C的多个顶点B,则模型A可以具有类型List<B>
的属性,而模型B具有类型A
的属性(对于一个 - 多对多的关系。)
private static void SetLinkedProperty(
ABaseModel parent, ABaseModel child, string className)
{
PropertyInfo[] properties = parent.GetType()
.GetProperties(BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.SetProperty |
BindingFlags.GetProperty);
PropertyInfo propertySingle = properties
.Where(prop => IsModelProperty(prop, className)).SingleOrDefault();
PropertyInfo propertyCollection = properties
.Where(prop => IsModelCollectionProperty(prop, className)).SingleOrDefault();
if (propertySingle != null)
{
propertySingle.SetValue(parent, child);
}
if (propertyCollection != null)
{
dynamic propertyValue = propertyCollection.GetValue(parent);
if (propertyValue == null)
{
Type listOfT = typeof(List<>).MakeGenericType(
propertyCollection.PropertyType.GenericTypeArguments[0]);
IEnumerable collection = (IEnumerable)Activator.CreateInstance(listOfT);
propertyValue = collection;
propertyCollection.SetValue(parent, collection);
}
propertyValue.Add(child);
}
}
模型中的属性可以具有为其提供别名的属性,以帮助在DB和C#类之间进行映射,因此IsModelProperty
和IsModelCollectionProperty
会检查该别名以及属性类型是否为可枚举的类型。
但是,当我运行代码时,我在第RuntimeBinderException
行获得了propertyValue.Add(child)
:
OrientTest.exe中出现未处理的“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException”类型异常
附加信息:'System.Collections.Generic.List.Add(OrientTest.Participant)'的最佳重载方法匹配有一些无效的参数
异常时:
parent
是OrientTest.Employer
child
是OrientTest.Participant
className
是“EmployerParticipant”(在数据库中将雇主和参与者顶点链接在一起的边缘类的名称)properties
包含7个元素,每个元素对应Employer
propertySingle
是null
propertyCollection
代表属性List<Participant> Participants
propertyValue
是List<Participant>
我不明白为什么List<Participant>#Add(Participant)
有无效的参数,但dynamic
经常做一些奇怪的事情。
答案 0 :(得分:3)
重载解析失败,因为child
的类型为ABaseModel
,而非OrientTest.Participant
。它在运行时的值恰好是方法所期望的类型并不重要。这似乎是反直觉的,因为名称RuntimeBinder
,但其中有意义:重载决策的规则虽然在运行时应用,但与编译时C#使用的规则相同(因为{{ 1}}实际上是一个普通的旧C#对象)。偏离这一点会带来更多惊喜。
如果您编写自己的dynamic
实现,当然可以覆盖或绕过此行为,因此这不是对DynamicObject
的一般限制 - 它只是意味着您不能(ab)使用dynamic
以这种方式在运行时进行方法解析。
在这种情况下,如果您知道该属性对于某些dynamic
始终属于List<T>
类型,则会有一个简单的修复,因为T
实现List<T>
接受IList
任何旧的object
(可能有运行时异常):
IList propertyValue = (IList) propertyCollection.GetValue(parent);
...
propertyValue.Add(child);
如果您不知道它是一个列表,那么您必须咬紧牙关并动态调用Add
方法:
object propertyValue = propertyCollection.GetValue(parent);
...
propertyValue.GetType().GetMethod("Add").Invoke(propertyValue, child);
如果对象有多个.Add()
方法,你想要使用&#34;最正确的&#34;一。我假设我们不需要涵盖那个特定的案例。
实际上有第三种方法,这种做法过于苛刻,但在其他情况下可能会有用,那就是让论证本身dynamic
因此决议被迫做出正确的事情&#34 ;在运行时(对于某些值&#34;对&#34;):
propertyValue.Add((dynamic) child);