Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/AutoMapper.DI.Tests/AppDomainResolutionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ public AppDomainResolutionTests()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper(opt =>
{
opt.AddMaps(typeof(AppDomainResolutionTests));
});
services.AddAutoMapper(_ => { }, typeof(AppDomainResolutionTests));
_provider = services.BuildServiceProvider();
}

Expand Down
5 changes: 1 addition & 4 deletions src/AutoMapper.DI.Tests/AssemblyResolutionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ private static ServiceProvider BuildServiceProvider()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper(opt =>
{
opt.AddMaps(typeof(Source).GetTypeInfo().Assembly);
});
services.AddAutoMapper(_ => { }, typeof(Source).GetTypeInfo().Assembly);
var serviceProvider = services.BuildServiceProvider();
return serviceProvider;
}
Expand Down
5 changes: 1 addition & 4 deletions src/AutoMapper.DI.Tests/AttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ public void Should_not_register_static_instance_when_configured()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper(opt =>
{
opt.AddMaps(typeof(Source3));
});
services.AddAutoMapper(_ => { }, typeof(Source3));

var serviceProvider = services.BuildServiceProvider();

Expand Down
5 changes: 1 addition & 4 deletions src/AutoMapper.DI.Tests/DependencyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ public DependencyTests()
IServiceCollection services = new ServiceCollection();
services.AddTransient<ISomeService>(sp => new FooService(5));
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper(opt =>
{
opt.AddMaps(typeof(Source), typeof(Profile));
});
services.AddAutoMapper(_ => { }, typeof(Source), typeof(Profile));
_provider = services.BuildServiceProvider();

_provider.GetService<IConfigurationProvider>().AssertConfigurationIsValid();
Expand Down
3 changes: 1 addition & 2 deletions src/AutoMapper.DI.Tests/Integrations/ServiceLifetimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,8 @@ public void CanUseDefaultInjectedIMapperInSingletonService()
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper(opt =>
{
opt.AddMaps(GetType().Assembly);
opt.CreateMap<Foo, Bar>().ReverseMap();
});
}, GetType().Assembly);
var sp = services.BuildServiceProvider();
Bar actual;

Expand Down
15 changes: 3 additions & 12 deletions src/AutoMapper.DI.Tests/MultipleRegistrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,9 @@ public void Can_register_assembly_multiple_times()
var services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);

services.AddAutoMapper(opt =>
{
opt.AddMaps(typeof(MultipleRegistrationTests));
});
services.AddAutoMapper(opt =>
{
opt.AddMaps(typeof(MultipleRegistrationTests));
});
services.AddAutoMapper(opt =>
{
opt.AddMaps(typeof(MultipleRegistrationTests));
});
services.AddAutoMapper(_ => { }, typeof(MultipleRegistrationTests));
services.AddAutoMapper(_ => { }, typeof(MultipleRegistrationTests));
services.AddAutoMapper(_ => { }, typeof(MultipleRegistrationTests));
services.AddTransient<ISomeService, MutableService>();

var serviceProvider = services.BuildServiceProvider();
Expand Down
17 changes: 4 additions & 13 deletions src/AutoMapper.DI.Tests/ScopeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ public void Can_depend_on_scoped_services_as_transient_default()
{
var services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper(opt =>
{
opt.AddMaps(typeof(Source).Assembly);
});
services.AddAutoMapper(_ => { }, typeof(Source).Assembly);
services.AddScoped<ISomeService, MutableService>();

var provider = services.BuildServiceProvider();
Expand All @@ -39,11 +36,9 @@ public void Can_depend_on_scoped_services_as_scoped()
{
var services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper(opt =>
services.AddAutoMapper(_ =>
{
opt.ServiceLifetime = ServiceLifetime.Scoped;
opt.AddMaps(typeof(Source).Assembly);
});
}, [typeof(Source).Assembly], ServiceLifetime.Scoped);
services.AddScoped<ISomeService, MutableService>();

var provider = services.BuildServiceProvider();
Expand All @@ -66,11 +61,7 @@ public void Cannot_correctly_resolve_scoped_services_as_singleton()
{
var services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper(opt =>
{
opt.AddMaps(typeof(Source).Assembly);
opt.ServiceLifetime = ServiceLifetime.Singleton;
});
services.AddAutoMapper(_ => { }, [typeof(Source).Assembly], ServiceLifetime.Singleton);
services.AddScoped<ISomeService, MutableService>();

var provider = services.BuildServiceProvider();
Expand Down
5 changes: 2 additions & 3 deletions src/AutoMapper.DI.Tests/ServiceLifetimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ public void AddAutoMapperExtensionDefaultWithServiceLifetime()
var serviceCollection = new ServiceCollection();

//act
serviceCollection.AddAutoMapper(opt =>
serviceCollection.AddAutoMapper(_ =>
{
opt.ServiceLifetime = ServiceLifetime.Singleton;
});
}, new List<Assembly>(), ServiceLifetime.Singleton);
var serviceDescriptor = serviceCollection.FirstOrDefault(sd => sd.ServiceType == typeof(IMapper));

//assert
Expand Down
48 changes: 48 additions & 0 deletions src/AutoMapper.DI.Tests/ServiceProviderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

namespace AutoMapper.Extensions.Microsoft.DependencyInjection.Tests
{
using System;
using global::Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;

public class ServiceProviderTests
{
private readonly IServiceProvider _provider;

public ServiceProviderTests()
{
IServiceCollection services = new ServiceCollection();
services.AddTransient<ISomeService>(sp => new FooService(5));
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper((sp, _) =>
{
var service = sp.GetRequiredService<ISomeService>();
service.Modify(5);
}, typeof(Source), typeof(Profile));
_provider = services.BuildServiceProvider();

_provider.GetService<IConfigurationProvider>().AssertConfigurationIsValid();
}

[Fact]
public void ShouldResolveWithDependency()
{
var mapper = _provider.GetService<IMapper>();
var dest = mapper.Map<Source2, Dest2>(new Source2());

dest.ResolvedValue.ShouldBe(5);
}

[Fact]
public void ShouldConvertWithDependency()
{
var mapper = _provider.GetService<IMapper>();
var dest = mapper.Map<Source2, Dest2>(new Source2 { ConvertedValue = 5});

dest.ConvertedValue.ShouldBe(10);
}
}
}
5 changes: 2 additions & 3 deletions src/AutoMapper.DI.Tests/TypeResolutionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ public TypeResolutionTests()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddAutoMapper(opt =>
services.AddAutoMapper(_ =>
{
opt.AddMaps(typeof(Source));
});
}, typeof(Source));

_provider = services.BuildServiceProvider();
}
Expand Down
30 changes: 0 additions & 30 deletions src/AutoMapper/Configuration/MapperConfigurationExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,6 @@ public interface IMapperConfigurationExpression : IProfileExpression
/// Gets or sets the license key. You can find your license key in your <a href="https://luckypennysoftware.com/account">account</a>.
/// </summary>
string LicenseKey { get; set; }

/// <summary>
/// Gets or sets the default service lifetime. Used for services registered using <see cref="ServiceCollectionExtensions.AddAutoMapper(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{AutoMapper.IMapperConfigurationExpression})"/>
/// Defaults to <see cref="Microsoft.Extensions.DependencyInjection.ServiceLifetime.Transient"/>
/// </summary>
public ServiceLifetime ServiceLifetime { get; set; }
}
public sealed class MapperConfigurationExpression : Profile, IGlobalConfigurationExpression
{
Expand Down Expand Up @@ -146,30 +140,6 @@ public void CreateProfile(string profileName, Action<IProfileExpression> config)

Features<IGlobalFeature> IGlobalConfigurationExpression.Features { get; } = new();

void IGlobalConfigurationExpression.RegisterServices(IServiceCollection services)
{
foreach (var type in _scannedAssembles.SelectMany(a => a.GetTypes().Where(type => type.IsClass && !type.IsAbstract)))
{
if (TryGetAmType(type, out var amType))
{
// use try add to avoid double-registration
services.TryAdd(new ServiceDescriptor(type, type, ServiceLifetime));
}
}

return;

bool TryGetAmType(Type type, out Type amType)
{
amType = AmTypes
.Select(type.GetGenericInterface)
.FirstOrDefault(serviceType => serviceType != null);

return amType != null;
}
}


public void AddProfile(Profile profile) => _profiles.Add(profile);

public void AddProfile<TProfile>() where TProfile : Profile, new() => AddProfile(new TProfile());
Expand Down
6 changes: 0 additions & 6 deletions src/AutoMapper/Internal/InternalApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,6 @@ public interface IGlobalConfigurationExpression : IMapperConfigurationExpression
/// Must be zero for EF6. Can be greater than zero for EF Core.
/// </summary>
int RecursiveQueriesMaxDepth { get; set; }

/// <summary>
/// Registers services from assemblies used in <see cref="IMapperConfigurationExpression.AddMaps(System.Collections.Generic.IEnumerable{System.Reflection.Assembly})"/>
/// </summary>
/// <param name="services">Service collection</param>
void RegisterServices(IServiceCollection services);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IGlobalConfiguration : IConfigurationProvider
Expand Down
62 changes: 46 additions & 16 deletions src/AutoMapper/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,53 @@ namespace Microsoft.Extensions.DependencyInjection;
/// </summary>
public static class ServiceCollectionExtensions
{
static readonly Type[] AmTypes = [typeof(IValueResolver<,,>), typeof(IMemberValueResolver<,,,>), typeof(ITypeConverter<,>), typeof(IValueConverter<,>), typeof(IMappingAction<,>)];
public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction)
=> AddAutoMapperClasses(services, configAction);
=> AddAutoMapperClasses(services, (sp, cfg) => configAction?.Invoke(cfg), null);

// public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction)
// => AddAutoMapperClasses(services, configAction);
public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction, params Assembly[] assemblies)
=> AddAutoMapperClasses(services, (sp, cfg) => configAction?.Invoke(cfg), assemblies);

private static IServiceCollection AddAutoMapperClasses(IServiceCollection services, Action<IMapperConfigurationExpression> configAction)
{
configAction ??= _ => { };
public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction, params Assembly[] assemblies)
=> AddAutoMapperClasses(services, configAction, assemblies);

public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction, IEnumerable<Assembly> assemblies, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
=> AddAutoMapperClasses(services, (sp, cfg) => configAction?.Invoke(cfg), assemblies, serviceLifetime);

public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction, IEnumerable<Assembly> assemblies, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
=> AddAutoMapperClasses(services, configAction, assemblies, serviceLifetime);

var config = new MapperConfigurationExpression();
public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction, params Type[] profileAssemblyMarkerTypes)
=> AddAutoMapperClasses(services, (sp, cfg) => configAction?.Invoke(cfg), profileAssemblyMarkerTypes.Select(t => t.GetTypeInfo().Assembly));

configAction(config);

((IGlobalConfigurationExpression)config).RegisterServices(services);
public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction, params Type[] profileAssemblyMarkerTypes)
=> AddAutoMapperClasses(services, configAction, profileAssemblyMarkerTypes.Select(t => t.GetTypeInfo().Assembly));

services.AddSingleton(config);

public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction,
IEnumerable<Type> profileAssemblyMarkerTypes, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
=> AddAutoMapperClasses(services, (sp, cfg) => configAction?.Invoke(cfg), profileAssemblyMarkerTypes.Select(t => t.GetTypeInfo().Assembly), serviceLifetime);

public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction,
IEnumerable<Type> profileAssemblyMarkerTypes, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
=> AddAutoMapperClasses(services, configAction, profileAssemblyMarkerTypes.Select(t => t.GetTypeInfo().Assembly), serviceLifetime);

private static IServiceCollection AddAutoMapperClasses(IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction,
IEnumerable<Assembly> assembliesToScan, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
{
if (configAction != null)
{
services.AddOptions<MapperConfigurationExpression>().Configure<IServiceProvider>((options, sp) => configAction(sp, options));
}
if (assembliesToScan != null)
{
assembliesToScan = assembliesToScan.Where(a => !a.IsDynamic && a != typeof(Mapper).Assembly).Distinct();
services.Configure<MapperConfigurationExpression>(options => options.AddMaps(assembliesToScan));
foreach (var type in assembliesToScan.SelectMany(a => a.GetTypes().Where(type => type.IsClass && !type.IsAbstract && IsAmType(type))))
{
// use try add to avoid double-registration
services.TryAddTransient(type);
}
}
// Just return if we've already added AutoMapper to avoid double-registration
if (services.Any(sd => sd.ServiceType == typeof(IMapper)))
{
Expand All @@ -49,11 +78,12 @@ private static IServiceCollection AddAutoMapperClasses(IServiceCollection servic
services.AddSingleton<IConfigurationProvider>(sp =>
{
// A mapper configuration is required
var options = sp.GetRequiredService<MapperConfigurationExpression>();
return new MapperConfiguration(options, sp.GetRequiredService<ILoggerFactory>());
var options = sp.GetRequiredService<IOptions<MapperConfigurationExpression>>();
var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
return new MapperConfiguration(options.Value, loggerFactory);
});
services.Add(new(typeof(IMapper), sp => new Mapper(sp.GetRequiredService<IConfigurationProvider>(), sp.GetService), config.ServiceLifetime));

services.Add(new(typeof(IMapper), sp => new Mapper(sp.GetRequiredService<IConfigurationProvider>(), sp.GetService), serviceLifetime));
return services;
bool IsAmType(Type type) => Array.Exists(AmTypes, openType => type.GetGenericInterface(openType) != null);
}
}