/// <summary>
		/// Retrieves multiple nodes from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a <see cref="Nodeset"/>.</returns>
		protected override Nodeset DoRetrieveNodeset(IMansionContext context, Query query)
		{
			// retrieve the nodeset
			var nodeset = DecoratedRepository.RetrieveNodeset(context, query);

			// attach the event handler to each node
			foreach (var node in nodeset.Nodes)
			{
				// get the node listeners
				var listeners = GetListeners(context, node.Pointer.Type);

				// attach missing property event
				var node1 = node;
				node.MissingProperty += (sender, args) =>
				                        {
				                        	foreach (var nodeListener in listeners)
				                        	{
				                        		object value;
				                        		if (!nodeListener.TryResolveMissingProperty(context, node1, args.PropertyName, out value))
				                        			continue;
				                        		args.Value = value;
				                        		break;
				                        	}
				                        };
			}

			// return the nodeset
			return nodeset;
		}
		/// <summary>
		/// Processes the <paramref name="parameters"/> and turn them into <paramref name="query"/>.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="parameters">The parameters which to process.</param>
		/// <param name="query">The <see cref="Query"/> in which to set the parameters.</param>
		protected override void DoProcess(IMansionContext context, IPropertyBag parameters, Query query)
		{
			// check for search
			string where;
			if (parameters.TryGetAndRemove(context, "sqlWhere", out where) && !string.IsNullOrEmpty(where))
				query.Add(new SqlWhereSpecification(where));
		}
Example #3
0
		/// <summary>
		/// Performs a search using the specified <paramref name="query"/>.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> on which to search.</param>
		/// <returns>Returns the resulting <see cref="RecordSet"/>.</returns>
		/// <exception cref="ArgumentNullException">Thrown if one of the parameters is null.</exception>
		/// <exception cref="ConnectionException">Thrown if a error occurred while executing the search query.</exception>
		public RecordSet Search(IMansionContext context, Query query)
		{
			// validate arguments
			if (context == null)
				throw new ArgumentNullException("context");
			if (query == null)
				throw new ArgumentNullException("query");

			// find the root type for this query
			var rootType = query.TypeHints.FindCommonAncestor(context);

			// resolve the index definitions of this record
			var indexDefinitions = indexDefinitionResolver.Resolve(context, rootType);

			// determine the index which to use to perform the search
			var indexDefinitionTypeMappingPair = SelectBestIndexForQuery(rootType, query, indexDefinitions);

			// create a search descriptor
			var search = new SearchQuery(indexDefinitionTypeMappingPair.Item1, indexDefinitionTypeMappingPair.Item2);

			// map all the query components to the search descriptor
			foreach (var component in query.Components)
				converters.Elect(context, component).Map(context, query, component, search);

			// execute the query
			return Search(context, search);
		}
		/// <summary>
		/// Retrieves a single node from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a single <see cref="Node"/>.</returns>
		protected override Node DoRetrieveSingleNode(IMansionContext context, Query query)
		{
			// retrieve the node
			var node = DecoratedRepository.RetrieveSingleNode(context, query);
			if (node == null)
				return null;

			// get the node listeners
			var listeners = GetListeners(context, node.Pointer.Type);

			// attach missing property event
			node.MissingProperty += (sender, args) =>
			                        {
			                        	foreach (var nodeListener in listeners)
			                        	{
			                        		object value;
			                        		if (!nodeListener.TryResolveMissingProperty(context, node, args.PropertyName, out value))
			                        			continue;
			                        		args.Value = value;
			                        		break;
			                        	}
			                        };

			// return the node
			return node;
		}
		/// <summary>
		/// Retrieves a <see cref="Dataset"/> from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a <see cref="Dataset"/> containing the results.</returns>
		/// <exception cref="ArgumentNullException">Thrown if <paramref name="context"/> or <paramref name="query"/> is null.</exception>
		protected override RecordSet DoRetrieve(IMansionContext context, Query query)
		{
			// select the engine
			var searcher = SelectQueryEngine(context, query);

			// execute the query
			return searcher.Retrieve(context, query);
		}
		/// <summary>
		/// Retrieves multiple nodes from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a <see cref="Nodeset"/>.</returns>
		protected override Nodeset DoRetrieveNodeset(IMansionContext context, Query query)
		{
			// check if this query is not cacheable
			if (!query.IsCacheable())
				return DecoratedRepository.RetrieveNodeset(context, query);

			// create the cache key for this node
			var cacheKey = query.CalculateCacheKey("Nodeset_Query_");

			// return the node
			return cachingService.GetOrAdd(context, cacheKey, () => DecoratedRepository.RetrieveNodeset(context, query).AsCacheableObject(query));
		}
		/// <summary>
		/// Retrieves multiple nodes from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns></returns>
		public Nodeset RetrieveNodeset(IMansionContext context, Query query)
		{
			// validate arguments
			if (context == null)
				throw new ArgumentNullException("context");
			if (query == null)
				throw new ArgumentNullException("query");
			CheckDisposed();

			// invoke template method
			return DoRetrieveNodeset(context, query);
		}
		/// <summary>
		/// Retrieves multiple nodes from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns></returns>
		protected override Nodeset DoRetrieveNodeset(IMansionContext context, Query query)
		{
			// create the connection and command
			using (var connection = CreateConnection())
			using (var command = context.Nucleus.CreateInstance<SelectNodeCommand>())
			{
				// init the command with the query
				command.Prepare(context, connection, query);

				// execute the command
				return command.Execute(context);
			}
		}
		/// <summary>
		/// Requests this voter to cast a vote.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="subject">The subject.</param>
		/// <returns>Returns the result of the vote.</returns>
		protected override VoteResult DoVote(IMansionContext context, Query subject)
		{
			// check if ElasticSearch is disabled
			if (!Configuration.IsEnabled)
				return VoteResult.Refrain;

			// find the root type for this query
			var rootType = subject.TypeHints.FindCommonAncestor(context);

			// resolve the index definitions of this record
			var indexDefinitions = indexDefinitionResolver.Resolve(context, rootType);

			// check if there is an index defined for this query
			return indexDefinitions.Any() ? VoteResult.MediumInterest : VoteResult.Refrain;
		}
		/// <summary>
		/// Turns the given <paramref name="obj"/> in a cacheable object.
		/// </summary>
		/// <param name="obj">The <see cref="Record"/> for which to create the cacheable object.</param>
		/// <param name="query">The <see cref="Query"/> which resulted in the given <paramref name="obj"/>.</param>
		/// <returns>Returns the <see cref="CachedObject{TObject}"/>.</returns>
		public static CachedObject<Record> AsCacheableObject(this Record obj, Query query)
		{
			// validate arguments
			if (query == null)
				throw new ArgumentNullException("query");

			// create a new cacheable object
			var cacheable = new CachedObject<Record>(obj);

			// if the result is found, cache it by it's id
			ChildOfSpecification childOfSpecification;
			if (obj != null)
			{
				// generate an ID for this specific record
				var recordIdCacheKey = obj.CalculateIdCacheKey();

				// add that cache key as the dependency
				cacheable.Add((StringCacheKeyDependency) recordIdCacheKey);
			}
			else if (query.TryGetSpecification(out childOfSpecification))
			{
				// cache on the parent tree Id
				var parentTreeIdCacheKey = childOfSpecification.ParentPointer.CalculateTreeIdCacheKey();

				// add that cache key as the dependency
				cacheable.Add((StringCacheKeyDependency) parentTreeIdCacheKey);
			}
			else
			{
				// add the repository modified cache key
				cacheable.Add(CachingRepositoryDecorator.RepositoryModifiedDependency);
			}

			// return the cacheable  object
			return cacheable;
		}
		/// <summary>
		/// Turns the given <paramref name="obj"/> in a cacheable object.
		/// </summary>
		/// <param name="obj">The <see cref="Nodeset"/> for which to create the cacheable object.</param>
		/// <param name="query">The <see cref="Query"/> which resulted in the given <paramref name="obj"/>.</param>
		/// <returns>Returns the <see cref="CachedObject{TObject}"/>.</returns>
		/// <exception cref="ArgumentNullException">Thrown if <paramref name="obj"/> is null.</exception>
		public static CachedObject<Nodeset> AsCacheableObject(this Nodeset obj, Query query)
		{
			// validate arguments
			if (obj == null)
				throw new ArgumentNullException("obj");
			if (query == null)
				throw new ArgumentNullException("query");

			// create a new cacheable object
			var cacheable = new CachedObject<Nodeset>(obj);

			ChildOfSpecification childOfSpecification;
			if (query.TryGetSpecification(out childOfSpecification))
			{
				// cache on the parent tree Id
				var parentTreeIdCacheKey = childOfSpecification.ParentPointer.CalculateTreeIdCacheKey();

				// add that cache key as the dependency
				cacheable.Add((StringCacheKeyDependency) parentTreeIdCacheKey);
			}
			else
			{
				// add the repository modified cache key
				cacheable.Add(CachingRepositoryDecorator.RepositoryModifiedDependency);
			}

			// return the cacheable  object
			return cacheable;
		}
		/// <summary>
		/// Retrieves multiple nodes from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a <see cref="Nodeset"/>.</returns>
		protected override Nodeset DoRetrieveNodeset(IMansionContext context, Query query)
		{
			return searcher.SearchNodes(context, query);
		}
		/// <summary>
		/// Retrieves a single node from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a single <see cref="Node"/>.</returns>
		protected override Node DoRetrieveSingleNode(IMansionContext context, Query query)
		{
			return searcher.SearchNodes(context, query).Nodes.FirstOrDefault();
		}
		/// <summary>
		/// Retrieves a <see cref="Dataset"/> from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a <see cref="Dataset"/> containing the results.</returns>
		protected override RecordSet DoRetrieve(IMansionContext context, Query query)
		{
			return searcher.Search(context, query);
		}
		/// <summary>
		/// Retrieves a single record from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a single <see cref="Record"/> or null when no result is found.</returns>
		/// <exception cref="ArgumentNullException">Thrown if <paramref name="context"/> or <paramref name="query"/> is null.</exception>
		protected override Record DoRetrieveSingle(IMansionContext context, Query query)
		{
			return searcher.Search(context, query).Records.FirstOrDefault();
		}
		/// <summary>
		/// Copies an existing node in this repository to a new node.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="pointer">The pointer to the node which will be copied.</param>
		/// <param name="targetParentPointer">The pointer to the parent to which the copied node is added.</param>
		/// <returns>Returns the copied node.</returns>
		protected override Node DoCopyNode(IMansionContext context, NodePointer pointer, NodePointer targetParentPointer)
		{
			// create a query to retrieve the new node
			var selectQuery = new Query();

			// build the query
			using (var connection = CreateConnection())
			using (var transaction = connection.BeginTransaction())
			{
				// retrieve the nodes
				// TODO: retrieve the nodes within the same transaction
				var nodeToCopy = RetrieveSingleNode(context, new Query().Add(new IsPropertyEqualSpecification("id", pointer.Id)));
				if (nodeToCopy == null)
					throw new ArgumentNullException(string.Format("Could not find node with pointer '{0}'", pointer));
				var targetParentNode = RetrieveSingleNode(context, new Query().Add(new IsPropertyEqualSpecification("id", targetParentPointer.Id)));
				if (targetParentNode == null)
					throw new ArgumentNullException(string.Format("Could not find node with pointer '{0}'", targetParentPointer));

				// create the copy query
				using (var command = context.Nucleus.CreateInstance<CopyNodeCommand>())
				{
					// init the command
					command.Prepare(context, connection, transaction, nodeToCopy, targetParentNode);

					// execute the command
					try
					{
						// execute the query

						var copiedNodeId = command.Execute();

						selectQuery.Add(new IsPropertyEqualSpecification("id", copiedNodeId));

						// woohoo it worked!
						transaction.Commit();
					}
					catch (Exception)
					{
						// something terrible happened, revert everything
						transaction.Rollback();
						throw;
					}
				}
			}

			// return the created node
			return RetrieveSingleNode(context, selectQuery);
		}
		/// <summary>
		/// Retrieves a <see cref="Dataset"/> from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a <see cref="Dataset"/> containing the results.</returns>
		/// <exception cref="ArgumentNullException">Thrown if <paramref name="context"/> or <paramref name="query"/> is null.</exception>
		protected abstract RecordSet DoRetrieve(IMansionContext context, Query query);
		/// <summary>
		/// Retrieves multiple nodes from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns></returns>
		protected abstract Nodeset DoRetrieveNodeset(IMansionContext context, Query query);
		/// <summary>
		/// Select the best <see cref="BaseQueryEngine"/> to execute the <paramref name="query"/> on.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which needs to be executed.</param>
		/// <returns>Returns the <see cref="BaseQueryEngine"/> which to use to execute <paramref name="query"/>.</returns>
		private BaseQueryEngine SelectQueryEngine(IMansionContext context, Query query)
		{
			return queryEngines.Elect(context, query, engines => engines.OrderByPriority().First());
		}
		/// <summary>
		/// Retrieves a <see cref="Dataset"/> from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a <see cref="Dataset"/> containing the results.</returns>
		protected override RecordSet DoRetrieve(IMansionContext context, Query query)
		{
			return repository.Retrieve(context, query);
		}
		/// <summary>
		/// Retrieves a single node from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a single <see cref="Node"/>.</returns>
		protected override Node DoRetrieveSingleNode(IMansionContext context, Query query)
		{
			return repository.RetrieveSingleNode(context, query);
		}
		/// <summary>
		/// Requests this voter to cast a vote.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="subject">The subject.</param>
		/// <returns>Returns the result of the vote.</returns>
		protected override VoteResult DoVote(IMansionContext context, Query subject)
		{
			IQueryComponentConverter converter;
			return subject.Components.All(component => Election<IQueryComponentConverter, QueryComponent>.TryElect(context, converters, component, out converter)) ? VoteResult.LowInterest : VoteResult.Refrain;
		}
		/// <summary>
		/// Retrieves a single node from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns the node.</returns>
		protected abstract Node DoRetrieveSingleNode(IMansionContext context, Query query);
		/// <summary>
		/// Retrieves a single record from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a single <see cref="Record"/> or null when no result is found.</returns>
		/// <exception cref="ArgumentNullException">Thrown if <paramref name="context"/> or <paramref name="query"/> is null.</exception>
		protected override Record DoRetrieveSingle(IMansionContext context, Query query)
		{
			return DecoratedRepository.RetrieveSingle(context, query);
		}
		/// <summary>
		/// Retrieves a single record from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a single <see cref="Record"/> or null when no result is found.</returns>
		/// <exception cref="ArgumentNullException">Thrown if <paramref name="context"/> or <paramref name="query"/> is null.</exception>
		protected abstract Record DoRetrieveSingle(IMansionContext context, Query query);
		/// <summary>
		/// Retrieves multiple nodes from this repository.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="query">The <see cref="Query"/> which to execute.</param>
		/// <returns>Returns a <see cref="Nodeset"/>.</returns>
		protected override Nodeset DoRetrieveNodeset(IMansionContext context, Query query)
		{
			return DecoratedRepository.RetrieveNodeset(context, query);
		}
Example #27
0
		/// <summary>
		/// Selects the best <see cref="IndexDefinition"/> for the given <paramref name="query"/>.
		/// </summary>
		/// <param name="type">The <see cref="ITypeDefinition"/>.</param>
		/// <param name="query">The <see cref="Query"/>.</param>
		/// <param name="indexDefinitions">The candidate <see cref="IndexDefinition"/>s.</param>
		/// <returns>Returns a<see cref="Tuple{IndexDefinition,TypeMapping}"/> containing the <see cref="IndexDefinition"/> and <see cref="TypeMapping"/>.</returns>
		private static Tuple<IndexDefinition, TypeMapping> SelectBestIndexForQuery(ITypeDefinition type, Query query, IEnumerable<IndexDefinition> indexDefinitions)
		{
			// first get the type mapping for the root type
			var indexDefinitionTypeMappingPairs = indexDefinitions.Select(candidate => new Tuple<IndexDefinition, TypeMapping>(candidate, candidate.FindTypeMapping(type.Name)));

			// return the type mapping which has to most fields matching the query
			return indexDefinitionTypeMappingPairs.OrderByDescending(pair => pair.Item2.Properties.Select(prop => prop.Key).Intersect(query.PropertyHints, StringComparer.OrdinalIgnoreCase).Count()).First();
		}
		/// <summary>
		/// Moves an existing node in this repository to a new parent node.
		/// </summary>
		/// <param name="context">The <see cref="IMansionContext"/>.</param>
		/// <param name="pointer">The pointer to the node which will be moved.</param>
		/// <param name="newParentPointer">The pointer to the parent to which the node is moved.</param>
		/// <returns>Returns the moved node.</returns>m
		protected override Node DoMoveNode(IMansionContext context, NodePointer pointer, NodePointer newParentPointer)
		{
			// build the query
			using (var connection = CreateConnection())
			using (var transaction = connection.BeginTransaction())
			using (var command = context.Nucleus.CreateInstance<MoveNodeCommand>())
			{
				// init the command
				command.Prepare(context, connection, transaction, pointer, newParentPointer);

				// execute the command
				try
				{
					// execute the query
					command.Execute();

					// woohoo it worked!
					transaction.Commit();
				}
				catch (Exception)
				{
					// something terrible happened, revert everything
					transaction.Rollback();
					throw;
				}
			}

			// return the moved node
			var selectQuery = new Query().Add(new IsPropertyEqualSpecification("id", pointer.Id));
			return RetrieveSingleNode(context, selectQuery);
		}