I am using Automapper with our asp net core application. For the mappings from type A -> B and A -> C, I need an external service to inject some additional data. Therefore I have written a ValueResolver<T>, where T is either B or C. As C# doesn't have OR operator for types, I use a generic T here.
For clarity, my value resolver looks like this:
public class MyValueResolver<T>: IValueResolver<A, T, string>
{
private readonly IMyService _service;
public MyValueResolver(IMyService service)
{
_service = service;
}
public string Resolve(A source, T destination, string destMember, ResolutionContext context)
{
// do something using only source and _service.
return ...;
}
}
Now, in my startup I register automapper like this services.AddAutoMapper();. Therefore, when I use it anywhere in my application, I just have to inject IMapper in the constructor and it will resolve MyValueResolver with IMyService dependency automatically.
The problem arises in tests when I don't use DI and I need both MyValueResolver<B> and MyValueResolver<C>. I tried the following:
var mappingConfig = new MapperConfiguration(cfg => {
cfg.AddProfile(new MappingProfile());
cfg.ConstructServicesUsing(MyValueResolver =>
new MyValueResolver<B>(service));
cfg.ConstructServicesUsing(MyValueResolver =>
new MyValueResolver<C>(service));
});
var mapper = new Mapper(mappingConfig);
But this doesn't work, as it sees tries to use the latter statement for both cases and fails with an error that it cannot cast MyValueResolver<C> to MyValueResolver<B>. But the method doesn't accept types with generics defined (I assume it is a C# limitation?) like this cfg.ConstructServicesUsing(MyValueResolver<C> => new MyValueResolver<C>(service));
Is there any workaround for this, or maybe a different pattern I could use?
I was using ConstructServicesUsing in a completely wrong way.
It takes a function , where Type - is a type of a ValueResolver I want to resolve and Object is the resolved instance. You should return null for the types you don't want to resolve.
So it looks along the lines:
cfg => cfg.ConstructServicesUsing(type => type == typeof(MyValueResolver<B>) ? new MyValueResolver<B>(service) : null));
Credit goes to #LucianBargaoanu for this answer.
Related
In X class, I have the following code block, and I'm facing with "'QueueConsumer' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TConsumer' in the generic type or method 'ConsumerExtensions.Consumer(IReceiveEndpointConfigurator, Action<IConsumerConfigurator>)'" error.
cfg =>
{
cfg.Host(ServiceBusConnectionString);
cfg.ReceiveEndpoint(router.Name, e =>
{
e.Consumer<QueueConsumer>(); // I got the error in this line
});
}
In QueueConsumer, I use IConfiguration class with dependency injection. I know, if I use empty constructor I won't see above error but then I can't use IConfiguration. This is my QueueConsumer class:
public class QueueConsumer : IConsumer<TransferMessage>
{
public readonly IConfiguration _configuration;
public QueueConsumer(IConfiguration configuration)
{
_configuration = configuration;
}
So, do you any idea for how to avoid this problem? How can I use dependency injection with parameterless constructor?
Masstransit supports factories for the consumer:
Taken from the above link:
cfg.ReceiveEndpoint("order-service", e =>
{
// delegate consumer factory
e.Consumer(() => new SubmitOrderConsumer());
// another delegate consumer factory, with dependency
e.Consumer(() => new LogOrderSubmittedConsumer(Console.Out));
// a type-based factory that returns an object (specialized uses)
var consumerType = typeof(SubmitOrderConsumer);
e.Consumer(consumerType, type => Activator.CreateInstance(consumerType));
});
So you can inject any dependency you want here. You should also be able to use whatever DI Framework you want in/as such a factory method.
However, if you are using ASP.Net Core DI, please read the following for the integration that MassTransit has built in:
https://masstransit-project.com/usage/configuration.html#asp-net-core
I assume you are using MassTransit with RabbitMQ and vanilla DI for ASP.NET Core in this case you can do it.
You might need to add NuGet MassTransit.Extensions.DependencyInjection
When configuring services with AddMassTransit you need to use AddConsumer[s]
When configuring rabbit with UsingRabbitMq you need to use ConfigureConsumer
The final code might look similar to this (a snippet for hosted service in .Net 6)
// some code above
Host.CreateDefaultBuilder().ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(
opt =>
{
// Add all consumers from the assembly
opt.AddConsumers(typeof(CoolConsumer).Assembly);
opt.UsingRabbitMq((context, cfg) =>
{
// Spin up the RabbitMQ bus with values from config
cfg.Host(hostContext.Configuration["RabbitMQ:Host"],
hostContext.Configuration["RabbitMQ:VirtualHost"], h =>
{
h.Username(hostContext.Configuration["RabbitMQ:Username"]);
h.Password(hostContext.Configuration["RabbitMQ:Password"]);
});
cfg.ReceiveEndpoint("my-queue-name", p =>
{
p.ConfigureConsumer<CoolConsumer>(context);
});
});
});
});
// some additional code
In Dot Net Core convention for configuring services is as follows.
Method that Requires Action<> As Parameter
public static IServiceCollection AddAsResourceServer(this IServiceCollection services, Action<AuthMiddlewareOptions> action = null)
{
if(action != null)
{
AuthMiddlewareOptions authOptions = new AuthMiddlewareOptions();
action.Invoke(authOptions);
}
...
return services
}
This allows us to configure our service in startup as follows.
Startup ConfigureServices Method
services.AddAsResourceServer((a) =>
{
a.Audience = "Long Audience";
a.Issuer = "provider.com/oauthprovider";
});
This is really cool! I like this pattern. It works very well for allowing anyone to overwrite base configuration options.
However this seems very cumbersome when you already have an instance of the object. How would I create an instance of an object, convert it to an action, and pass that to the .AddAsResourceServer method?
I have tried:
Action<AuthMiddleWareOptions> auth = new Action<AuthMiddleWareOptions>()
/// The above way still requires a "target" in constructor so it doesnt work.
///so i still need to pass each
///property into it.
My consumers of the service may have just created an instance of AuthMiddleWareOptions and populated through appsettings!
They may have done something like this.
AuthMiddlewareOptions myoptions = new AuthMddlewareOptions();
Configuration.Bind("AuthMiddlewareOptions", myoptions);
Now that they have 'myoptions'. Is there a quick way to use that with the Action Parameter. Maybe like this?
AuthMiddlewareOptions myoptions = new AuthMddlewareOptions();
Configuration.Bind("AuthMiddlewareOptions", myoptions);
services.AddAsResourceServer((a) => SpecialThing(myoptions));
You can simply bind your configuration to the options instance provided by the method.
services.AddAsResourceServer(options =>
{
Configuration.Bind("AuthMiddlewareOptions", options);
});
You may register multiple configuration delegates for a single option class:
services.Configure<AuthMiddlewareOptions>(Configuration.GetSection("AuthMiddlewareOptions"));
services.AddAsResourceServer(options => SpecialThing(options));
And AddAsResourceServer should call services.Configure, as well:
public static IServiceCollection AddAsResourceServer(this IServiceCollection services, Action<AuthMiddlewareOptions> action = null)
{
if (action != null)
services.Configure(action);
// ...
return services;
}
Having this setup, AuthMiddlewareOptions is populated from the configuration section "AuthMiddlewareOptions", then the customization delegate (SpecialThing) has a chance to change the options filled at the previous step.
It's important that the order matters! If you swap the two lines, SpecialThing's changes will be overwritten by the values coming from the configuration.
You need to do it the other way arround.
I use for example the AddAuthorization since I do not find your method:
services.AddAuthorization(c =>
{
c.DefaultPolicy = null;
});
This can be configured using the following method:
public static void Config(AuthorizationOptions configure, IConfiguration configuration)
{
configure.DefaultPolicy = null;
//Configuration.Bind("AuthMiddlewareOptions", myoptions);
}
That way you can do:
services.AddAuthorization(c => Config(c, Configuration));
The problem is, that in these Action<T> methods you get the object T, which is already instantiated. So you need to copy the custom created object to it. With above we change the API, so it is configured on the correct object.
I tried many patterns, it wasn't until I hovered over a in the method to realize that it wasnt of type Action<AuthMiddlewareOptions>, but it was in fact AuthMiddleWareOptions. so this very simple assignment worked!
services.AddAsResourceServer(a => a = myoptions);
Quick answer to your question "Passing an Instance of an Object to a Method that takes an Action<> as Parameter" is "no, you can't"
But as a workaround, you could simply make a deepclone of your object, to swap the properties of the "inner" options.
Something like this :
static class Copy
{
public static void ObjectToAnother<T>(T source, T target)
{
foreach (var sourceProp in source.GetType().GetProperties())
{
var targetProp = target.GetType().GetProperty(sourceProp.Name);
targetProp.SetValue(target, sourceProp.GetValue(source, null), null);
}
}
}
And you could use that like this :
var obj = new Obj();
Foo.Bar((a) =>
{
Copy.ObjectToAnother(a, obj);
});
It should work
I know there are a lot of question similar to this one but actually none of them solved my issue.
I created a new Asp.Net Core 2 application.
Now I am trying to use an intercepter for a specific service to fetch some data into this service(I am using Castle.Core nuget package).
I have a generic IConfigurationInterceptor<> and a real implementation ConfigurationInterceptor<>
Here is the interface:
public interface IConfigurationInterceptor<T> : IInterceptor where T : class { }
public class ConfigurationInterceptor<T> : IConfigurationInterceptor<T> where T : class
{
public ConfigurationInterceptor(ConfigurationInfo<T> configurationInfo,
some other services)
{
_configurationInfo = configurationInfo;
//.....
}
public void Intercept(IInvocation invocation)
{
invocation.ReturnValue = somefunc(someconfig, invocation.Arguments);
}
}
Then I have an extension method like below:
public static void AddSingletonConfiguration<TInterface, TImplementation>(
this IServiceCollection services, string featureName)
where TImplementation : class, TInterface where TInterface : class
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
services.AddSingleton(x =>
{
var ic = x.GetService<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>();
var icTemp = ic.Invoke(info);
return (TInterface) generator.CreateInterfaceProxyWithoutTarget(
info.ServiceType, icTemp);
});
}
But when I get to this line of code:
var ic = x.GetService<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>();
it returns me a null value for ic:
ConfigurationInfo class is just a simple class I create for storing some extra data.
public sealed class ConfigurationInfo<TImpl>
{
public Type ServiceType { get; }
public string FeatureName { get; }
public ConfigurationInfo(string featureName, Type serviceType)
{
FeatureName = featureName;
ServiceType = serviceType;
}
public override string ToString()
=> $"{FeatureName} ({ServiceType} -> {typeof(TImpl)})";
}
In my ConfigureServices I have these both lines:
services.AddSingleton(typeof(IConfigurationInterceptor<>),
typeof(ConfigurationInterceptor<>));
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
"SomeFeatureKey");
I am not sure why ic variable is null because previously another project was using Autofac and was working perfectly but in the startup you would find something like this:
builder.RegisterGeneric(typeof(ConfigurationInterceptor<>))
.As(typeof(IConfigurationInterceptor<>)).SingleInstance();
builder.RegisterConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
"SomeFeatureKey");
and the extension method was like this one:
public static void RegisterConfiguration<TInterface, TImplementation>(
this ContainerBuilder builder, string featureName)
where TImplementation : class, TInterface
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
builder
.Register(c =>
{
var ic = c.Resolve<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>()(info);
return generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, ic);
})
.As<TInterface>()
.SingleInstance();
}
Any help would be appreaciated.
EDIT 1:
Now I changed from method GetService<> to method GetRequiredService<> and throws an exception like below:
No service for type 'System.Func'2[StaticDataProvider.DomainModel.ConfigurationInfo'1[StaticDataProvider.Services.StaticDataConfiguration],StaticDataProvider.Services.Interfaces.IConfigurationInterceptor'1[StaticDataProvider.Services.StaticDataConfiguration]]' has been registered.
EDIT 2:
To wrap it up here is the issue: In my current project in Asp.Net core I can not get a Func<X, B> while in the Asp.Net MVC 5 project(It is a whole different project) I can get a Func<X, B> using Autofac. I think this has to do with parametrized instantiation feature in Autofac provided by default: here
Now, I dont know if in Asp.Net Core default DI container has something like this 'parametrized instantiation' feature where it allows me resolving Func<X, B> instead of B.
I'm guessing the root of the problem is in the fairly complex manual wiring up of the interceptors.
If you're using interceptors with Autofac, it'd be better to use the Autofac.Extras.DynamicProxy2 package and wire up interceptors using the built-in Autofac functionality instead of trying to chain a bunch of resolutions together with functions and parameters. I see a lot of little gotchas in here like how you're setting up a singleton interface proxy without a target but I'm not entirely clear how the target gets added post-facto. There's a lot of complexity you can avoid by using the tools provided.
That said, I'm also looking at the exception message. Without a stack trace I can't 100% guarantee it, but a search on the Autofac source indicates that's not a message that came from Autofac - it's likely, then, a message from the default Microsoft.Extensions.DependencyInjection container. That indicates you may not actually have everything wired up the way you think you do.
I'd back up a bit and just get simple things working and ensure they're coming from Autofac. If you decide you don't want Autofac in play, make sure you've removed it entirely from the equation. Basically, just make sure it's clean and working in the general sense.
After that, add things back slowly, one at a time. I might recommend putting a reproduction in a unit test where you use these registration mechanisms and get things working without the complexity of the entire app weighing down. Unwind it from there. If it's too complex to unit test... maybe that's an indicator you should simplify it and refactor. Make it testable.
I'll leave my previous answer for posterity, but... The default Microsoft IoC provider is very simple and does not support all the features of Autofac. You won't get parameterized resolution or auto-generated factories from it.
Here is what I had to do:
Modified ConfigureService method like below:
public void ConfigureServices(IServiceCollection services)
{
IConfigurationInterceptor<T> GetConfigurationInterceptor<T>(ConfigurationInfo<T> info) where T : class
{
return new ConfigurationInterceptor<T>(info, services.GetService<IConfigurationProvider>(), Configuration);
}
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>("someFeatureKey", GetConfigurationInterceptor);
}
Then modified extension methods like below:
public static void AddSingletonConfiguration<TInterface, TImplementation>(this IServiceCollection services,
string featureName, Func<ConfigurationInfo<TImplementation>, IConfigurationInterceptor<TImplementation>> ic) where TImplementation : class, TInterface where TInterface : class
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
services.AddSingleton(x =>
{
var icTemp = ic.Invoke(info);
return (TInterface) generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, icTemp);
});
}
public static TInterface GetService<TInterface>(this IServiceCollection services) where TInterface : class
{
var serviceProvider = services.BuildServiceProvider();
return serviceProvider.GetRequiredService<TInterface>();
}
Now its working fine but the idea is that I had to create Func<X, B> myself and pass as a parameter to extension method.
In some cases one of my application services has to generate DTOs with anonymized data for the frontend. The idea was to use different AutoMapper profiles to either map the domain object to the DTO with all properties mapped or the anonymized DTO.
I generated these two profiles and injected them into the service. The AutoMapper is also injected into the service as IMapper and contains all mapping profiles of the application.
What I need now is to tell the mapper to use one specific profile in a call of the Map-function.
Something like this:
var anonymizedDto = _autoMapper.Map<SourceType, DestinationType>
(sourceObject, ops => ops.UseMappingProfile(_anonymizedMapingProfile));
var normalDto = _autoMapper.Map<SourceType, DestinationType>
(sourceObject, ops => ops.UseMappingProfile(_normalMappingProfile));
Is this possible and if yes: how?
As far as i know, you can not change the profile when you call Map.
What you can do is inject two mappers that have been configured with different profiles.
public class MyService : IService {
private readonly IMappingEngine _defaultMapper;
private readonly IMappingEngine _anonymousMapper;
public MyService(IMappingEngine defaultMapper, IMappingEngine anonymousMapper) {
_defaultMapper = defaultMapper;
_anonymousMapper = anonymousMapper;
}
public MyDto GetDefault() {
return _defaultMapper.Map<MyDto>(sourceObject);
}
public MyDto GetAnonymous() {
return _anonymousMapper.Map<MyDto>(sourceObject);
}
}
In your dependency container, set up the constructor injection to respect the name of the ctor parameter. For example with StructureMap:
public void ConfigureAutoMappers(ConfigurationExpression x) {
// register default mapper (static mapping configuration)
Mapper.Configuration.ConstructServicesUsing(t => container.GetInstance(t));
Mapper.Configuration.AddProfile<DefaultProfile>();
var defaultAutomapper = Mapper.Engine
x.For<IMappingEngine>().Use(() => defaultAutoMapper).Named("DefaultAutoMapper");
// register anonymous mapper
var anonConfig = new AnonConfigurationStore( // class derived from ConfigurationStore
new TypeMapFactory(),
AutoMapper.Mappers.MapperRegistry.AllMappers()
);
anonConfig.ConstructServicesUsing(container.GetInstance);
var anonAutoMapper = new MappingEngine(anonConfig);
x.For<IMappingEngine>().Add(anonAutoMapper).Named("AnonAutoMapper");
// Inject the two different mappers into our service
x.For<IService>().Use<MyService>()
.Ctor<IMappingEngine>("defaultMapper").Named("DefaultAutoMapper")
.Ctor<IMappingEngine>("anonymousMapper").Named("AnonAutoMapper");
}
Whats the best way to setup a mock expection for the Map function in AutoMapper.
I extract the IMapper interface so I can setup expects for that interface. My mapper has dependencies, so I have to pass those in to the mapper.
What happens when I create 2 instances of my mapper class, with 2 different dependency implementations? I asume that both mappers will use the same dependency instance, since the AutoMapper map is static. Or AutoMapper might even throw an exception because I try to setup 2 different maps with the same objects.?
Whats the best way to solve this?
public interface IMapper {
TTarget Map<TSource, TTarget>(TSource source);
void ValidateMappingConfiguration();
}
public class MyMapper : IMapper {
private readonly IMyService service;
public MyMapper(IMyService service) {
this.service = service
Mapper.CreateMap<MyModelClass, MyDTO>()
.ForMember(d => d.RelatedData, o => o.MapFrom(s =>
service.getData(s.id).RelatedData))
}
public void ValidateMappingConfiguration() {
Mapper.AssertConfigurationIsValid();
}
public TTarget Map<TSource, TTarget>(TSource source) {
return Mapper.Map<TSource, TTarget>(source);
}
}
You don't need to mock AutoMapper, you can just inject the real one as explained here:
var myProfile = new MyProfile();
var configuration = new MapperConfiguration(cfg => cfg.AddProfile(myProfile));
IMapper mapper = new Mapper(configuration);
You can inject this mapper in your unit tests. The whole point of using tools like AutoMapper is for you not having to write a lot of mapping code. If you mock AutoMapper you'll end up having to do that.
Whats the best way to setup a mock expection for the Map function in AutoMapper[?]
Here's one way:
var mapperMock = new Mock<IMapper>();
mapperMock.Setup(m => m.Map<Foo, Bar>(It.IsAny<Foo>())).Returns(new Bar());
http://richarddingwall.name/2009/05/07/mocking-out-automapper-with-dependency-injection/
Points out another way of handling dependencies to AutoMapper, which basically will replace my attempt to extract my own IMapper interface. Instead I will attempt to use the existing IMappingEngine as dependency for my classes, to see if that works.
What you need to do is setup AutoMapper like this (StructureMap is IoC). Then you can make your services dependent on IMappingEngine. From there mocking it should be very easy.
public class AutoMapperConfigurationRegistry : Registry
{
public AutoMapperConfigurationRegistry()
{
ForRequestedType<Configuration>()
.CacheBy(InstanceScope.Singleton)
.TheDefault.Is.OfConcreteType<Configuration>()
.CtorDependency<IEnumerable<IObjectMapper>>().Is(expr => expr.ConstructedBy(MapperRegistry.AllMappers));
ForRequestedType<ITypeMapFactory>().TheDefaultIsConcreteType<TypeMapFactory>();
ForRequestedType<IConfigurationProvider>()
.TheDefault.Is.ConstructedBy(ctx => ctx.GetInstance<Configuration>());
ForRequestedType<IConfiguration>()
.TheDefault.Is.ConstructedBy(ctx => ctx.GetInstance<Configuration>());
}
}
The reason you have to invoke automapper config is because, the UNIT Test cases instance runs outside of main application start up files/configs. Hence the auto mapper configuration has to be called and setup before any unit tests start to run. Ideally you place it in TestInitialize methods.
Here is an example of how I provide an instance of AutoMapper as default Mock for IMapper, using AutoFixture and Moq:
Thanks Lucian Bargaoanu for this hint: Actually you can use cfg.AddMaps(params Assembly[]) and Automapper will search for profiles
Create an ICustomization
public class MapperCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Register<IMapper>(() =>
{
var configuration = new MapperConfiguration(cfg =>
{
cfg.AddMaps(
Assembly.Load("BookSharing.Application"),
Assembly.Load("BookSharing.Infrastructure"));
});
return new Mapper(configuration);
});
}
}
Register the customization
fixture.Customize(new MapperCustomization());
following #Dorin Baba answer, I created a useful generic class that can be used to inject any custom mapper in the unit tests
public class MapperCustomization<T> : ICustomization
where T : class
{
public void Customize(IFixture fixture)
{
fixture.Register<IMapper>(() =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddMaps(
typeof(T).Assembly);
});
return new Mapper(config);
});
}
}