I'm trying to check if my entity has any dependencies before allowing deletion:
Normally we can do this for each entity type by doing this:
public class Company{
public int id;
public string name;
public virtual IList<Department> Departments { get; set; }
public virtual IList<Building> Buildings {get;set;}
public bool hasDependencies => Departments.Any() || Buildings.Any();
}
But I want to do this generically for any nagivational properties an entity could have
Note: Lazy loading is enabled.
public bool HasDependencies()
{
int ExampleEntityId = 2; // just example
Company companyEntity = _unitofwork.CompanyRepository.GetEntityById(ExampleEntityId);
IEnumerable<PropertyInfo> propertyInfoList = _unitofwork.CompanyRepository.GetNavigationProperties();
bool dependenciesExist = false;
foreach (PropertyInfo property in propertyInfoList)
{
dependenciesExist = companyEntity.**property**.Any(); //This obviously doesn't work, but just to convey the intent.
if (dependenciesExist) {
break;
};
}
return dependenciesExist;
}
This is the GetNavigationProperties method:
public IEnumerable<PropertyInfo> GetNavigationProperties()
{
var modelData = _context.Model.GetEntityTypes(typeof(TEntity)).ToList()[0];
var propertyInfoData = modelData.GetNavigations().Select(x => x.PropertyInfo);
return propertyInfoData;
}
I've looked in many pages including the following to figure this out:
EF5 How to get list of navigation properties for a domain object
Check if entity has references in other entities in Code First
If you rely on materialized entity instances with loaded navigations (lazy, eager or explicit), you can easily simulate the Any
check on collections since all they can be cast to IEnumerable<object>
, e.g.
dependenciesExist = property.GetValue(entity) is IEnumerable<object> collection
&& collection.Any();
But note that GetNavigations()
API does not return skip navigations (i.e. EF Core 5.0 introduced many-to-many via implicit join entity) - these are returned by GetSkipNavigations()
API. At the same time GetNavigations()
returns bot collection and reference navigations, so if you need to filter them out, or process them separately, you'd better return EF Core metadata objects rather than reflection parts. e.g.
var entityType = _context.Model.FindEntityType(typeof(TEntity));
var collectionNavigations = entityType.GetNavigations()
.Where(nav => nav.IsCollection)
.Concat<INavigationBase>(entityType.GetSkipNavigations());
var referenceNavigations = entityType.GetNavigations()
.Where(nav => !nav.IsOnDependent);
For reference navigations, you simply do null
check
dependenciesExist = property.GetValue(entity) != null
and for collections (both skip many-to-may or regular one-to-many) do the IEnumerable<object>
check as shown at the beginning.