Is there a succinct way that I can constrain the number of child entities that can belong to a parent in Entity Framework. I am using 4.3.1 at th
There is no built-in way so you will have to code such validation yourselves. Some quick ideas:
SaveChanges
. You will at least have to check how many searches are already related to search list but you will still have concurrency problem (what if other request tries to add search to the same list but only one place is remaining - both can succeed the check and insert related search)Avoiding concurrency problems completely requires hand written queries with locking hints to ensure that only one request can check number of searches per search list and insert a new search in atomic transaction.
I ended up going with CustomValidationAttribute, and implemented it with a great deal of success. See below for my implementation info:
[NotMapped]
public String ValidationMessage { get; set; }
[CustomValidation(typeof(EntityValidation.EntityValidators), "ValidateSearchCount")]
public virtual List<Search> Searches { get; set; }
public static bool Create(ProjectContext db, SearchList searchList)
{
try
{
db.SearchLists.Add(searchList);
db.SaveChanges();
return true;
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
searchList.ValidationMessage += validationError.ErrorMessage;
}
}
return false;
}
catch (Exception)
{
return false;
}
}
public static ValidationResult ValidateSearchCount(List<Search> Searches)
{
bool isValid;
int count = Searches.Count();
isValid = (count <= 5000 ? true : false);
if (isValid)
{
return ValidationResult.Success;
}
else
{
return new ValidationResult("A maximum of 5000 searches may be added to a SearchList.");
}
}
A similar exception block is on the update method. In this way, when SaveChanges gets called it attempts to validate the entity and its child collections, and when the collection count is greater than 5000 the validator will return an error message which gets caught in the exception handler and stored in a local property for my controller to check when things go wrong.