tools/AutoMapper/QueryableExtensions/Impl/QueryDataSourceInjection.cs (105 lines of code) (raw):

using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using AutoMapper.Configuration; using AutoMapper.Internal; namespace AutoMapper.QueryableExtensions.Impl { using MemberPaths = IEnumerable<IEnumerable<MemberInfo>>; using IObjectDictionary = IDictionary<string, object>; public interface IQueryDataSourceInjection<TSource> { /// <summary> /// Creates the mapped query with an optional inspector /// </summary> /// <typeparam name="TDestination">The type of the destination.</typeparam> /// <returns></returns> ISourceInjectedQueryable<TDestination> For<TDestination>(); ISourceInjectedQueryable<TDestination> For<TDestination>(object parameters, params Expression<Func<TDestination, object>>[] membersToExpand); ISourceInjectedQueryable<TDestination> For<TDestination>(params Expression<Func<TDestination, object>>[] membersToExpand); ISourceInjectedQueryable<TDestination> For<TDestination>(IObjectDictionary parameters, params string[] membersToExpand); IQueryDataSourceInjection<TSource> UsingInspector(SourceInjectedQueryInspector inspector); /// <summary> /// ExpressionVisitors called before MappingVisitor itself is executed /// </summary> /// <param name="visitors">The visitors.</param> /// <returns></returns> IQueryDataSourceInjection<TSource> BeforeProjection(params ExpressionVisitor[] visitors); /// <summary> /// ExpressionVisitors called after the MappingVisitor itself is executed /// </summary> /// <param name="visitors">The visitors.</param> /// <returns></returns> IQueryDataSourceInjection<TSource> AfterProjection(params ExpressionVisitor[] visitors); /// <summary> /// Allows specifying a handler that will be called when the underlying QueryProvider encounters an exception. /// This is especially useful if you expose the resulting IQueryable in e.g. a WebApi controller where /// you do not call "ToList" yourself and therefore cannot catch exceptions /// </summary> /// <param name="exceptionHandler">The exception handler.</param> /// <returns></returns> IQueryDataSourceInjection<TSource> OnError(Action<Exception> exceptionHandler); } public class QueryDataSourceInjection<TSource> : IQueryDataSourceInjection<TSource> { private readonly IQueryable<TSource> _dataSource; private readonly IMapper _mapper; private readonly List<ExpressionVisitor> _beforeMappingVisitors = new List<ExpressionVisitor>(); private readonly List<ExpressionVisitor> _afterMappingVisitors = new List<ExpressionVisitor>(); private readonly ExpressionVisitor _sourceExpressionTracer = null; private readonly ExpressionVisitor _destinationExpressionTracer = null; private Action<Exception> _exceptionHandler = x => { }; private MemberPaths _membersToExpand; private IObjectDictionary _parameters; private SourceInjectedQueryInspector _inspector; public QueryDataSourceInjection(IQueryable<TSource> dataSource, IMapper mapper) { _dataSource = dataSource; _mapper = mapper; } public ISourceInjectedQueryable<TDestination> For<TDestination>() => CreateQueryable<TDestination>(); public ISourceInjectedQueryable<TDestination> For<TDestination>(object parameters, params Expression<Func<TDestination, object>>[] membersToExpand) { _parameters = GetParameters(parameters); _membersToExpand = ProjectionExpression.GetMemberPaths(membersToExpand); return CreateQueryable<TDestination>(); } public ISourceInjectedQueryable<TDestination> For<TDestination>(params Expression<Func<TDestination, object>>[] membersToExpand) { _membersToExpand = ProjectionExpression.GetMemberPaths(membersToExpand); return CreateQueryable<TDestination>(); } public ISourceInjectedQueryable<TDestination> For<TDestination>(IObjectDictionary parameters, params string[] membersToExpand) { _parameters = parameters; _membersToExpand = ProjectionExpression.GetMemberPaths(typeof(TDestination), membersToExpand); return CreateQueryable<TDestination>(); } public IQueryDataSourceInjection<TSource> UsingInspector(SourceInjectedQueryInspector inspector) { _inspector = inspector; if (_sourceExpressionTracer != null) _beforeMappingVisitors.Insert(0, _sourceExpressionTracer); if (_destinationExpressionTracer != null) _afterMappingVisitors.Add(_destinationExpressionTracer); return this; } /// <summary> /// ExpressionVisitors called before MappingVisitor itself is executed /// </summary> /// <param name="visitors">The visitors.</param> /// <returns></returns> public IQueryDataSourceInjection<TSource> BeforeProjection(params ExpressionVisitor[] visitors) { foreach (var visitor in visitors.Where(visitor => !_beforeMappingVisitors.Contains(visitor))) { _beforeMappingVisitors.Add(visitor); } return this; } /// <summary> /// ExpressionVisitors called after the MappingVisitor itself is executed /// </summary> /// <param name="visitors">The visitors.</param> /// <returns></returns> public IQueryDataSourceInjection<TSource> AfterProjection(params ExpressionVisitor[] visitors) { foreach (var visitor in visitors.Where(visitor => !_afterMappingVisitors.Contains(visitor))) { _afterMappingVisitors.Add(visitor); } return this; } /// <summary> /// Allows specifying a handler that will be called when the underlying QueryProvider encounters an exception. /// This is especially useful if you expose the resulting IQueryable in e.g. a WebApi controller where /// you do not call "ToList" yourself and therefore cannot catch exceptions /// </summary> /// <param name="exceptionHandler">The exception handler.</param> /// <returns></returns> public IQueryDataSourceInjection<TSource> OnError(Action<Exception> exceptionHandler) { _exceptionHandler = exceptionHandler; return this; } private ISourceInjectedQueryable<TDestination> CreateQueryable<TDestination>() => new SourceSourceInjectedQuery<TSource, TDestination>(_dataSource, new TDestination[0].AsQueryable(), _mapper, _beforeMappingVisitors, _afterMappingVisitors, _exceptionHandler, _parameters, _membersToExpand, _inspector); private static IObjectDictionary GetParameters(object parameters) { return (parameters ?? new object()).GetType() .GetDeclaredProperties() .ToDictionary(pi => pi.Name, pi => pi.GetValue(parameters, null)); } } }