protected override Expression VisitMemberAccess(MemberExpression memberExpression)
		{
			MemberExpression expression; 
			var visited = new List<MemberExpression>();
			var root = memberExpression.Expression.StripForIncludeScanning();
			var memberIsDataAccessObjectGatheringForProjection = false;

			// Don't perform implicit joins for RelatedDataAccessObject collections as those currently turn into N+1 queries
			if (this.nesting < 1 && (memberExpression.Type.GetSequenceElementType()?.IsDataAccessObjectType() ?? false))
			{
				return base.VisitMemberAccess(memberExpression);
			}
			
			if (memberExpression.Type.IsTypeRequiringJoin())
			{
				if (this.forProjection)
				{
					memberIsDataAccessObjectGatheringForProjection = true;

					expression = memberExpression.StripForIncludeScanning() as MemberExpression;
				}
				else
				{
					expression = memberExpression.Expression.StripForIncludeScanning() as MemberExpression;

					if (expression == null || !expression.Expression.StripForIncludeScanning().Type.IsTypeRequiringJoin())
					{
						return memberExpression;
					}
				}
			}
			else
			{
				if (memberExpression.Expression?.StripForIncludeScanning() == null)
				{
					return memberExpression;
				}

				var typeDescriptor = this.model.TypeDescriptorProvider.GetTypeDescriptor(memberExpression.Expression.StripForIncludeScanning().Type);

				if (typeDescriptor == null)
				{
					return Expression.MakeMemberAccess(this.Visit(memberExpression.Expression.StripForIncludeScanning()), memberExpression.Member);
				}

				var property = typeDescriptor.GetPropertyDescriptorByPropertyName(memberExpression.Member.Name);

				if (property.IsPrimaryKey && memberExpression.Expression.StripForIncludeScanning() is MemberExpression)
				{
					expression = ((MemberExpression)memberExpression.Expression.StripForIncludeScanning()).Expression.StripForIncludeScanning() as MemberExpression;
				}
				else
				{
					expression = memberExpression.Expression.StripForIncludeScanning() as MemberExpression;
				}
			}

			var currentExpression = expression.StripForIncludeScanning() as MemberExpression;

			while (currentExpression?.Member is PropertyInfo)
			{
				visited.Add(currentExpression);

				root = currentExpression.Expression.StripForIncludeScanning();

				currentExpression = root as MemberExpression;

				if (currentExpression == null)
				{
					root = this.GetExpression(root).StripForIncludeScanning();
					currentExpression = root as MemberExpression;
				}
			}

			var includedPathSkip = 0;

			var i = 0;

			foreach (var current in visited)
			{
				if (!current.Member.ReflectedType.IsTypeRequiringJoin()
					|| current == this.currentParent /* @see: Test_Select_Project_Related_Object_And_Include1 */)
				{
					includedPathSkip = visited.Count - i;
					
					break;
				}

				i++;
			}

			visited.Reverse();

			i = 0;
			currentExpression = expression;

			while (currentExpression?.Member is PropertyInfo)
			{
				var path = new PropertyPath(c => c.Name, visited.Select(c=> (PropertyInfo)c.Member).Take(visited.Count - i).ToArray());
				
				ReferencedRelatedObject objectInfo;

				if (path.Length == 0)
				{
					break;
				}

				if (!path.Last.ReflectedType.IsTypeRequiringJoin())
				{
					this.rootExpressionsByPath[path] = currentExpression;
					
					break;
				}

				if (!this.results.TryGetValue(path, out objectInfo))
				{
					var includedPropertyPath = new PropertyPath(c => c.Name, path.Skip(includedPathSkip));

					objectInfo = new ReferencedRelatedObject(path, includedPropertyPath, sourceParameterExpression);

					this.results[path] = objectInfo;
				}

				this.referencedRelatedObjects.Add(objectInfo);

				if (memberIsDataAccessObjectGatheringForProjection)
				{
					objectInfo.TargetExpressions.Add(currentExpression);
				}
				else if (currentExpression == expression)
				{
					objectInfo.TargetExpressions.Add(expression);
				}

				i++;
				currentExpression = currentExpression.Expression.StripForIncludeScanning() as MemberExpression;
			}

			return memberExpression;
		}