Esempio n. 1
0
        /// <inheritdoc />
        public IReadOnlyList <Resource> SaveReferences(IUnitOfWork uow, Resource instance, ResourceEntity entity)
        {
            var context = new ReferenceSaverContext(uow, Graph, instance, entity);

            SaveReferences(context, instance);
            return(context.EntityCache.Keys.Where(i => i.Id == 0).ToList());
        }
Esempio n. 2
0
        /// <summary>
        /// Create a <see cref="ResourceRelation"/> entity for a property match
        /// </summary>
        private static ResourceRelation CreateRelationForProperty(ReferenceSaverContext context, IResourceRelationRepository relationRepo, ResourceReferenceAttribute att)
        {
            var relationType = att.RelationType;
            var relEntity    = relationRepo.Create((int)relationType);

            context.CreatedRelations.Add(relEntity);

            return(relEntity);
        }
Esempio n. 3
0
        /// <inheritdoc />
        public IReadOnlyList <Resource> SaveRoots(IUnitOfWork uow, IReadOnlyList <Resource> instances)
        {
            var context = new ReferenceSaverContext(uow, Graph);

            foreach (var instance in instances)
            {
                SaveReferences(context, instance);
            }
            return(context.EntityCache.Keys.ToArray());
        }
Esempio n. 4
0
        /// <inheritdoc />
        public IReadOnlyList <Resource> SaveSingleCollection(IUnitOfWork uow, Resource instance, PropertyInfo property)
        {
            var entity    = uow.GetEntity <ResourceEntity>(instance);
            var relations = ResourceRelationAccessor.FromEntity(uow, entity);

            var context     = new ReferenceSaverContext(uow, Graph, instance, entity);
            var matches     = MatchingRelations(relations, property);
            var typeMatches = TypeFilter(matches, property, context.ResolveReference).ToList();
            var created     = UpdateCollectionReference(context, entity, instance, property, typeMatches);

            foreach (var resource in created)
            {
                SaveReferences(context, resource);
            }

            return(context.EntityCache.Keys.Where(i => i.Id == 0).ToList());
        }
Esempio n. 5
0
        /// <summary>
        /// Make sure our resource-relation graph in the database is synced to the resource object graph. This method
        /// updates a collection of references
        /// </summary>
        /// <example>
        /// [ResourceReference(ResourceRelationType.TransportRoute, ResourceReferenceRole.Source)]
        /// public IReferences&lt;Resource&gt; FriendResources { get; set; }
        /// </example>
        private IEnumerable <Resource> UpdateCollectionReference(ReferenceSaverContext context, ResourceEntity entity, Resource resource, PropertyInfo referenceProperty, IReadOnlyList <ResourceRelationAccessor> relationTemplates)
        {
            var relationRepo = context.UnitOfWork.GetRepository <IResourceRelationRepository>();
            var referenceAtt = referenceProperty.GetCustomAttribute <ResourceReferenceAttribute>();

            // Get the value stored in the reference property
            var propertyValue       = referenceProperty.GetValue(resource);
            var referencedResources = ((IEnumerable <IResource>)propertyValue).Cast <Resource>().ToList();

            // Check required attribute against empty collections
            if (referencedResources.Count == 0 && referenceAtt.IsRequired)
            {
                throw new ValidationException($"Property {referenceProperty.Name} is flagged 'Required' and was empty!");
            }

            // First delete references that are not used by ANY property of the same configuration
            var currentReferences = CurrentReferences(resource, referenceAtt);
            var deleted           = relationTemplates.Where(m => currentReferences.All(cr => cr != context.ResolveReference(m))).ToList();

            foreach (var relation in deleted)
            {
                ClearOnTarget(context.ResolveReference(relation), resource, referenceAtt);
                relationRepo.Remove(relation.Entity);
            }

            // Now create new relations
            var created = referencedResources.Where(rr => relationTemplates.All(m => rr != context.ResolveReference(m))).ToList();

            foreach (var createdReference in created)
            {
                SetOnTarget(createdReference, resource, referenceAtt);
                var relEntity        = CreateRelationForProperty(context, relationRepo, referenceAtt);
                var referencedEntity = GetOrCreateEntity(context, createdReference);
                UpdateRelationEntity(entity, referencedEntity, relEntity, referenceAtt);
            }

            return(created.Where(cr => cr.Id == 0));
        }
Esempio n. 6
0
        /// <summary>
        /// Get or create an entity for a resource instance
        /// </summary>
        private static ResourceEntity GetOrCreateEntity(ReferenceSaverContext context, Resource instance)
        {
            // First check if the context contains an entity for the instance
            if (context.EntityCache.ContainsKey(instance))
            {
                return(context.EntityCache[instance]);
            }

            ResourceEntity entity;

            if (instance.Id > 0)
            {
                entity = context.UnitOfWork.GetEntity <ResourceEntity>(instance);
            }
            else
            {
                entity = ResourceEntityAccessor.SaveToEntity(context.UnitOfWork, instance);
                context.ResourceLookup[entity] = instance;
            }

            // Get or create an entity for the instance
            return(context.EntityCache[instance] = entity);
        }
Esempio n. 7
0
        private void SaveReferences(ReferenceSaverContext context, Resource instance)
        {
            var entity = GetOrCreateEntity(context, instance);

            var relations = ResourceRelationAccessor.FromEntity(context.UnitOfWork, entity)
                            .Union(ResourceRelationAccessor.FromQueryable(context.CreatedRelations.AsQueryable(), entity))
                            .ToList();

            var createdResources = new List <Resource>();

            foreach (var referenceProperty in ReferenceProperties(instance.GetType(), false))
            {
                var matches     = MatchingRelations(relations, referenceProperty);
                var typeMatches = TypeFilter(matches, referenceProperty, context.ResolveReference).ToList();
                if (typeof(IEnumerable <IResource>).IsAssignableFrom(referenceProperty.PropertyType))
                {
                    // Save a collection reference
                    var created = UpdateCollectionReference(context, entity, instance, referenceProperty, typeMatches);
                    createdResources.AddRange(created);
                }
                else
                {
                    // Save a single reference
                    var createdResource = UpdateSingleReference(context, entity, instance, referenceProperty, typeMatches);
                    if (createdResource != null)
                    {
                        createdResources.Add(createdResource);
                    }
                }
            }

            // Recursively save references for new resources
            foreach (var resource in createdResources)
            {
                SaveReferences(context, resource);
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Make sure our resource-relation graph in the database is synced to the resource object graph. This method
        /// updates single references like in the example below
        /// </summary>
        /// <example>
        /// [ResourceReference(ResourceRelationType.TransportRoute, ResourceReferenceRole.Source)]
        /// public Resource FriendResource { get; set; }
        /// </example>
        private Resource UpdateSingleReference(ReferenceSaverContext context, ResourceEntity entity, Resource resource, PropertyInfo referenceProperty, IReadOnlyList <ResourceRelationAccessor> matches)
        {
            var relationRepo = context.UnitOfWork.GetRepository <IResourceRelationRepository>();

            var value = referenceProperty.GetValue(resource);
            var referencedResource = value as Resource;

            // Validate if object assigned to the property is a resource
            if (value != null && referencedResource == null)
            {
                throw new ArgumentException($"Value of property {referenceProperty.Name} on resource {resource.Id}:{resource.GetType().Name} must be a Resource");
            }

            var referenceAtt = referenceProperty.GetCustomAttribute <ResourceReferenceAttribute>();

            // Validate if required property is set
            if (referencedResource == null && referenceAtt.IsRequired)
            {
                throw new ValidationException($"Property {referenceProperty.Name} is flagged 'Required' and was null!");
            }

            // Check if there is a relation that represents this reference
            if (referencedResource != null && matches.Any(m => referencedResource == context.ResolveReference(m)))
            {
                return(null);
            }

            // Get all references of this resource with the same relation information
            var currentReferences = CurrentReferences(resource, referenceAtt);

            // Try to find a match that is not used in any reference
            var relMatch = (from match in matches
                            where currentReferences.All(cr => cr != context.ResolveReference(match))
                            select match).FirstOrDefault();
            var relEntity = relMatch?.Entity;

            if (relEntity == null && referencedResource != null)
            {
                // Create a new relation
                relEntity = CreateRelationForProperty(context, relationRepo, referenceAtt);
                SetOnTarget(referencedResource, resource, referenceAtt);
            }
            else if (relEntity != null && referencedResource == null)
            {
                // Delete a relation, that no longer exists
                ClearOnTarget(context.ResolveReference(relMatch), resource, referenceAtt);
                relationRepo.Remove(relEntity);
                return(null);
            }
            // ReSharper disable once ConditionIsAlwaysTrueOrFalse <<- To identify the remaining case
            else if (relEntity == null && referencedResource == null)
            {
                // Relation did not exist before and still does not
                return(null);
            }
            // Relation was updated, make sure the backlinks match
            else
            {
                ClearOnTarget(context.ResolveReference(relMatch), resource, referenceAtt);
                SetOnTarget(referencedResource, resource, referenceAtt);
            }

            // Set source and target of the relation depending on the reference roles
            var referencedEntity = GetOrCreateEntity(context, referencedResource);

            UpdateRelationEntity(entity, referencedEntity, relEntity, referenceAtt);

            // Return referenced resource if it is new
            return(referencedResource.Id == 0 ? referencedResource : null);
        }