Reputation: 12376
I'm building my own generic solution to Entity Framework update in disconnected scenario. Many different approaches could be taken but I decided to decorate ICollection properties inside my entities with a custom attribute so that I can check the state of each entity inside those collections. Here's a sample entity with a navigation property:
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 {}
I have a generic Update method that I'm planning to use to update any type of entity which will ensure that the related entities also get updated properly.
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)
{
}
}
}
}
Now, assume I'm sending an instnce of Task to the above update method.In line 3, when iterator comes to the Executors
property, the condition in line 5 resolves to true. Now I need to iterate through the Executors property and do appropriate tasks. For this particular case, in line 6 I can say:
foreach (var item in (pi.GetValue(entity) as ICollection<TaskExecutor>))
But how can I determine what to type instead of T in ICollection<T>
?
Upvotes: 0
Views: 628
Reputation: 111890
The usual solution is:
foreach (object item in (IEnumerable)pi.GetValue(entity))
{
}
and then inside check the type of the item
.
Note that for historical reasons IEnumerable<T>
is based on IEnumerable
, and ICollection<T>
is based on IEnumerable<T>
and so on IEnumerable
, but ICollection<T>
isn't based on ICollection
.
In general if you want the type T
of an IEnumerable<T>
you can (taken from https://stackoverflow.com/a/906513/613130):
// 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);
}
note that with the changes I've introduced I've created some small side-effects: a collection NOT based on IEnumerable<T>
but only on IEnumerable
will return typeof(object)
. A collection based on multiple IEnumerable<T>
will return only a single one... For example:
public class MyStupidClass : IEnumerable<int>, IEnumerable<long>
{
}
But this is a degenerate example.
Upvotes: 2