Example #1
0
        /// <summary>
        /// Compares Collections, optimizes entity collection comparisons for better error messages or defers to the base
        /// </summary>
        /// <param name="expected">Expected Value</param>
        /// <param name="actualElements">Actual Elements to compare against</param>
        /// <param name="path">Path of the elements</param>
        /// <param name="shouldThrow">Should throw on error or not</param>
        /// <param name="comparisonSkippedForAnyElement">Skip for particular comparisons</param>
        /// <returns>A Comparison result</returns>
        protected override ComparisonResult CompareCollections(QueryCollectionValue expected, IEnumerable <object> actualElements, string path, bool shouldThrow, out bool comparisonSkippedForAnyElement)
        {
            comparisonSkippedForAnyElement = false;
            QueryEntityType queryEntityType = expected.Type.ElementType as QueryEntityType;

            if (queryEntityType != null)
            {
                List <QueryProperty> keyProperties = queryEntityType.Properties.Where(p => p.IsPrimaryKey).ToList();
                ExceptionUtilities.Assert(queryEntityType.Properties.Where(p => p.IsPrimaryKey).Count() > 0, "QueryEntityType '{0}' must have a Primary key defined in order to compare using primary keys", queryEntityType.EntityType.FullName);

                List <object> unprocessableElements = actualElements.Where(e => !queryEntityType.ClrType.IsAssignableFrom(e.GetType())).ToList();
                if (unprocessableElements.Count > 0)
                {
                    string errorDetails = string.Join(", ", unprocessableElements.Select(o => string.Format(CultureInfo.InvariantCulture, "Expected object '{0}' to be assignable to '{1}'", o.GetType().FullName, queryEntityType.ClrType.FullName)).ToArray());
                    this.ThrowOrLogError(shouldThrow, "There are '{0}' elements that are unprocessable, Details '{1}'", unprocessableElements.Count, errorDetails);
                    return(ComparisonResult.Failure);
                }

                List <object> unprocessedElements = actualElements.Where(e => queryEntityType.ClrType.IsAssignableFrom(e.GetType())).ToList();
                foreach (QueryStructuralValue queryStructuralValue in expected.Elements)
                {
                    var queryEntityValue = queryStructuralValue as QueryEntityValue;
                    ExceptionUtilities.CheckObjectNotNull(queryEntityValue, "Expected Collection element to be a 'QueryEntityValue', got a '{0}' instead", queryStructuralValue);
                    ExceptionUtilities.CheckObjectNotNull(queryEntityValue.IsNull, "Expected Collection of QueryEntityValues to not contain a QueryEntityValue that is null '{0}'", queryEntityValue.ToString());

                    // Note: We could fix this limitation by simply using the normal unsorted code path instead in these cases but no point implementing this unless its needed.
                    ExceptionUtilities.CheckObjectNotNull(keyProperties.Select(kp => queryEntityValue.GetScalarValue(kp.Name).IsNull).Any(), "Error: QueryEntityValue must have key values defined");

                    QueryKeyStructuralValue key = queryEntityValue.Key();
                    object entityInstance       = unprocessedElements.Where(upe => queryEntityType.GetEntityInstanceKey(upe).Equals(key)).SingleOrDefault();
                    if (entityInstance == null)
                    {
                        this.ThrowOrLogError(shouldThrow, "Cannot find actual EntityInstance that is assignable to type '{0}' with key '{1}' in expected collection '{2}'", queryEntityType.EntityType.FullName, key.GetDebugKeyString(), expected);
                        return(ComparisonResult.Failure);
                    }
                    else
                    {
                        unprocessedElements.Remove(entityInstance);
                        string           childPath   = string.Format(CultureInfo.InvariantCulture, "{0}.CollectionItem(ElementType='{1}',Key='{2}')", path, queryEntityType.EntityType.FullName, key.GetDebugKeyString());
                        ComparisonResult innerResult = this.Compare(queryEntityValue, entityInstance, childPath, false);

                        if (innerResult == ComparisonResult.Failure)
                        {
                            this.DisplayLog();
                            this.ThrowOrLogError(shouldThrow, "Comparision of EntityInstance in path '{0}' failed, see log for details", childPath, key.GetDebugKeyString(), expected);
                            return(ComparisonResult.Failure);
                        }
                    }
                }

                if (unprocessedElements.Count > 0)
                {
                    string errorDetails = string.Join(", ", unprocessedElements.Select(o => string.Format(CultureInfo.InvariantCulture, "Actual EntityInstance of type '{0}' with key '{1}' is not contained in expected collection '{2}'", o.GetType().FullName, queryEntityType.GetEntityInstanceKey(o).GetDebugKeyString(), expected)).ToArray());
                    this.ThrowOrLogError(shouldThrow, "There are '{0}' elements that are unprocessable, Details '{1}'", unprocessedElements.Count, errorDetails);
                    return(ComparisonResult.Failure);
                }

                return(ComparisonResult.Success);
            }
            else
            {
                return(base.CompareCollections(expected, actualElements, path, shouldThrow, out comparisonSkippedForAnyElement));
            }
        }