2

I am trying to write a generic method in a base class in my Entity Framework repository which will mark a record and related child records as deleted. Essentially to same function as EF cascade delete but updating a property on each record as opposed to deleting it.

In models my I have a Boolean property called "Deleted" as well as various navigation properties of type ICollection. I have created the generic method in my base repository called MarkDeleted which takes a single entity as a generic type parameter.

My plan is to iterate through all the properties of the entity and if it is the "Deleted" property set it to "true" and if it is a collection property iterate through all the entities of the collection calling the same MarkDeleted method passing the current entity as parameter.

I have got as far as setting the Deleted flag and Identifying which of the properties of the entity are collection properties by looking as posts such as How would I know if a property is a generic collection

My problem is to retrieve the collection of related entities from the collection property.

    public int MarkDeleted(T entity)
    {
        foreach (var prop in entity.GetType().GetProperties())
        {
            if (prop.Name == "Deleted")
            {
                prop.SetValue(entity, true);
            }

            if (prop.PropertyType.IsGenericType && 
                typeof(ICollection<>).IsAssignableFrom(prop.PropertyType.GetGenericTypeDefinition()))
            {
                foreach (var child in prop.xxxx ) //This is where I come unstuck. 
                                                  //How do I convert current property to its collection?
                {
                    MarkDeleted(child);
                }
            }
        }
        return SaveChanges();
    }

If I inspect this entity parameter at run-time I can see that EF has eager loaded all the related collections in the entity. So they are there I just need to figure out how to access them.

Tony
  • 89
  • 1
  • 12

1 Answers1

2

To accomplish this, you need to grab the property and cast it to something that can be iterated over. More specifically you need to cast it to at least IEnumerable. This is a valid cast because you are already checking for ICollection<> which implements IEnumerable.

if (prop.PropertyType.IsGenericType &&
    typeof(ICollection<>).IsAssignableFrom(prop.PropertyType.GetGenericTypeDefinition()))
{
    foreach (var child in (prop.GetValue(entity) as IEnumerable)) //Cast it here
    {
        MarkDeleted(child);
    }
}
Matt Rowland
  • 4,575
  • 4
  • 25
  • 34
  • 1
    Perfect, thanks. I had to make one additional change and that was to change the signature of MarkDeleted from MarkDeleted(T entity) to MarkDeleted(object entity) otherwise I got the error Cannot convert from 'object' to T. – Tony Aug 02 '19 at 13:55