如何将 PropertyInfo 值转换为 ICollection<T>,其中 T 可能是任何 class
How to cast PropertyInfo value to ICollection<T> where T might be any class
我正在为 Entity Framework 在断开连接的情况下更新构建自己的通用解决方案。可以采用许多不同的方法,但我决定使用自定义属性装饰我的实体内的 ICollection 属性,以便我可以检查这些集合内每个实体的状态。这是带有导航 属性:
的示例实体
public class SomeEntity
{
public int TaskId{ get; set; }
public string Instruction { get; set; }
[EntityNavigationProperty]
public virtual ICollection<TaskExecutor> Executors { get; set; }
}
public class TaskExecutor
{
public int TaskExecutorId { get; set; }
public int TaskId { get; set; }
public virtual Task Task { get; set; }
}
public class EntityNavigationProperty : Attribute {}
我有一个通用的更新方法,我打算用它来更新任何类型的实体,这将确保相关实体也得到正确更新。
public void Update(TEntity entity)
{
PropertyInfo[] properties = entity.GetType().GetProperties();
foreach (PropertyInfo pi in properties)
{
if (Attribute.IsDefined(pi, typeof(EntityNavigationProperty)))
{
foreach (//here I need to iterate through the ICollection object)
{
}
}
}
}
现在,假设我正在向上面的更新 method.In 第 3 行发送任务实例,当迭代器到达 Executors
属性 时,第 5 行的条件解决为真。现在我需要遍历执行器 属性 并执行适当的任务。对于这种特殊情况,在第 6 行我可以说:
foreach (var item in (pi.GetValue(entity) as ICollection<TaskExecutor>))
但是我如何确定在 ICollection<T>
中键入什么而不是 T?
通常的解决方法是:
foreach (object item in (IEnumerable)pi.GetValue(entity))
{
}
然后在里面检查item
.
的类型
注意由于历史原因IEnumerable<T>
是基于IEnumerable
,ICollection<T>
是基于IEnumerable<T>
等等IEnumerable
,但是ICollection<T>
不是 基于 ICollection
.
一般来说,如果您想要 IEnumerable<T>
的 T
类型,您可以(取自 ):
// returns typeof(T) of an IEnumerable<T>,
// or typeof(object) of an IEnumerable.
public static Type GetGenericTOfIEnumerable(object o)
{
return o.GetType()
.GetInterfaces()
.Where(t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GetGenericArguments()[0])
.FirstOrDefault() ?? (o is IEnumerable ? typeof(object) : null);
}
请注意,通过我介绍的更改,我产生了一些小的副作用:一个不基于 IEnumerable<T>
但仅基于 IEnumerable
的集合将 return typeof(object)
。基于多个 IEnumerable<T>
的集合将 return 只有一个... 例如:
public class MyStupidClass : IEnumerable<int>, IEnumerable<long>
{
}
但这是一个退化的例子。
我正在为 Entity Framework 在断开连接的情况下更新构建自己的通用解决方案。可以采用许多不同的方法,但我决定使用自定义属性装饰我的实体内的 ICollection 属性,以便我可以检查这些集合内每个实体的状态。这是带有导航 属性:
的示例实体public class SomeEntity
{
public int TaskId{ get; set; }
public string Instruction { get; set; }
[EntityNavigationProperty]
public virtual ICollection<TaskExecutor> Executors { get; set; }
}
public class TaskExecutor
{
public int TaskExecutorId { get; set; }
public int TaskId { get; set; }
public virtual Task Task { get; set; }
}
public class EntityNavigationProperty : Attribute {}
我有一个通用的更新方法,我打算用它来更新任何类型的实体,这将确保相关实体也得到正确更新。
public void Update(TEntity entity)
{
PropertyInfo[] properties = entity.GetType().GetProperties();
foreach (PropertyInfo pi in properties)
{
if (Attribute.IsDefined(pi, typeof(EntityNavigationProperty)))
{
foreach (//here I need to iterate through the ICollection object)
{
}
}
}
}
现在,假设我正在向上面的更新 method.In 第 3 行发送任务实例,当迭代器到达 Executors
属性 时,第 5 行的条件解决为真。现在我需要遍历执行器 属性 并执行适当的任务。对于这种特殊情况,在第 6 行我可以说:
foreach (var item in (pi.GetValue(entity) as ICollection<TaskExecutor>))
但是我如何确定在 ICollection<T>
中键入什么而不是 T?
通常的解决方法是:
foreach (object item in (IEnumerable)pi.GetValue(entity))
{
}
然后在里面检查item
.
注意由于历史原因IEnumerable<T>
是基于IEnumerable
,ICollection<T>
是基于IEnumerable<T>
等等IEnumerable
,但是ICollection<T>
不是 基于 ICollection
.
一般来说,如果您想要 IEnumerable<T>
的 T
类型,您可以(取自 ):
// returns typeof(T) of an IEnumerable<T>,
// or typeof(object) of an IEnumerable.
public static Type GetGenericTOfIEnumerable(object o)
{
return o.GetType()
.GetInterfaces()
.Where(t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GetGenericArguments()[0])
.FirstOrDefault() ?? (o is IEnumerable ? typeof(object) : null);
}
请注意,通过我介绍的更改,我产生了一些小的副作用:一个不基于 IEnumerable<T>
但仅基于 IEnumerable
的集合将 return typeof(object)
。基于多个 IEnumerable<T>
的集合将 return 只有一个... 例如:
public class MyStupidClass : IEnumerable<int>, IEnumerable<long>
{
}
但这是一个退化的例子。