diff --git a/JsonApiDotnetCore.sln b/JsonApiDotnetCore.sln index 76349968a2..a0330ce005 100644 --- a/JsonApiDotnetCore.sln +++ b/JsonApiDotnetCore.sln @@ -10,7 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7A2B7ADD-ECB EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCoreExampleTests", "test\JsonApiDotNetCoreExampleTests\JsonApiDotNetCoreExampleTests.csproj", "{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCoreExampleTests", "test\JsonApiDotNetCoreExampleTests\JsonApiDotNetCoreExampleTests.csproj", "{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C5B4D998-CECB-454D-9F32-085A897577BE}" ProjectSection(SolutionItems) = preProject diff --git a/benchmarks/Serialization/JsonApiDeserializer_Benchmarks.cs b/benchmarks/Serialization/JsonApiDeserializer_Benchmarks.cs index d65f0e65c1..983bc07f90 100644 --- a/benchmarks/Serialization/JsonApiDeserializer_Benchmarks.cs +++ b/benchmarks/Serialization/JsonApiDeserializer_Benchmarks.cs @@ -45,9 +45,8 @@ public JsonApiDeserializer_Benchmarks() { jsonApiOptions.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions); - var genericProcessorFactoryMock = new Mock(); - _jsonApiDeSerializer = new JsonApiDeSerializer(jsonApiContextMock.Object, genericProcessorFactoryMock.Object); + _jsonApiDeSerializer = new JsonApiDeSerializer(jsonApiContextMock.Object); } [Benchmark] diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoCollectionsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoCollectionsController.cs index 6bac2ff6a0..6a27038191 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoCollectionsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoCollectionsController.cs @@ -33,7 +33,7 @@ public override async Task PatchAsync(Guid id, [FromBody] TodoIte if (entity.Name == "PRE-ATTACH-TEST") { var targetTodoId = entity.TodoItems.First().Id; - var todoItemContext = _dbResolver.GetDbSet(); + var todoItemContext = _dbResolver.GetContext().Set(); await todoItemContext.Where(ti => ti.Id == targetTodoId).FirstOrDefaultAsync(); } return await base.PatchAsync(id, entity); diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs index b1a86bf4b8..457468c484 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs @@ -22,7 +22,7 @@ public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = fal } } - public override void BeforeImplicitUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) + public override void BeforeImplicitUpdateRelationship(IRelationshipsDictionary resourcesByRelationship, ResourcePipeline pipeline) { resourcesByRelationship.GetByRelationship().ToList().ForEach(kvp => DoesNotTouchLockedPassports(kvp.Value)); } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs index 50e9efb54c..032c9acb4b 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs @@ -10,7 +10,7 @@ public class PersonResource : LockableResourceBase { public PersonResource(IResourceGraph graph) : base(graph) { } - public override IEnumerable BeforeUpdateRelationship(HashSet ids, IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) + public override IEnumerable BeforeUpdateRelationship(HashSet ids, IRelationshipsDictionary resourcesByRelationship, ResourcePipeline pipeline) { BeforeImplicitUpdateRelationship(resourcesByRelationship, pipeline); return ids; @@ -22,7 +22,7 @@ public override IEnumerable BeforeUpdateRelationship(HashSet ids // return entityDiff.Entities; //} - public override void BeforeImplicitUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) + public override void BeforeImplicitUpdateRelationship(IRelationshipsDictionary resourcesByRelationship, ResourcePipeline pipeline) { resourcesByRelationship.GetByRelationship().ToList().ForEach(kvp => DisallowLocked(kvp.Value)); } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs index 99769feade..e3b3100ddd 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -13,6 +13,11 @@ public TagResource(IResourceGraph graph) : base(graph) { } + public override IEnumerable BeforeCreate(IEntityHashSet affected, ResourcePipeline pipeline) + { + return base.BeforeCreate(affected, pipeline); + } + public override IEnumerable OnReturn(HashSet entities, ResourcePipeline pipeline) { return entities.Where(t => t.Name != "This should be not be included"); diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs index 1de3a02a6a..02a7ba6f15 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs @@ -19,7 +19,7 @@ public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = fal } } - public override void BeforeImplicitUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) + public override void BeforeImplicitUpdateRelationship(IRelationshipsDictionary resourcesByRelationship, ResourcePipeline pipeline) { List todos = resourcesByRelationship.GetByRelationship().SelectMany(kvp => kvp.Value).ToList(); DisallowLocked(todos); diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index 906ee4fba8..21efdb97ed 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -181,12 +181,6 @@ public class JsonApiOptions /// public bool ValidateModelState { get; set; } - [Obsolete("JsonContract resolver can now be set on SerializerSettings.")] - public IContractResolver JsonContractResolver - { - get => SerializerSettings.ContractResolver; - set => SerializerSettings.ContractResolver = value; - } public JsonSerializerSettings SerializerSettings { get; } = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, diff --git a/src/JsonApiDotNetCore/Data/DbContextResolver.cs b/src/JsonApiDotNetCore/Data/DbContextResolver.cs index e681e660bb..7ce7eec921 100644 --- a/src/JsonApiDotNetCore/Data/DbContextResolver.cs +++ b/src/JsonApiDotNetCore/Data/DbContextResolver.cs @@ -15,7 +15,7 @@ public DbContextResolver(TContext context) public DbContext GetContext() => _context; - public DbSet GetDbSet() where TEntity : class - => _context.GetDbSet(); + public DbSet GetDbSet() where TEntity : class => null; + } } diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index e23b4a316d..889e2f7198 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -56,7 +56,7 @@ public DefaultEntityRepository( ResourceDefinition resourceDefinition = null) { _context = contextResolver.GetContext(); - _dbSet = contextResolver.GetDbSet(); + _dbSet = _context.Set(); _jsonApiContext = jsonApiContext; _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; _resourceDefinition = resourceDefinition; @@ -69,7 +69,7 @@ public DefaultEntityRepository( ResourceDefinition resourceDefinition = null) { _context = contextResolver.GetContext(); - _dbSet = contextResolver.GetDbSet(); + _dbSet = _context.Set(); _jsonApiContext = jsonApiContext; _logger = loggerFactory.CreateLogger>(); _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory; diff --git a/src/JsonApiDotNetCore/Data/IDbContextResolver.cs b/src/JsonApiDotNetCore/Data/IDbContextResolver.cs index 5a8c5a2e12..4915d788c4 100644 --- a/src/JsonApiDotNetCore/Data/IDbContextResolver.cs +++ b/src/JsonApiDotNetCore/Data/IDbContextResolver.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.EntityFrameworkCore; namespace JsonApiDotNetCore.Data @@ -5,6 +6,8 @@ namespace JsonApiDotNetCore.Data public interface IDbContextResolver { DbContext GetContext(); + + [Obsolete("Use DbContext.Set() instead", error: true)] DbSet GetDbSet() where TEntity : class; } diff --git a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs index 8407ede0d3..87de9d187d 100644 --- a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs @@ -30,15 +30,6 @@ private static MethodInfo ContainsMethod } } - [Obsolete("Use overload Sort(IJsonApiContext, List) instead.", error: true)] - public static IQueryable Sort(this IQueryable source, List sortQueries) => null; - - [Obsolete("Use overload Sort(IJsonApiContext, SortQuery) instead.", error: true)] - public static IOrderedQueryable Sort(this IQueryable source, SortQuery sortQuery) => null; - - [Obsolete("Use overload Sort(IJsonApiContext, SortQuery) instead.", error: true)] - public static IOrderedQueryable Sort(this IOrderedQueryable source, SortQuery sortQuery) => null; - public static IQueryable Sort(this IQueryable source, IJsonApiContext jsonApiContext, List sortQueries) { if (sortQueries == null || sortQueries.Count == 0) diff --git a/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs b/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs index 4b31a7f021..023ef09ae2 100644 --- a/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs @@ -7,26 +7,6 @@ namespace JsonApiDotNetCore.Extensions { public static class ModelStateExtensions { - [Obsolete("Use Generic Method ConvertToErrorCollection(IResourceGraph resourceGraph) instead for full validation errors")] - public static ErrorCollection ConvertToErrorCollection(this ModelStateDictionary modelState) - { - ErrorCollection collection = new ErrorCollection(); - foreach (var entry in modelState) - { - if (entry.Value.Errors.Any() == false) - continue; - - foreach (var modelError in entry.Value.Errors) - { - if (modelError.Exception is JsonApiException jex) - collection.Errors.AddRange(jex.GetError().Errors); - else - collection.Errors.Add(new Error(400, entry.Key, modelError.ErrorMessage, modelError.Exception != null ? ErrorMeta.FromException(modelError.Exception) : null)); - } - } - - return collection; - } public static ErrorCollection ConvertToErrorCollection(this ModelStateDictionary modelState, IResourceGraph resourceGraph) { ErrorCollection collection = new ErrorCollection(); diff --git a/src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs b/src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs deleted file mode 100644 index dc2590f9bd..0000000000 --- a/src/JsonApiDotNetCore/Hooks/Execution/AffectedRelationships.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Hooks -{ - public interface IAffectedRelationships { } - - /// - /// A helper class that provides insights in which relationships have been updated for which entities. - /// - public interface IAffectedRelationships : IAffectedRelationships where TDependent : class, IIdentifiable - { - /// - /// Gets a dictionary of all entities grouped by affected relationship. - /// - Dictionary> AllByRelationships(); - - /// - /// Gets a dictionary of all entities that have an affected relationship to type - /// - Dictionary> GetByRelationship() where TPrincipal : class, IIdentifiable; - /// - /// Gets a dictionary of all entities that have an affected relationship to type - /// - Dictionary> GetByRelationship(Type principalType); - } - - public class AffectedRelationships : IAffectedRelationships where TDependent : class, IIdentifiable - { - private readonly Dictionary> _groups; - - public Dictionary> AllByRelationships() - { - return _groups?.ToDictionary(p => p.Key.Attribute, p => p.Value); - } - internal AffectedRelationships(Dictionary relationships) - { - _groups = relationships.ToDictionary(kvp => kvp.Key, kvp => new HashSet((IEnumerable)kvp.Value)); - } - - public Dictionary> GetByRelationship() where TPrincipal : class, IIdentifiable - { - return GetByRelationship(typeof(TPrincipal)); - } - - public Dictionary> GetByRelationship(Type principalType) - { - return _groups?.Where(p => p.Key.PrincipalType == principalType).ToDictionary(p => p.Key.Attribute, p => p.Value); - } - } -} diff --git a/src/JsonApiDotNetCore/Hooks/Execution/AffectedResourceDiff.cs b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResourceDiff.cs deleted file mode 100644 index 720f7853b2..0000000000 --- a/src/JsonApiDotNetCore/Hooks/Execution/AffectedResourceDiff.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Hooks -{ - /// - /// A helper class that provides insight in what is to be updated. The - /// property reflects what was parsed from the incoming request, - /// where the reflects what is the current state in the database. - /// - /// Any relationships that are updated can be retrieved via the methods implemented on - /// . - /// - public interface IAffectedResourcesDiff : IAffectedResources where TEntity : class, IIdentifiable - { - HashSet DatabaseValues { get; } - IEnumerable> GetDiff(); - } - - public class ResourceDiff : AffectedResources, IAffectedResourcesDiff where TEntity : class, IIdentifiable - { - - private readonly HashSet _databaseValues; - private readonly bool _databaseValuesLoaded; - - /// - /// the current database values of the affected resources collection. - /// - public HashSet DatabaseValues { get => _databaseValues ?? ThrowNoDbValuesError(); } - - public ResourceDiff(IEnumerable requestEntities, - IEnumerable databaseEntities, - Dictionary relationships) : base(requestEntities, relationships) - { - _databaseValues = (HashSet)databaseEntities; - _databaseValuesLoaded |= _databaseValues != null; - } - - public IEnumerable> GetDiff() - { - foreach (var entity in Entities) - { - TEntity currentValueInDatabase = null; - if (_databaseValuesLoaded) currentValueInDatabase = _databaseValues.Single(e => entity.StringId == e.StringId); - yield return new ResourceDiffPair(entity, currentValueInDatabase); - } - } - - private HashSet ThrowNoDbValuesError() - { - throw new MemberAccessException("Cannot access database entities if the LoadDatabaseValues option is set to false"); - } - } - - public class ResourceDiffPair where TEntity : class, IIdentifiable - { - public ResourceDiffPair(TEntity entity, TEntity databaseValue) - { - Entity = entity; - DatabaseValue = databaseValue; - } - - public TEntity Entity { get; private set; } - public TEntity DatabaseValue { get; private set; } - } -} diff --git a/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs b/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs deleted file mode 100644 index a004dcc244..0000000000 --- a/src/JsonApiDotNetCore/Hooks/Execution/AffectedResources.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using JsonApiDotNetCore.Models; -using System.Linq; -using System.Collections; - -namespace JsonApiDotNetCore.Hooks -{ - - public interface IAffectedResources : IEnumerable where TEntity : class, IIdentifiable - { - HashSet Entities { get; } - } - - public class AffectedResources : AffectedRelationships, IAffectedResources where TEntity : class, IIdentifiable - { - /// - /// The entities that are affected by the request. - /// - public HashSet Entities { get; } - - internal AffectedResources(IEnumerable entities, - Dictionary relationships) : base(relationships) - { - Entities = new HashSet(entities.Cast()); - } - public IEnumerator GetEnumerator() - { - return Entities.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - -} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityDiffs.cs b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiffs.cs new file mode 100644 index 0000000000..f0379636b5 --- /dev/null +++ b/src/JsonApiDotNetCore/Hooks/Execution/EntityDiffs.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Hooks +{ + /// + /// A wrapper class that contains information about the resources that are updated by the request. + /// Contains the resources from the request and the corresponding database values. + /// + /// Also contains information about updated relationships through + /// implementation of IRelationshipsDictionary> + /// + public interface IEntityDiff : IRelationshipsDictionary, IEnumerable> where TEntity : class, IIdentifiable + { + /// + /// The database values of the resources affected by the request. + /// + HashSet DatabaseValues { get; } + + /// + /// The resources that were affected by the request. + /// + HashSet Entities { get; } + } + + /// + public class EntityDiffs : IEntityDiff where TEntity : class, IIdentifiable + { + /// + public HashSet DatabaseValues { get => _databaseValues ?? ThrowNoDbValuesError(); } + private readonly HashSet _databaseValues; + private readonly bool _databaseValuesLoaded; + + /// + public HashSet Entities { get; private set; } + /// + public RelationshipsDictionary AffectedRelationships { get; private set; } + + public EntityDiffs(HashSet requestEntities, + HashSet databaseEntities, + Dictionary> relationships) + { + Entities = requestEntities; + AffectedRelationships = new RelationshipsDictionary(relationships); + _databaseValues = databaseEntities; + _databaseValuesLoaded |= _databaseValues != null; + } + + /// + /// Used internally by the ResourceHookExecutor to make live a bit easier with generics + /// + internal EntityDiffs(IEnumerable requestEntities, + IEnumerable databaseEntities, + Dictionary relationships) + : this((HashSet)requestEntities, (HashSet)databaseEntities, TypeHelper.ConvertRelationshipDictionary(relationships)) { } + + + /// + public Dictionary> GetByRelationship() where TPrincipalResource : class, IIdentifiable + { + return GetByRelationship(typeof(TPrincipalResource)); + } + + /// + public Dictionary> GetByRelationship(Type principalType) + { + return AffectedRelationships.GetByRelationship(principalType); + } + + /// + public IEnumerator> GetEnumerator() + { + if (!_databaseValuesLoaded) ThrowNoDbValuesError(); + + foreach (var entity in Entities) + { + TEntity currentValueInDatabase = null; + currentValueInDatabase = _databaseValues.Single(e => entity.StringId == e.StringId); + yield return new EntityDiffPair(entity, currentValueInDatabase); + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private HashSet ThrowNoDbValuesError() + { + throw new MemberAccessException("Cannot access database entities if the LoadDatabaseValues option is set to false"); + } + } + + /// + /// A wrapper that contains an entity that is affected by the request, + /// matched to its current database value + /// + public class EntityDiffPair where TEntity : class, IIdentifiable + { + public EntityDiffPair(TEntity entity, TEntity databaseValue) + { + Entity = entity; + DatabaseValue = databaseValue; + } + + /// + /// The resource from the request matching the resource from the database. + /// + public TEntity Entity { get; private set; } + /// + /// The resource from the database matching the resource from the request. + /// + public TEntity DatabaseValue { get; private set; } + } +} diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs b/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs new file mode 100644 index 0000000000..98d7393796 --- /dev/null +++ b/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using JsonApiDotNetCore.Models; +using System.Collections; +using JsonApiDotNetCore.Internal; +using System; + +namespace JsonApiDotNetCore.Hooks +{ + /// + /// Basically a enumerable of of resources that were affected by the request. + /// + /// Also contains information about updated relationships through + /// implementation of IAffectedRelationshipsDictionary> + /// + public interface IEntityHashSet : IRelationshipsDictionary, IEnumerable where TResource : class, IIdentifiable { } + + /// + /// Implementation of IResourceHashSet{TResource}. + /// + /// Basically a enumerable of of resources that were affected by the request. + /// + /// Also contains information about updated relationships through + /// implementation of IRelationshipsDictionary> + /// + public class EntityHashSet : HashSet, IEntityHashSet where TResource : class, IIdentifiable + { + /// + public RelationshipsDictionary AffectedRelationships { get; private set; } + + public EntityHashSet(HashSet entities, + Dictionary> relationships) : base(entities) + { + AffectedRelationships = new RelationshipsDictionary(relationships); + } + + /// + /// Used internally by the ResourceHookExecutor to make live a bit easier with generics + /// + internal EntityHashSet(IEnumerable entities, + Dictionary relationships) + : this((HashSet)entities, TypeHelper.ConvertRelationshipDictionary(relationships)) { } + + + /// + public Dictionary> GetByRelationship(Type principalType) + { + return AffectedRelationships.GetByRelationship(principalType); + } + + /// + public Dictionary> GetByRelationship() where TPrincipalResource : class, IIdentifiable + { + return GetByRelationship(); + } + } +} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index 68b8018c01..6a2c2767bf 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -24,7 +24,6 @@ internal class HookExecutorHelper : IHookExecutorHelper protected readonly Dictionary _hookDiscoveries; protected readonly List _targetedHooksForRelatedEntities; protected readonly IJsonApiContext _context; - protected Dictionary> _meta; public HookExecutorHelper( IGenericProcessorFactory genericProcessorFactory, @@ -35,7 +34,6 @@ IJsonApiContext context _genericProcessorFactory = genericProcessorFactory; _graph = graph; _context = context; - _meta = new Dictionary>(); _hookContainers = new Dictionary(); _hookDiscoveries = new Dictionary(); _targetedHooksForRelatedEntities = new List(); @@ -81,9 +79,9 @@ public IResourceHookContainer GetResourceHookContainer(Resourc return (IResourceHookContainer)GetResourceHookContainer(typeof(TEntity), hook); } - public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships) + public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] relationships) { - var paths = relationships.Select(p => p.Attribute.RelationshipPath).ToArray(); + var paths = relationships.Select(p => p.RelationshipPath).ToArray(); var idType = GetIdentifierType(entityTypeForRepository); var parameterizedGetWhere = GetType() .GetMethod(nameof(GetWhereAndInclude), BindingFlags.NonPublic | BindingFlags.Instance) @@ -95,7 +93,7 @@ public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerab return (IEnumerable)Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(entityTypeForRepository), values.Cast(entityTypeForRepository)); } - public HashSet LoadDbValues(IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships) where TEntity : class, IIdentifiable + public HashSet LoadDbValues(IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] relationships) where TEntity : class, IIdentifiable { var entityType = typeof(TEntity); var dbValues = LoadDbValues(entityType, entities, hook, relationships)?.Cast(); @@ -168,11 +166,11 @@ IEntityReadRepository GetRepository() where TEntity } - public Dictionary LoadImplicitlyAffected( - Dictionary principalEntitiesByRelation, + public Dictionary LoadImplicitlyAffected( + Dictionary principalEntitiesByRelation, IEnumerable existingDependentEntities = null) { - var implicitlyAffected = new Dictionary(); + var implicitlyAffected = new Dictionary(); foreach (var kvp in principalEntitiesByRelation) { if (IsHasManyThrough(kvp, out var principals, out var relationship)) continue; @@ -208,17 +206,22 @@ public Dictionary LoadImplicitlyAffected( } } - return implicitlyAffected.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + return implicitlyAffected.ToDictionary(kvp => kvp.Key, kvp => TypeHelper.CreateHashSetFor(kvp.Key.DependentType, kvp.Value)); } - bool IsHasManyThrough(KeyValuePair kvp, + private IEnumerable CreateHashSet(Type type, IList elements) + { + return (IEnumerable)Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(type), new object[] { elements }); + } + + bool IsHasManyThrough(KeyValuePair kvp, out IEnumerable entities, - out RelationshipProxy proxy) + out RelationshipAttribute attr) { - proxy = kvp.Key; + attr = kvp.Key; entities = (kvp.Value); - return (kvp.Key.Attribute is HasManyThroughAttribute); + return (kvp.Key is HasManyThroughAttribute); } } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs index 33546aa864..63b3621a34 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs @@ -37,12 +37,12 @@ internal interface IHookExecutorHelper /// Load the implicitly affected entities from the database for a given set of target target entities and involved relationships /// /// The implicitly affected entities by relationship - Dictionary LoadImplicitlyAffected(Dictionary principalEntities, IEnumerable existingDependentEntities = null); + Dictionary LoadImplicitlyAffected(Dictionary principalEntities, IEnumerable existingDependentEntities = null); /// /// For a set of entities, loads current values from the database /// - IEnumerable LoadDbValues(Type repositoryEntityType, IEnumerable entities, ResourceHook hook, params RelationshipProxy[] relationships); + IEnumerable LoadDbValues(Type repositoryEntityType, IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] relationships); bool ShouldLoadDbValues(Type containerEntityType, ResourceHook hook); } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs b/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs new file mode 100644 index 0000000000..77248f5169 --- /dev/null +++ b/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Hooks +{ + public interface IRelationshipsDictionary { } + + /// + /// An interface that is implemented to expose a relationship dictionary on another class. + /// + public interface IExposeRelationshipsDictionary : IRelationshipsDictionary where TDependentResource : class, IIdentifiable + { + /// + /// Gets a dictionary of affected resources grouped by affected relationships. + /// + RelationshipsDictionary AffectedRelationships { get; } + } + + /// + /// A helper class that provides insights in which relationships have been updated for which entities. + /// + public interface IRelationshipsDictionary : IRelationshipsDictionary where TDependentResource : class, IIdentifiable + { + /// + /// Gets a dictionary of all entities that have an affected relationship to type + /// + Dictionary> GetByRelationship() where TPrincipalResource : class, IIdentifiable; + /// + /// Gets a dictionary of all entities that have an affected relationship to type + /// + Dictionary> GetByRelationship(Type principalType); + } + + /// + /// Implementation of IAffectedRelationships{TDependentResource} + /// + /// It is practically a ReadOnlyDictionary{RelationshipAttribute, HashSet{TDependentResource}} dictionary + /// with the two helper methods defined on IAffectedRelationships{TDependentResource}. + /// + public class RelationshipsDictionary : ReadOnlyDictionary>, IRelationshipsDictionary where TDependentResource : class, IIdentifiable + { + /// + /// a dictionary with affected relationships as keys and values being the corresponding resources + /// that were affected + /// + private readonly Dictionary> _groups; + + /// + public RelationshipsDictionary(Dictionary> relationships) : base(relationships) + { + _groups = relationships; + } + + /// + /// Used internally by the ResourceHookExecutor to make live a bit easier with generics + /// + internal RelationshipsDictionary(Dictionary relationships) + : this(TypeHelper.ConvertRelationshipDictionary(relationships)) { } + + + /// + public Dictionary> GetByRelationship() where TPrincipalResource : class, IIdentifiable + { + return GetByRelationship(typeof(TPrincipalResource)); + } + + /// + public Dictionary> GetByRelationship(Type principalType) + { + return this.Where(p => p.Key.PrincipalType == principalType).ToDictionary(p => p.Key, p => p.Value); + } + } +} diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs index 7597a02d6e..cadb96a4fa 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs @@ -12,72 +12,16 @@ public interface IResourceHookContainer { } /// /// Implement this interface to implement business logic hooks on . /// - public interface IResourceHookContainer : IBeforeHooks, IAfterHooks, IOnHooks, IResourceHookContainer where TEntity : class, IIdentifiable { } - - /// - /// Wrapper interface for all After hooks. - /// - public interface IAfterHooks where TEntity : class, IIdentifiable - { - /// - /// Implement this hook to run custom logic in the - /// layer just after creation of entities of type . - /// - /// If relationships were created with the created entities, this will - /// be reflected by the corresponding NavigationProperty being set. - /// For each of these relationships, the - /// hook is fired after the execution of this hook. - /// - /// The transformed entity set - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - void AfterCreate(HashSet entities, ResourcePipeline pipeline); - /// - /// Implement this hook to run custom logic in the - /// layer just after reading entities of type . - /// - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - /// A boolean to indicate whether the entities in this hook execution are the main entities of the request, - /// or if they were included as a relationship - void AfterRead(HashSet entities, ResourcePipeline pipeline, bool isIncluded = false); - /// - /// Implement this hook to run custom logic in the - /// layer just after updating entities of type . - /// - /// If relationships were updated with the updated entities, this will - /// be reflected by the corresponding NavigationProperty being set. - /// For each of these relationships, the - /// hook is fired after the execution of this hook. - /// - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - void AfterUpdate(HashSet entities, ResourcePipeline pipeline); - /// - /// Implement this hook to run custom logic in the - /// layer just after deletion of entities of type . - /// - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - /// If set to true if the deletion was succeeded in the repository layer. - void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded); - /// - /// Implement this hook to run custom logic in the layer - /// just after a relationship was updated. - /// - /// Relationship helper. - /// An enum indicating from where the hook was triggered. - void AfterUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline); - } + public interface IResourceHookContainer : IBeforeHooks, IAfterHooks, IOnHooks, IResourceHookContainer where TResource : class, IIdentifiable { } /// /// Wrapper interface for all Before hooks. /// - public interface IBeforeHooks where TEntity : class, IIdentifiable + public interface IBeforeHooks where TResource : class, IIdentifiable { /// /// Implement this hook to run custom logic in the - /// layer just before creation of entities of type . + /// layer just before creation of entities of type . /// /// For the pipeline, /// will typically contain one entry. For , @@ -96,10 +40,10 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// The transformed entity set /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. - IEnumerable BeforeCreate(IAffectedResources entities, ResourcePipeline pipeline); + IEnumerable BeforeCreate(IEntityHashSet entities, ResourcePipeline pipeline); /// /// Implement this hook to run custom logic in the - /// layer just before reading entities of type . + /// layer just before reading entities of type . /// /// An enum indicating from where the hook was triggered. /// Indicates whether the to be queried entities are the main request entities or if they were included @@ -107,15 +51,15 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null); /// /// Implement this hook to run custom logic in the - /// layer just before updating entities of type . + /// layer just before updating entities of type . /// /// For the pipeline, the - /// will typically contain one entity. + /// will typically contain one entity. /// For , this it may contain /// multiple entities. /// /// The returned may be a subset - /// of the property in parameter , + /// of the property in parameter , /// in which case the operation of the pipeline will not be executed /// for the omitted entities. The returned set may also contain custom /// changes of the properties on the entities. @@ -131,12 +75,13 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// hook is fired for these. /// /// The transformed entity set - /// The entity diff. + /// The entity diff. /// An enum indicating from where the hook was triggered. - IEnumerable BeforeUpdate(IAffectedResourcesDiff ResourceDiff, ResourcePipeline pipeline); + IEnumerable BeforeUpdate(IEntityDiff entityDiff, ResourcePipeline pipeline); + /// /// Implement this hook to run custom logic in the - /// layer just before deleting entities of type . + /// layer just before deleting entities of type . /// /// For the pipeline, /// will typically contain one entity. @@ -155,13 +100,13 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// The transformed entity set /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. - IEnumerable BeforeDelete(IAffectedResources entities, ResourcePipeline pipeline); + IEnumerable BeforeDelete(IEntityHashSet entities, ResourcePipeline pipeline); /// /// Implement this hook to run custom logic in the - /// layer just before updating relationships to entities of type . + /// layer just before updating relationships to entities of type . /// /// This hook is fired when a relationship is created to entities of type - /// from a dependent pipeline ( + /// from a dependent pipeline ( /// or ). For example, If an Article was created /// and its author relationship was set to an existing Person, this hook will be fired /// for that particular Person. @@ -174,14 +119,14 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// The transformed set of ids /// The unique set of ids /// An enum indicating from where the hook was triggered. - /// A helper that groups the entities by the affected relationship - IEnumerable BeforeUpdateRelationship(HashSet ids, IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline); + /// A helper that groups the entities by the affected relationship + IEnumerable BeforeUpdateRelationship(HashSet ids, IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); /// /// Implement this hook to run custom logic in the - /// layer just before implicitly updating relationships to entities of type . + /// layer just before implicitly updating relationships to entities of type . /// /// This hook is fired when a relationship to entities of type - /// is implicitly affected from a dependent pipeline ( + /// is implicitly affected from a dependent pipeline ( /// or ). For example, if an Article was updated /// by setting its author relationship (one-to-one) to an existing Person, /// and by this the relationship to a different Person was implicitly removed, @@ -192,19 +137,75 @@ public interface IBeforeHooks where TEntity : class, IIdentifiable /// /// /// The transformed set of ids - /// A helper that groups the entities by the affected relationship + /// A helper that groups the entities by the affected relationship + /// An enum indicating from where the hook was triggered. + void BeforeImplicitUpdateRelationship(IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); + } + + /// + /// Wrapper interface for all After hooks. + /// + public interface IAfterHooks where TResource : class, IIdentifiable + { + /// + /// Implement this hook to run custom logic in the + /// layer just after creation of entities of type . + /// + /// If relationships were created with the created entities, this will + /// be reflected by the corresponding NavigationProperty being set. + /// For each of these relationships, the + /// hook is fired after the execution of this hook. + /// + /// The transformed entity set + /// The unique set of affected entities. + /// An enum indicating from where the hook was triggered. + void AfterCreate(HashSet entities, ResourcePipeline pipeline); + /// + /// Implement this hook to run custom logic in the + /// layer just after reading entities of type . + /// + /// The unique set of affected entities. + /// An enum indicating from where the hook was triggered. + /// A boolean to indicate whether the entities in this hook execution are the main entities of the request, + /// or if they were included as a relationship + void AfterRead(HashSet entities, ResourcePipeline pipeline, bool isIncluded = false); + /// + /// Implement this hook to run custom logic in the + /// layer just after updating entities of type . + /// + /// If relationships were updated with the updated entities, this will + /// be reflected by the corresponding NavigationProperty being set. + /// For each of these relationships, the + /// hook is fired after the execution of this hook. + /// + /// The unique set of affected entities. + /// An enum indicating from where the hook was triggered. + void AfterUpdate(HashSet entities, ResourcePipeline pipeline); + /// + /// Implement this hook to run custom logic in the + /// layer just after deletion of entities of type . + /// + /// The unique set of affected entities. + /// An enum indicating from where the hook was triggered. + /// If set to true if the deletion was succeeded in the repository layer. + void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded); + /// + /// Implement this hook to run custom logic in the layer + /// just after a relationship was updated. + /// + /// Relationship helper. /// An enum indicating from where the hook was triggered. - void BeforeImplicitUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline); + void AfterUpdateRelationship(IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); } /// /// Wrapper interface for all on hooks. /// - public interface IOnHooks where TEntity : class, IIdentifiable + public interface IOnHooks where TResource : class, IIdentifiable { /// /// Implement this hook to transform the result data just before returning - /// the entities of type from the + /// the entities of type from the /// layer /// /// The returned may be a subset @@ -215,6 +216,6 @@ public interface IOnHooks where TEntity : class, IIdentifiable /// The transformed entity set /// The unique set of affected entities /// An enum indicating from where the hook was triggered. - IEnumerable OnReturn(HashSet entities, ResourcePipeline pipeline); + IEnumerable OnReturn(HashSet entities, ResourcePipeline pipeline); } } diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs index 75c2a35f2e..2d4f1fbdb7 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs @@ -26,7 +26,7 @@ public interface IBeforeExecutor /// The returned set will be used in the actual operation in . /// /// Fires the - /// hook where T = for values in parameter . + /// hook where T = for values in parameter . /// /// Fires the /// hook for any related (nested) entity for values within parameter @@ -34,26 +34,26 @@ public interface IBeforeExecutor /// The transformed set /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - IEnumerable BeforeCreate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable; + /// The type of the root entities + IEnumerable BeforeCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. /// /// Fires the - /// hook where T = for the requested + /// hook where T = for the requested /// entities as well as any related relationship. /// /// An enum indicating from where the hook was triggered. /// StringId of the requested entity in the case of /// . - /// The type of the request entity - void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TEntity : class, IIdentifiable; + /// The type of the request entity + void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TResource : class, IIdentifiable; /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. /// The returned set will be used in the actual operation in . /// /// Fires the - /// hook where T = for values in parameter . + /// hook where T = for values in parameter . /// /// Fires the /// hook for any related (nested) entity for values within parameter @@ -67,14 +67,14 @@ public interface IBeforeExecutor /// The transformed set /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable; + /// The type of the root entities + IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. /// The returned set will be used in the actual operation in . /// /// Fires the - /// hook where T = for values in parameter . + /// hook where T = for values in parameter . /// /// Fires the /// hook for any entities that are indirectly (implicitly) affected by this operation. @@ -84,8 +84,8 @@ public interface IBeforeExecutor /// The transformed set /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - IEnumerable BeforeDelete(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable; + /// The type of the root entities + IEnumerable BeforeDelete(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; } /// @@ -97,15 +97,15 @@ public interface IAfterExecutor /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// /// Fires the - /// hook where T = for values in parameter . + /// hook where T = for values in parameter . /// /// Fires the /// hook for any related (nested) entity for values within parameter /// /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable; + /// The type of the root entities + void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// @@ -114,31 +114,31 @@ public interface IAfterExecutor /// /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - void AfterRead(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable; + /// The type of the root entities + void AfterRead(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// /// Fires the - /// hook where T = for values in parameter . + /// hook where T = for values in parameter . /// /// Fires the /// hook for any related (nested) entity for values within parameter /// /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable; + /// The type of the root entities + void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// /// Fires the - /// hook where T = for values in parameter . + /// hook where T = for values in parameter . /// /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TEntity : class, IIdentifiable; + /// The type of the root entities + void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TResource : class, IIdentifiable; } /// @@ -155,7 +155,7 @@ public interface IOnExecutor /// The transformed set /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - IEnumerable OnReturn(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable; + /// The type of the root entities + IEnumerable OnReturn(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index aca190225a..7fccbbba7f 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -47,8 +47,9 @@ public virtual IEnumerable BeforeUpdate(IEnumerable e { if (GetHook(ResourceHook.BeforeUpdate, entities, out var container, out var node)) { - var dbValues = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, node.RelationshipsToNextLayer); - var diff = new ResourceDiff(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer()); + var relationships = node.RelationshipsToNextLayer.Select(p => p.Attribute).ToArray(); + var dbValues = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, relationships); + var diff = new EntityDiffs(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer()); IEnumerable updated = container.BeforeUpdate(diff, pipeline); node.UpdateUnique(updated); node.Reassign(entities); @@ -64,7 +65,7 @@ public virtual IEnumerable BeforeCreate(IEnumerable e { if (GetHook(ResourceHook.BeforeCreate, entities, out var container, out var node)) { - var affected = new AffectedResources((HashSet)node.UniqueEntities, node.PrincipalsToNextLayer()); + var affected = new EntityHashSet((HashSet)node.UniqueEntities, node.PrincipalsToNextLayer()); IEnumerable updated = container.BeforeCreate(affected, pipeline); node.UpdateUnique(updated); node.Reassign(entities); @@ -79,8 +80,9 @@ public virtual IEnumerable BeforeDelete(IEnumerable e { if (GetHook(ResourceHook.BeforeDelete, entities, out var container, out var node)) { - var targetEntities = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, node.RelationshipsToNextLayer) ?? node.UniqueEntities; - var affected = new AffectedResources(targetEntities, node.PrincipalsToNextLayer()); + var relationships = node.RelationshipsToNextLayer.Select(p => p.Attribute).ToArray(); + var targetEntities = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, relationships) ?? node.UniqueEntities; + var affected = new EntityHashSet(targetEntities, node.PrincipalsToNextLayer()); IEnumerable updated = container.BeforeDelete(affected, pipeline); node.UpdateUnique(updated); @@ -251,7 +253,8 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, EntityChildLayer lay { if (uniqueEntities.Cast().Any()) { - var dbValues = LoadDbValues(entityType, uniqueEntities, ResourceHook.BeforeUpdateRelationship, node.RelationshipsToNextLayer); + var relationships = node.RelationshipsToNextLayer.Select(p => p.Attribute).ToArray(); + var dbValues = LoadDbValues(entityType, uniqueEntities, ResourceHook.BeforeUpdateRelationship, relationships); var resourcesByRelationship = CreateRelationshipHelper(entityType, node.RelationshipsFromPreviousLayer.GetDependentEntities(), dbValues); var allowedIds = CallHook(nestedHookcontainer, ResourceHook.BeforeUpdateRelationship, new object[] { GetIds(uniqueEntities), resourcesByRelationship, pipeline }).Cast(); var updated = GetAllowedEntities(uniqueEntities, allowedIds); @@ -281,7 +284,7 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, EntityChildLayer lay /// Given a source of entities, gets the implicitly affected entities /// from the database and calls the BeforeImplicitUpdateRelationship hook. /// - void FireForAffectedImplicits(Type entityTypeToInclude, Dictionary implicitsTarget, ResourcePipeline pipeline, IEnumerable existingImplicitEntities = null) + void FireForAffectedImplicits(Type entityTypeToInclude, Dictionary implicitsTarget, ResourcePipeline pipeline, IEnumerable existingImplicitEntities = null) { var container = _executorHelper.GetResourceHookContainer(entityTypeToInclude, ResourceHook.BeforeImplicitUpdateRelationship); if (container == null) return; @@ -310,10 +313,10 @@ void ValidateHookResponse(IEnumerable returnedList, ResourcePipeline pipel /// NOTE: in JADNC usage, the root layer is ALWAYS homogenous, so we can be sure that for every /// relationship to the previous layer, the principal type is the same. /// - (Dictionary, PrincipalType) GetDependentImplicitsTargets(Dictionary dependentEntities) + (Dictionary, PrincipalType) GetDependentImplicitsTargets(Dictionary dependentEntities) { PrincipalType principalType = dependentEntities.First().Key.PrincipalType; - var byInverseRelationship = dependentEntities.Where(kvp => kvp.Key.Attribute.InverseNavigation != null).ToDictionary(kvp => GetInverseRelationship(kvp.Key), kvp => kvp.Value); + var byInverseRelationship = dependentEntities.Where(kvp => kvp.Key.InverseNavigation != null).ToDictionary(kvp => GetInverseRelationship(kvp.Key), kvp => kvp.Value); return (byInverseRelationship, principalType); } @@ -350,23 +353,24 @@ object ThrowJsonApiExceptionOnError(Func action) /// If are included, the values of the entries in need to be replaced with these values. /// /// The relationship helper. - IAffectedRelationships CreateRelationshipHelper(DependentType entityType, Dictionary prevLayerRelationships, IEnumerable dbValues = null) + IRelationshipsDictionary CreateRelationshipHelper(DependentType entityType, Dictionary prevLayerRelationships, IEnumerable dbValues = null) { - if (dbValues != null) ReplaceWithDbValues(prevLayerRelationships, dbValues.Cast()); - return (IAffectedRelationships)TypeHelper.CreateInstanceOfOpenType(typeof(AffectedRelationships<>), entityType, true, prevLayerRelationships); + if (dbValues != null) prevLayerRelationships = ReplaceWithDbValues(prevLayerRelationships, dbValues.Cast()); + return (IRelationshipsDictionary)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipsDictionary<>), entityType, true, prevLayerRelationships); } /// /// Replaces the entities in the values of the prevLayerRelationships dictionary /// with the corresponding entities loaded from the db. /// - void ReplaceWithDbValues(Dictionary prevLayerRelationships, IEnumerable dbValues) + Dictionary ReplaceWithDbValues(Dictionary prevLayerRelationships, IEnumerable dbValues) { foreach (var key in prevLayerRelationships.Keys.ToList()) { var replaced = prevLayerRelationships[key].Cast().Select(entity => dbValues.Single(dbEntity => dbEntity.StringId == entity.StringId)).Cast(key.DependentType); - prevLayerRelationships[key] = replaced; + prevLayerRelationships[key] = TypeHelper.CreateHashSetFor(key.DependentType, replaced); } + return prevLayerRelationships; } /// @@ -379,14 +383,14 @@ HashSet GetAllowedEntities(IEnumerable source, IEnumerable - /// Gets the inverse for + /// Gets the inverse for /// - RelationshipProxy GetInverseRelationship(RelationshipProxy proxy) + RelationshipAttribute GetInverseRelationship(RelationshipAttribute attribute) { - return new RelationshipProxy(_graph.GetInverseRelationship(proxy.Attribute), proxy.PrincipalType, false); + return _graph.GetInverseRelationship(attribute); } - IEnumerable LoadDbValues(Type containerEntityType, IEnumerable uniqueEntities, ResourceHook targetHook, RelationshipProxy[] relationshipsToNextLayer) + IEnumerable LoadDbValues(Type containerEntityType, IEnumerable uniqueEntities, ResourceHook targetHook, RelationshipAttribute[] relationshipsToNextLayer) { if (!_executorHelper.ShouldLoadDbValues(containerEntityType, targetHook)) return null; return _executorHelper.LoadDbValues(containerEntityType, uniqueEntities, targetHook, relationshipsToNextLayer); diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs index 95a96cf524..cb4185d1fd 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs @@ -14,12 +14,12 @@ internal interface IRelationshipsFromPreviousLayer /// Grouped by relationship to the previous layer, gets all the entities of the current layer /// /// The dependent entities. - Dictionary GetDependentEntities(); + Dictionary GetDependentEntities(); /// /// Grouped by relationship to the previous layer, gets all the entities of the previous layer /// /// The dependent entities. - Dictionary GetPrincipalEntities(); + Dictionary GetPrincipalEntities(); } internal class RelationshipsFromPreviousLayer : IRelationshipsFromPreviousLayer, IEnumerable> where TDependent : class, IIdentifiable @@ -31,14 +31,14 @@ public RelationshipsFromPreviousLayer(IEnumerable> _collection = collection; } - public Dictionary GetDependentEntities() + public Dictionary GetDependentEntities() { - return _collection.ToDictionary(rg => rg.Proxy, rg => (IEnumerable)rg.DependentEntities); + return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.DependentEntities); } - public Dictionary GetPrincipalEntities() + public Dictionary GetPrincipalEntities() { - return _collection.ToDictionary(rg => rg.Proxy, rg => (IEnumerable)rg.PrincipalEntities); + return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.PrincipalEntities); } public IEnumerator> GetEnumerator() diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs index d13640a956..a7965f1ec9 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs @@ -17,19 +17,19 @@ internal class RootNode : IEntityNode where TEntity : class, IIdentifia public Type EntityType { get; internal set; } public IEnumerable UniqueEntities { get { return _uniqueEntities; } } public RelationshipProxy[] RelationshipsToNextLayer { get; private set; } - public Dictionary> PrincipalsToNextLayerByType() + public Dictionary> PrincipalsToNextLayerByType() { return RelationshipsToNextLayer .GroupBy(proxy => proxy.DependentType) - .ToDictionary(gdc => gdc.Key, gdc => gdc.ToDictionary(p => p, p => UniqueEntities)); + .ToDictionary(gdc => gdc.Key, gdc => gdc.ToDictionary(p => p.Attribute, p => UniqueEntities)); } /// /// The current layer entities grouped by affected relationship to the next layer /// - public Dictionary PrincipalsToNextLayer() + public Dictionary PrincipalsToNextLayer() { - return RelationshipsToNextLayer.ToDictionary(p => p, p => UniqueEntities); + return RelationshipsToNextLayer.ToDictionary(p => p.Attribute, p => UniqueEntities); } /// diff --git a/src/JsonApiDotNetCore/Internal/ContextGraph.cs b/src/JsonApiDotNetCore/Internal/ContextGraph.cs deleted file mode 100644 index 18aab6646e..0000000000 --- a/src/JsonApiDotNetCore/Internal/ContextGraph.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Internal -{ - /// - [Obsolete("Use IResourceGraph instead.")] - public interface IContextGraph : IResourceGraph { } - - [Obsolete("Use ResourceGraph instead.")] - public class ContextGraph : ResourceGraph { } -} diff --git a/src/JsonApiDotNetCore/Internal/Error.cs b/src/JsonApiDotNetCore/Internal/Error.cs index 7e45f65be3..058fdf6f36 100644 --- a/src/JsonApiDotNetCore/Internal/Error.cs +++ b/src/JsonApiDotNetCore/Internal/Error.cs @@ -9,17 +9,7 @@ namespace JsonApiDotNetCore.Internal { public class Error { - public Error() - { } - - [Obsolete("Use Error constructors with int typed status")] - public Error(string status, string title, ErrorMeta meta = null, object source = null) - { - Status = status; - Title = title; - Meta = meta; - Source = source; - } + public Error() { } public Error(int status, string title, ErrorMeta meta = null, object source = null) { @@ -29,16 +19,6 @@ public Error(int status, string title, ErrorMeta meta = null, object source = nu Source = source; } - [Obsolete("Use Error constructors with int typed status")] - public Error(string status, string title, string detail, ErrorMeta meta = null, object source = null) - { - Status = status; - Title = title; - Detail = detail; - Meta = meta; - Source = source; - } - public Error(int status, string title, string detail, ErrorMeta meta = null, object source = null) { Status = status.ToString(); diff --git a/src/JsonApiDotNetCore/Internal/JsonApiException.cs b/src/JsonApiDotNetCore/Internal/JsonApiException.cs index 0852ac1e04..0f1f06dfdb 100644 --- a/src/JsonApiDotNetCore/Internal/JsonApiException.cs +++ b/src/JsonApiDotNetCore/Internal/JsonApiException.cs @@ -14,17 +14,7 @@ public JsonApiException(ErrorCollection errorCollection) public JsonApiException(Error error) : base(error.Title) => _errors.Add(error); - - [Obsolete("Use int statusCode overload instead")] - public JsonApiException(string statusCode, string message, string source = null) - : base(message) - => _errors.Add(new Error(statusCode, message, null, GetMeta(), source)); - - [Obsolete("Use int statusCode overload instead")] - public JsonApiException(string statusCode, string message, string detail, string source = null) - : base(message) - => _errors.Add(new Error(statusCode, message, detail, GetMeta(), source)); - + public JsonApiException(int statusCode, string message, string source = null) : base(message) => _errors.Add(new Error(statusCode, message, null, GetMeta(), source)); diff --git a/src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs b/src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs index 92e79a85ee..3205e1e01a 100644 --- a/src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs +++ b/src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs @@ -16,11 +16,6 @@ public AttrFilterQuery( if (Attribute.IsFilterable == false) throw new JsonApiException(400, $"Filter is not allowed for attribute '{Attribute.PublicAttributeName}'."); - - FilteredAttribute = Attribute; } - - [Obsolete("Use " + nameof(BaseAttrQuery.Attribute) + " instead.")] - public AttrAttribute FilteredAttribute { get; set; } } } diff --git a/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs b/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs index 541203dc4c..e1b53cd47d 100644 --- a/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs +++ b/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs @@ -20,13 +20,10 @@ public class FilterQuery : BaseQuery public FilterQuery(string attribute, string value, string operation) : base(attribute) { - Key = attribute.ToProperCase(); Value = value; Operation = operation; } - [Obsolete("Key has been replaced by '" + nameof(Attribute) + "'. Members should be located by their public name, not by coercing the provided value to the internal name.")] - public string Key { get; set; } public string Value { get; set; } public string Operation { get; set; } diff --git a/src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs b/src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs index 96d33d0e72..8fef8de693 100644 --- a/src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs +++ b/src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs @@ -19,15 +19,7 @@ public RelatedAttrFilterQuery( if (Attribute.IsFilterable == false) throw new JsonApiException(400, $"Filter is not allowed for attribute '{Attribute.PublicAttributeName}'."); - - FilteredRelationship = Relationship; - FilteredAttribute = Attribute; + } - - [Obsolete("Use " + nameof(Attribute) + " instead.")] - public AttrAttribute FilteredAttribute { get; set; } - - [Obsolete("Use " + nameof(Relationship) + " instead.")] - public RelationshipAttribute FilteredRelationship { get; set; } } } diff --git a/src/JsonApiDotNetCore/Internal/Query/SortQuery.cs b/src/JsonApiDotNetCore/Internal/Query/SortQuery.cs index 254d328a43..7194b6e948 100644 --- a/src/JsonApiDotNetCore/Internal/Query/SortQuery.cs +++ b/src/JsonApiDotNetCore/Internal/Query/SortQuery.cs @@ -8,10 +8,6 @@ namespace JsonApiDotNetCore.Internal.Query /// public class SortQuery : BaseQuery { - [Obsolete("Use constructor overload (SortDirection, string) instead. The string should be the publicly exposed attribute name.", error: true)] - public SortQuery(SortDirection direction, AttrAttribute sortedAttribute) - : base(sortedAttribute.PublicAttributeName) { } - public SortQuery(SortDirection direction, string attribute) : base(attribute) { @@ -22,8 +18,5 @@ public SortQuery(SortDirection direction, string attribute) /// Direction the sort should be applied /// public SortDirection Direction { get; set; } - - [Obsolete("Use string based Attribute instead.", error: true)] - public AttrAttribute SortedAttribute { get; set; } } } diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index a7bd486a65..efec077536 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -69,6 +69,11 @@ public interface IResourceGraph /// /// The internal attribute name for a . string GetPublicAttributeName(string internalAttributeName); + + /// + /// Helper method to get the inverse relationship attribute corresponding + /// to a relationship. + /// RelationshipAttribute GetInverseRelationship(RelationshipAttribute relationship); /// diff --git a/src/JsonApiDotNetCore/Internal/TypeHelper.cs b/src/JsonApiDotNetCore/Internal/TypeHelper.cs index 7677862efb..cf642aa395 100644 --- a/src/JsonApiDotNetCore/Internal/TypeHelper.cs +++ b/src/JsonApiDotNetCore/Internal/TypeHelper.cs @@ -1,11 +1,13 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Reflection; +using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Internal { - public static class TypeHelper + internal static class TypeHelper { public static IList ConvertCollection(IEnumerable collection, Type targetType) { @@ -107,6 +109,15 @@ public static object CreateInstanceOfOpenType(Type openType, Type[] parameters, return Activator.CreateInstance(parameterizedType, constructorArguments); } + + /// + /// Helper method that "unboxes" the TValue from the relationship dictionary into + /// + public static Dictionary> ConvertRelationshipDictionary(Dictionary relationships) + { + return relationships.ToDictionary(pair => pair.Key, pair => (HashSet)pair.Value); + } + /// /// Use this overload if you need to instantiate a type that has a internal constructor /// @@ -147,9 +158,17 @@ public static object CreateInstanceOfOpenType(Type openType, Type parameter, boo /// The target type public static IList CreateListFor(Type type) { - IList list = (IList)CreateInstanceOfOpenType(typeof(List<>), type ); + IList list = (IList)CreateInstanceOfOpenType(typeof(List<>), type); return list; + } + /// + /// Reflectively instantiates a hashset of a certain type. + /// + /// + public static IEnumerable CreateHashSetFor(Type type, object elements = null) + { + return (IEnumerable)CreateInstanceOfOpenType(typeof(HashSet<>), type, elements ?? new object()); } /// diff --git a/src/JsonApiDotNetCore/Models/Identifiable.cs b/src/JsonApiDotNetCore/Models/Identifiable.cs index 663786de93..b62f31fe89 100644 --- a/src/JsonApiDotNetCore/Models/Identifiable.cs +++ b/src/JsonApiDotNetCore/Models/Identifiable.cs @@ -62,11 +62,5 @@ protected virtual T GetTypedId(string value) var convertedValue = TypeHelper.ConvertType(value, typeof(T)); return convertedValue == null ? default : (T)convertedValue; } - - [Obsolete("Use GetTypedId instead")] - protected virtual object GetConcreteId(string value) - { - return TypeHelper.ConvertType(value, typeof(T)); - } } } diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index c8fbec627d..e615de6538 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -173,19 +173,19 @@ public virtual void AfterUpdate(HashSet entities, ResourcePipeline pipeline) /// public virtual void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } /// - public virtual void AfterUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { } + public virtual void AfterUpdateRelationship(IRelationshipsDictionary resourcesByRelationship, ResourcePipeline pipeline) { } /// - public virtual IEnumerable BeforeCreate(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } + public virtual IEnumerable BeforeCreate(IEntityHashSet affected, ResourcePipeline pipeline) { return affected; } /// public virtual void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { } /// - public virtual IEnumerable BeforeUpdate(IAffectedResourcesDiff ResourceDiff, ResourcePipeline pipeline) { return ResourceDiff; } + public virtual IEnumerable BeforeUpdate(IEntityDiff ResourceDiff, ResourcePipeline pipeline) { return ResourceDiff.Entities; } /// - public virtual IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } + public virtual IEnumerable BeforeDelete(IEntityHashSet affected, ResourcePipeline pipeline) { return affected; } /// - public virtual IEnumerable BeforeUpdateRelationship(HashSet ids, IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { return ids; } + public virtual IEnumerable BeforeUpdateRelationship(HashSet ids, IRelationshipsDictionary resourcesByRelationship, ResourcePipeline pipeline) { return ids; } /// - public virtual void BeforeImplicitUpdateRelationship(IAffectedRelationships resourcesByRelationship, ResourcePipeline pipeline) { } + public virtual void BeforeImplicitUpdateRelationship(IRelationshipsDictionary resourcesByRelationship, ResourcePipeline pipeline) { } /// public virtual IEnumerable OnReturn(HashSet entities, ResourcePipeline pipeline) { return entities; } diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs index d289c1a788..c11c011852 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs @@ -18,16 +18,6 @@ public class JsonApiDeSerializer : IJsonApiDeSerializer { private readonly IJsonApiContext _jsonApiContext; - [Obsolete( - "The deserializer no longer depends on the IGenericProcessorFactory", - error: false)] - public JsonApiDeSerializer( - IJsonApiContext jsonApiContext, - IGenericProcessorFactory genericProcessorFactory) - { - _jsonApiContext = jsonApiContext; - } - public JsonApiDeSerializer(IJsonApiContext jsonApiContext) { _jsonApiContext = jsonApiContext; diff --git a/src/JsonApiDotNetCore/Services/IJsonApiContext.cs b/src/JsonApiDotNetCore/Services/IJsonApiContext.cs index e0a14a4e02..d3fb014bcc 100644 --- a/src/JsonApiDotNetCore/Services/IJsonApiContext.cs +++ b/src/JsonApiDotNetCore/Services/IJsonApiContext.cs @@ -139,9 +139,6 @@ public interface IJsonApiRequest : IJsonApiApplication, IUpdateRequest, IQueryRe /// If the request is on the `{id}/relationships/{relationshipName}` route /// bool IsRelationshipPath { get; } - - [Obsolete("Use `IsRelationshipPath` instead.")] - bool IsRelationshipData { get; set; } } public interface IJsonApiContext : IJsonApiRequest @@ -150,9 +147,6 @@ public interface IJsonApiContext : IJsonApiRequest IMetaBuilder MetaBuilder { get; set; } IGenericProcessorFactory GenericProcessorFactory { get; set; } - [Obsolete("Use the proxied method IControllerContext.GetControllerAttribute instead.")] - TAttribute GetControllerAttribute() where TAttribute : Attribute; - /// /// **_Experimental_**: do not use. It is likely to change in the future. /// diff --git a/src/JsonApiDotNetCore/Services/JsonApiContext.cs b/src/JsonApiDotNetCore/Services/JsonApiContext.cs index bec78e8237..495354b1e0 100644 --- a/src/JsonApiDotNetCore/Services/JsonApiContext.cs +++ b/src/JsonApiDotNetCore/Services/JsonApiContext.cs @@ -139,10 +139,6 @@ private PageManager GetPageManager() }; } - [Obsolete("Use the proxied method IControllerContext.GetControllerAttribute instead.")] - public TAttribute GetControllerAttribute() where TAttribute : Attribute - => _controllerContext.GetControllerAttribute(); - public void BeginOperation() { IncludedRelationships = new List(); diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index 6953b5f49c..09ffcfcfc1 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -5,6 +5,7 @@ using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Moq; using Xunit; @@ -14,7 +15,16 @@ namespace DiscoveryTests public class ServiceDiscoveryFacadeTests { private readonly IServiceCollection _services = new ServiceCollection(); - private readonly ResourceGraphBuilder _graphBuilder = new ResourceGraphBuilder(); + private readonly ResourceGraphBuilder _graphBuilder = new ResourceGraphBuilder(); + + public ServiceDiscoveryFacadeTests() + { + var contextMock = new Mock(); + var dbResolverMock = new Mock(); + dbResolverMock.Setup(m => m.GetContext()).Returns(new Mock().Object); + TestModelRepository._dbContextResolver = dbResolverMock.Object; + } + private ServiceDiscoveryFacade _facade => new ServiceDiscoveryFacade(_services, _graphBuilder); [Fact] @@ -79,7 +89,7 @@ public TestModelService() : base(_jsonApiContext, _repo) { } public class TestModelRepository : DefaultEntityRepository { - private static IDbContextResolver _dbContextResolver = new Mock().Object; + internal static IDbContextResolver _dbContextResolver; private static IJsonApiContext _jsonApiContext = new Mock().Object; public TestModelRepository() : base(_jsonApiContext, _dbContextResolver) { } } diff --git a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj old mode 100755 new mode 100644 index bf27086a63..b4fcaf7ae0 --- a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj +++ b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj @@ -17,7 +17,7 @@ - + diff --git a/test/UnitTests/Data/DefaultEntityRepository_Tests.cs b/test/UnitTests/Data/DefaultEntityRepository_Tests.cs index 958639575e..97cf51d587 100644 --- a/test/UnitTests/Data/DefaultEntityRepository_Tests.cs +++ b/test/UnitTests/Data/DefaultEntityRepository_Tests.cs @@ -12,7 +12,6 @@ using JsonApiDotNetCore.Services; using System.Threading.Tasks; using System.Linq; -using System.Collections; using JsonApiDotNetCore.Request; namespace UnitTests.Data @@ -77,14 +76,15 @@ public async Task UpdateAsync_Updates_Attributes_In_AttributesToUpdate() private DefaultEntityRepository GetRepository() { + + _contextMock + .Setup(m => m.Set()) + .Returns(_dbSetMock.Object); + _contextResolverMock .Setup(m => m.GetContext()) .Returns(_contextMock.Object); - _contextResolverMock - .Setup(m => m.GetDbSet()) - .Returns(_dbSetMock.Object); - _jsonApiContextMock .Setup(m => m.AttributesToUpdate) .Returns(_attrsToUpdate); diff --git a/test/UnitTests/ResourceHooks/DiscoveryTests.cs b/test/UnitTests/ResourceHooks/DiscoveryTests.cs index 6af4f6a392..bde024f530 100644 --- a/test/UnitTests/ResourceHooks/DiscoveryTests.cs +++ b/test/UnitTests/ResourceHooks/DiscoveryTests.cs @@ -14,7 +14,7 @@ public class DummyResourceDefinition : ResourceDefinition { public DummyResourceDefinition() : base(new ResourceGraphBuilder().AddResource().Build()) { } - public override IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } + public override IEnumerable BeforeDelete(IEntityHashSet affected, ResourcePipeline pipeline) { return affected; } public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } } @@ -29,13 +29,12 @@ public void Hook_Discovery() } - public class AnotherDummy : Identifiable { } public abstract class ResourceDefintionBase : ResourceDefinition where T : class, IIdentifiable { protected ResourceDefintionBase(IResourceGraph graph) : base(graph) { } - public override IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } + public override IEnumerable BeforeDelete(IEntityHashSet affected, ResourcePipeline pipeline) { return affected; } public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } } @@ -59,7 +58,7 @@ public class YetAnotherDummyResourceDefinition : ResourceDefinition().Build()) { } - public override IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected; } + public override IEnumerable BeforeDelete(IEntityHashSet affected, ResourcePipeline pipeline) { return affected; } [LoadDatabaseValues(false)] public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } @@ -81,7 +80,7 @@ public class DoubleDummyResourceDefinition1 : ResourceDefinition { public DoubleDummyResourceDefinition1() : base(new ResourceGraphBuilder().AddResource().Build()) { } - public override IEnumerable BeforeDelete(IAffectedResources affected, ResourcePipeline pipeline) { return affected.Entities; } + public override IEnumerable BeforeDelete(IEntityHashSet affected, ResourcePipeline pipeline) { return affected; } } public class DoubleDummyResourceDefinition2 : ResourceDefinition { diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/AfterCreateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/AfterCreateTests.cs index ca7b78f86d..dea114facc 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/AfterCreateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/AfterCreateTests.cs @@ -25,7 +25,7 @@ public void AfterCreate() // assert todoResourceMock.Verify(rd => rd.AfterCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Post), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -43,7 +43,7 @@ public void AfterCreate_Without_Parent_Hook_Implemented() hookExecutor.AfterCreate(todoList, ResourcePipeline.Post); // assert - ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Post), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs index 4a35411171..bc2163df2f 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreateTests.cs @@ -24,8 +24,8 @@ public void BeforeCreate() // act hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -43,8 +43,8 @@ public void BeforeCreate_Without_Parent_Hook_Implemented() // act hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Never()); - ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Never()); + ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } [Fact] @@ -61,7 +61,7 @@ public void BeforeCreate_Without_Child_Hook_Implemented() // act hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } [Fact] diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs index 11d1af329a..c574de145c 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Create/BeforeCreate_WithDbValues_Tests.cs @@ -54,14 +54,14 @@ public void BeforeCreate() hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), - It.IsAny>(), + It.IsAny>(), ResourcePipeline.Post), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( - It.Is>(rh => TodoCheck(rh, description + description)), + It.Is>(rh => TodoCheckRelationships(rh, description + description)), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -82,7 +82,7 @@ public void BeforeCreate_Without_Parent_Hook_Implemented() // assert ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), - It.IsAny>(), + It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -101,9 +101,9 @@ public void BeforeCreate_Without_Child_Hook_Implemented() hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( - It.Is>(rh => TodoCheck(rh, description + description)), + It.Is>(rh => TodoCheckRelationships(rh, description + description)), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -122,10 +122,10 @@ public void BeforeCreate_NoImplicit() hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), - It.IsAny>(), + It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -146,7 +146,7 @@ public void BeforeCreate_NoImplicit_Without_Parent_Hook_Implemented() // assert ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), - It.IsAny>(), + It.IsAny>(), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -165,7 +165,7 @@ public void BeforeCreate_NoImplicit_Without_Child_Hook_Implemented() hookExecutor.BeforeCreate(todoList, ResourcePipeline.Post); // assert - todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeCreate(It.Is>((entities) => TodoCheck(entities, description)), ResourcePipeline.Post), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -174,7 +174,7 @@ private bool TodoCheck(IEnumerable entities, string checksum) return entities.Single().Description == checksum; } - private bool TodoCheck(IAffectedRelationships rh, string checksum) + private bool TodoCheckRelationships(IRelationshipsDictionary rh, string checksum) { return rh.GetByRelationship().Single().Value.First().Description == checksum; } @@ -184,7 +184,7 @@ private bool PersonIdCheck(IEnumerable ids, string checksum) return ids.Single() == checksum; } - private bool PersonCheck(string checksum, IAffectedRelationships helper) + private bool PersonCheck(string checksum, IRelationshipsDictionary helper) { var entries = helper.GetByRelationship(); diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDeleteTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDeleteTests.cs index 763d0716c4..887a322994 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDeleteTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDeleteTests.cs @@ -22,7 +22,7 @@ public void BeforeDelete() hookExecutor.BeforeDelete(todoList, ResourcePipeline.Delete); // assert - resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); + resourceDefinitionMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); resourceDefinitionMock.VerifyNoOtherCalls(); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDelete_WithDbValue_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDelete_WithDbValue_Tests.cs index 7c23e8d133..f63adcbd6e 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDelete_WithDbValue_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Delete/BeforeDelete_WithDbValue_Tests.cs @@ -47,9 +47,9 @@ public void BeforeDelete() hookExecutor.BeforeDelete(new List { person }, ResourcePipeline.Delete); // assert - personResourceMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); - todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>( rh => CheckImplicitTodos(rh) ), ResourcePipeline.Delete), Times.Once()); - passportResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>( rh => CheckImplicitPassports(rh) ), ResourcePipeline.Delete), Times.Once()); + personResourceMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>( rh => CheckImplicitTodos(rh) ), ResourcePipeline.Delete), Times.Once()); + passportResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>( rh => CheckImplicitPassports(rh) ), ResourcePipeline.Delete), Times.Once()); VerifyNoOtherCalls(personResourceMock, todoResourceMock, passportResourceMock); } @@ -68,8 +68,8 @@ public void BeforeDelete_No_Parent_Hooks() hookExecutor.BeforeDelete(new List { person }, ResourcePipeline.Delete); // assert - todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>(rh => CheckImplicitTodos(rh)), ResourcePipeline.Delete), Times.Once()); - passportResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>(rh => CheckImplicitPassports(rh)), ResourcePipeline.Delete), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>(rh => CheckImplicitTodos(rh)), ResourcePipeline.Delete), Times.Once()); + passportResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship(It.Is>(rh => CheckImplicitPassports(rh)), ResourcePipeline.Delete), Times.Once()); VerifyNoOtherCalls(personResourceMock, todoResourceMock, passportResourceMock); } @@ -88,17 +88,17 @@ public void BeforeDelete_No_Children_Hooks() hookExecutor.BeforeDelete(new List { person }, ResourcePipeline.Delete); // assert - personResourceMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); + personResourceMock.Verify(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny()), Times.Once()); VerifyNoOtherCalls(personResourceMock, todoResourceMock, passportResourceMock); } - private bool CheckImplicitTodos(IAffectedRelationships rh) + private bool CheckImplicitTodos(IRelationshipsDictionary rh) { var todos = rh.GetByRelationship().ToList(); return todos.Count == 2; } - private bool CheckImplicitPassports(IAffectedRelationships rh) + private bool CheckImplicitPassports(IRelationshipsDictionary rh) { var passports = rh.GetByRelationship().Single().Value; return passports.Count == 1; diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/AfterUpdateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/AfterUpdateTests.cs index e9c7f98473..e8d4f4fd60 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/AfterUpdateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/AfterUpdateTests.cs @@ -25,7 +25,7 @@ public void AfterUpdate() // assert todoResourceMock.Verify(rd => rd.AfterUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); - ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -43,7 +43,7 @@ public void AfterUpdate_Without_Parent_Hook_Implemented() hookExecutor.AfterUpdate(todoList, ResourcePipeline.Patch); // assert - ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + ownerResourceMock.Verify(rd => rd.AfterUpdateRelationship(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs index 67195f2d95..e27f7f8caa 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdateTests.cs @@ -24,8 +24,8 @@ public void BeforeUpdate() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); - ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -43,7 +43,7 @@ public void BeforeUpdate_Without_Parent_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } @@ -62,7 +62,7 @@ public void BeforeUpdate_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs index 42b7151a07..dd1eeea5e1 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs @@ -59,18 +59,18 @@ public void BeforeUpdate() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), - It.Is>(rh => PersonCheck(lastName, rh)), + It.Is>(rh => PersonCheck(lastName, rh)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( - It.Is>(rh => PersonCheck(lastName + lastName, rh)), + It.Is>(rh => PersonCheck(lastName + lastName, rh)), ResourcePipeline.Patch), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( - It.Is>( rh => TodoCheck(rh, description + description)), + It.Is>( rh => TodoCheck(rh, description + description)), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -93,9 +93,9 @@ public void BeforeUpdate_Deleting_Relationship() hookExecutor.BeforeUpdate(_todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( - It.Is>(rh => PersonCheck(lastName + lastName, rh)), + It.Is>(rh => PersonCheck(lastName + lastName, rh)), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -117,11 +117,11 @@ public void BeforeUpdate_Without_Parent_Hook_Implemented() // assert ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), - It.Is>(rh => PersonCheck(lastName, rh)), + It.Is>(rh => PersonCheck(lastName, rh)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( - It.Is>(rh => PersonCheck(lastName + lastName, rh)), + It.Is>(rh => PersonCheck(lastName + lastName, rh)), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -140,9 +140,9 @@ public void BeforeUpdate_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); todoResourceMock.Verify(rd => rd.BeforeImplicitUpdateRelationship( - It.Is>(rh => TodoCheck(rh, description + description)), + It.Is>(rh => TodoCheck(rh, description + description)), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -161,10 +161,10 @@ public void BeforeUpdate_NoImplicit() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), - It.IsAny>(), + It.IsAny>(), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -185,7 +185,7 @@ public void BeforeUpdate_NoImplicit_Without_Parent_Hook_Implemented() // assert ownerResourceMock.Verify(rd => rd.BeforeUpdateRelationship( It.Is>(ids => PersonIdCheck(ids, personId)), - It.Is>(rh => PersonCheck(lastName, rh)), + It.Is>(rh => PersonCheck(lastName, rh)), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); @@ -204,19 +204,19 @@ public void BeforeUpdate_NoImplicit_Without_Child_Hook_Implemented() hookExecutor.BeforeUpdate(todoList, ResourcePipeline.Patch); // assert - todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); + todoResourceMock.Verify(rd => rd.BeforeUpdate(It.Is>((diff) => TodoCheck(diff, description)), ResourcePipeline.Patch), Times.Once()); VerifyNoOtherCalls(todoResourceMock, ownerResourceMock); } - private bool TodoCheck(IAffectedResourcesDiff diff, string checksum) + private bool TodoCheck(IEntityDiff diff, string checksum) { - var diffPair = diff.GetDiff().Single(); + var diffPair = diff.Single(); var dbCheck = diffPair.DatabaseValue.Description == checksum; var reqCheck = diffPair.Entity.Description == null; return (dbCheck && reqCheck); } - private bool TodoCheck(IAffectedRelationships rh, string checksum) + private bool TodoCheck(IRelationshipsDictionary rh, string checksum) { return rh.GetByRelationship().Single().Value.First().Description == checksum; } @@ -226,7 +226,7 @@ private bool PersonIdCheck(IEnumerable ids, string checksum) return ids.Single() == checksum; } - private bool PersonCheck(string checksum, IAffectedRelationships helper) + private bool PersonCheck(string checksum, IRelationshipsDictionary helper) { var entries = helper.GetByRelationship(); return entries.Single().Value.Single().LastName == checksum; diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 271e1baf09..369e46b26d 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -254,26 +254,26 @@ protected DbContextOptions InitInMemoryDb(Action seeder void MockHooks(Mock> resourceDefinition) where TModel : class, IIdentifiable { resourceDefinition - .Setup(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny())) + .Setup(rd => rd.BeforeCreate(It.IsAny>(), It.IsAny())) .Returns, ResourcePipeline>((entities, context) => entities) .Verifiable(); resourceDefinition .Setup(rd => rd.BeforeRead(It.IsAny(), It.IsAny(), It.IsAny())) .Verifiable(); resourceDefinition - .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) - .Returns, ResourcePipeline>((entityDiff, context) => entityDiff.Entities) + .Setup(rd => rd.BeforeUpdate(It.IsAny>(), It.IsAny())) + .Returns, ResourcePipeline>((entityDiff, context) => entityDiff.Entities) .Verifiable(); resourceDefinition - .Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())) + .Setup(rd => rd.BeforeDelete(It.IsAny>(), It.IsAny())) .Returns, ResourcePipeline>((entities, context) => entities) .Verifiable(); resourceDefinition - .Setup(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), It.IsAny())) - .Returns, IAffectedRelationships, ResourcePipeline>((ids, context, helper) => ids) + .Setup(rd => rd.BeforeUpdateRelationship(It.IsAny>(), It.IsAny>(), It.IsAny())) + .Returns, IRelationshipsDictionary, ResourcePipeline>((ids, context, helper) => ids) .Verifiable(); resourceDefinition - .Setup(rd => rd.BeforeImplicitUpdateRelationship(It.IsAny>(), It.IsAny())) + .Setup(rd => rd.BeforeImplicitUpdateRelationship(It.IsAny>(), It.IsAny())) .Verifiable(); resourceDefinition @@ -343,7 +343,6 @@ IDbContextResolver CreateTestDbResolver(AppDbContext dbContext) where TM { var mock = new Mock(); mock.Setup(r => r.GetContext()).Returns(dbContext); - mock.Setup(r => r.GetDbSet()).Returns(dbContext.Set()); return mock.Object; }