Skip to content

Commit

Permalink
fix #266 support mapper.From(queryable).ProjectToType<T>()
Browse files Browse the repository at this point in the history
  • Loading branch information
chaowlert committed Oct 22, 2020
1 parent 4b5b49e commit 52fe4e4
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/Mapster.EFCore/Mapster.EFCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Mapster" Version="4.1.1" />
<PackageReference Include="Mapster" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
</ItemGroup>

<ItemGroup>
<None Include="icon.png" Pack="true" PackagePath="\"/>
<None Include="icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>
125 changes: 125 additions & 0 deletions src/Mapster.EFCore/MapsterQueryable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Mapster.EFCore
{
class MapsterQueryable : IQueryable
{
private readonly IQueryable _queryable;
public MapsterQueryable(IQueryable queryable, IAdapterBuilder builder)
{
_queryable = queryable;
this.Provider = new MapsterQueryableProvider(queryable.Provider, builder);
}

IEnumerator IEnumerable.GetEnumerator() => this.Provider.Execute<IEnumerable>(this.Expression).GetEnumerator();

public Type ElementType => _queryable.ElementType;
public Expression Expression => _queryable.Expression;
public IQueryProvider Provider { get; }
}
class MapsterQueryable<T> : MapsterQueryable, IQueryable<T>, IAsyncEnumerable<T>
{
public MapsterQueryable(IQueryable<T> queryable, IAdapterBuilder builder) :
base(queryable, builder) { }
public IEnumerator<T> GetEnumerator() => this.Provider.Execute<IEnumerable<T>>(this.Expression).GetEnumerator();
IAsyncEnumerator<T> IAsyncEnumerable<T>.GetEnumerator() => ((IAsyncQueryProvider) this.Provider).ExecuteAsync<T>(this.Expression).GetEnumerator();
}

class MapsterQueryableProvider : IAsyncQueryProvider
{
private readonly IQueryProvider _provider;
private readonly IAdapterBuilder _builder;

public MapsterQueryableProvider(IQueryProvider provider, IAdapterBuilder builder)
{
_provider = provider;
_builder = builder;
}

public IQueryable CreateQuery(Expression expression)
{
return new MapsterQueryable(_provider.CreateQuery(expression), _builder);
}

public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new MapsterQueryable<TElement>(_provider.CreateQuery<TElement>(expression), _builder);
}

public object Execute(Expression expression)
{
using (_builder.CreateMapContextScope())
{
return _provider.Execute(expression);
}
}

public TResult Execute<TResult>(Expression expression)
{
using (_builder.CreateMapContextScope())
{
return _provider.Execute<TResult>(expression);
}
}

public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
{
var enumerable = ((IAsyncQueryProvider) _provider).ExecuteAsync<TResult>(expression);
return new MapsterAsyncEnumerable<TResult>(enumerable, _builder);
}

public async Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
using (_builder.CreateMapContextScope())
{
return await ((IAsyncQueryProvider) _provider).ExecuteAsync<TResult>(expression, cancellationToken);
}
}
}

class MapsterAsyncEnumerable<T> : IAsyncEnumerable<T>
{
private readonly IAsyncEnumerable<T> _enumerable;
private readonly IAdapterBuilder _builder;
public MapsterAsyncEnumerable(IAsyncEnumerable<T> enumerable, IAdapterBuilder builder)
{
_enumerable = enumerable;
_builder = builder;
}

public IAsyncEnumerator<T> GetEnumerator()
{
return new MapsterAsyncEnumerator<T>(_enumerable.GetEnumerator(), _builder);
}
}

class MapsterAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IAsyncEnumerator<T> _enumerator;
private readonly MapContextScope _scope;
public MapsterAsyncEnumerator(IAsyncEnumerator<T> enumerator, IAdapterBuilder builder)
{
_enumerator = enumerator;
_scope = builder.CreateMapContextScope();
}

public void Dispose()
{
_scope.Dispose();
}

public Task<bool> MoveNext(CancellationToken cancellationToken)
{
return _enumerator.MoveNext(cancellationToken);
}

public T Current => _enumerator.Current;
}
}
7 changes: 7 additions & 0 deletions src/Mapster.EFCore/TypeAdapterBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Mapster.EFCore;
using Microsoft.EntityFrameworkCore;

namespace Mapster
Expand Down Expand Up @@ -77,5 +78,11 @@ public static TypeAdapterBuilder<TSource> EntityFromContext<TSource>(this TypeAd
}
}, context.GetType().FullName);
}

public static IQueryable<TDestination> ProjectToType<TDestination>(this IAdapterBuilder<IQueryable> source)
{
var queryable = source.Source.ProjectToType<TDestination>(source.Config);
return new MapsterQueryable<TDestination>(queryable, source);
}
}
}
10 changes: 10 additions & 0 deletions src/Mapster/MapContext/MapContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Mapster.Utils;
using System;
using System.Collections.Generic;
using System.Threading;

namespace Mapster
{
Expand All @@ -14,8 +15,17 @@ namespace Mapster
/// </remarks>
public class MapContext
{
#if NETSTANDARD
private static readonly AsyncLocal<MapContext?> _localContext = new AsyncLocal<MapContext?>();
public static MapContext? Current
{
get => _localContext.Value;
set => _localContext.Value = value;
}
#else
[field: ThreadStatic]
public static MapContext? Current { get; set; }
#endif

private Dictionary<ReferenceTuple, object>? _references;
public Dictionary<ReferenceTuple, object> References => _references ??= new Dictionary<ReferenceTuple, object>();
Expand Down
7 changes: 4 additions & 3 deletions src/Mapster/MapContext/MapContextScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ public class MapContextScope : IDisposable
private readonly bool _isRootScope;
public MapContextScope()
{
this.Context = MapContext.Current!;
if (this.Context == null)
var context = MapContext.Current;
if (context == null)
{
_isRootScope = true;
this.Context = MapContext.Current = new MapContext();
MapContext.Current = context = new MapContext();
}
this.Context = context;
}

public void Dispose()
Expand Down
7 changes: 4 additions & 3 deletions src/Sample.AspNetCore/Controllers/SchoolController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public SchoolController(IMapper mapper, SchoolContext context)
[EnableQuery]
public IQueryable<CourseDto> GetCourses()
{
return _context.Courses.ProjectToType<CourseDto>(_mapper.Config);
return _mapper.From(_context.Courses).ProjectToType<CourseDto>();
}

// Async Sample
Expand All @@ -44,8 +44,9 @@ public async Task<EnrollmentDto> GetEnrollment([FromRoute] int id)
[HttpGet("student/{id}")]
public Task<StudentDto> GetStudent([FromRoute] int id)
{
return _context.Students.Where(it => it.ID == id)
.ProjectToType<StudentDto>(_mapper.Config)
var query = _context.Students.Where(it => it.ID == id);
return _mapper.From(query)
.ProjectToType<StudentDto>()
.FirstOrDefaultAsync();
}

Expand Down
10 changes: 10 additions & 0 deletions src/Sample.AspNetCore/NameFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Sample.AspNetCore
{
public class NameFormatter
{
public string Format(string firstName, string lastName)
{
return $"{firstName} {lastName}";
}
}
}
6 changes: 3 additions & 3 deletions src/Sample.AspNetCore/Sample.AspNetCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
<ItemGroup>
<PackageReference Include="ExpressionDebugger" Version="2.1.2" />
<PackageReference Include="Hellang.Middleware.ProblemDetails" Version="4.0.0" />
<PackageReference Include="Mapster.Async" Version="1.0.0" />
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.0" />
<PackageReference Include="Mapster.EFCore" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.3.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.6" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Mapster.Async\Mapster.Async.csproj" />
<ProjectReference Include="..\Mapster.DependencyInjection\Mapster.DependencyInjection.csproj" />
<ProjectReference Include="..\Mapster.EFCore\Mapster.EFCore.csproj" />
<ProjectReference Include="..\Mapster\Mapster.csproj" />
</ItemGroup>

Expand Down
5 changes: 3 additions & 2 deletions src/Sample.AspNetCore/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public void ConfigureServices(IServiceCollection services)
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddSingleton(GetConfiguredMappingConfig());
services.AddScoped<IMapper, ServiceMapper>();
services.AddSingleton<NameFormatter>();
services.AddProblemDetails();
services.AddOData();
}
Expand All @@ -52,10 +53,10 @@ private static TypeAdapterConfig GetConfiguredMappingConfig()
dto.CourseTitle = course.Title;
var student = await context.Students.FindAsync(dto.StudentID);
if (student != null)
dto.StudentName = $"{student.FirstMidName} {student.LastName}";
dto.StudentName = MapContext.Current.GetService<NameFormatter>().Format(student.FirstMidName, student.LastName);
});
config.NewConfig<Student, StudentDto>()
.Map(dest => dest.Name, src => $"{src.FirstMidName} {src.LastName}");
.Map(dest => dest.Name, src => MapContext.Current.GetService<NameFormatter>().Format(src.FirstMidName, src.LastName));
config.NewConfig<Course, CourseDto>()
.Map(dest => dest.CourseIDDto, src => src.CourseID)
.Map(dest => dest.CreditsDto, src => src.Credits)
Expand Down

0 comments on commit 52fe4e4

Please sign in to comment.