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
Related
I created a .Net Core 5 API having 2 types of models:
Entities (used by Entity Framework Core)
DTOs (Data Transfer Objects for requests and responses, replacing "{Property}Id" properties from Entity with "{Property}Code" in DTO)
I have a service responsible of mapping Entities types to Dtos types added as singleton in ConfigureServices:
services.AddSingleton(typeof(IEntityDtoMappingProvider), typeof(EntityDtoMappingProvider));
The service EntityDtoMappingProvider has a method which returns the mapping between Entities and Dtos for an assembly through reflection described by this interface:
public interface IEntityDtoMappingProvider
{
Dictionary<Type,Type> GetEntityDtoMapping(Assembly assembly);
}
I have an AutoMapper Profile requiring Entities and DTOs mapped, returned by the first service IEntityDtoMappingProvider:
public class EntitiesToDtosProfile : Profile
{
public EntitiesToDtosProfile(Dictionary<Type,Type> mapping)
{
if (mapping == null || mapping.Count == 0)
{
throw new ArgumentException( $"Empty mapping argument passed to {nameof(EntitiesToDtosProfile)} profile", nameof(mapping));
}
foreach(var item in mapping)
{
// Create AutoMapper mapping both ways based on those types
CreateMap(item.Key, item.Value); // Entity-DTO
CreateMap(item.Value, item.Key); // DTO-Entity
}
}
}
I need to create the AutoMapper profile in Startup.cs in ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddSingleton(typeof(IEntityDtoMappingProvider), typeof(EntityDtoMappingProvider));
// Note: Using services.BuildServiceProvider() is a bad practice because an additional copy of singleton services being created
using (var serviceProvider = services.BuildServiceProvider())
{
var mappingService = serviceProvider.GetRequiredService<IEntityDtoMappingProvider>();
var mappings = mappingService.GetEntityDtoMapping(typeof(Workflow).Assembly);
// Add AutoMapper IMapper to services
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new EntitiesToDtosProfile(mappings));
});
var mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
// Here I should call other IServiceCollection extensions like:
// Database-related services: GenericRepository<TEntity, TDbContext> : IGenericRepository<TEntity>
services.AddDatabaseGenericRepositories<ApplicationDbContext>(mappings, Log.Logger);
// Mapping-related services: MappingHelper<TEntity, TDto> : IMappingHelper<TEntity, TDto>
services.AddMappingHelpers(mappings, Log.Logger);
// ...
}
// ...
}
As I was saying in the code, using services.BuildServiceProvider() is a bad practice because an additional copy of singleton services being created, creates a second container, which can create torn singletons and cause references to object graphs across multiple containers. Microsoft .Net Core 5 documentation backing those statements.
Please give an answer about how I should create the Entity-DTO mapping of type Dictionary<Type,Type> in CreateServices using IEntityDtoMappingProvider in order to build the AutoMapper profile EntitiesToDtosProfile and create other services through reflection without calling services.BuildServiceProvider taking into considerations the following:
I have many services created through reflection using extension methods for IServiceCollection requiring Entity-DTO mapping in ConfigureServices
I cannot use IOptions having a property of type Dictionary<Type,Type> because IOptions shouldn't be used in ConfigureServices: "An inconsistent options state may exist due to the ordering of service registrations." Source: IOptions Microsoft Documentation.
I looked through a lot of questions (some might be a little unrelated), but all solved their issues using services.BuildServiceProvider() or IOptions which is not ok.
If you are only registering IEntityDtoMappingProvider so that you use it to build your mapping component then maybe you shouldn't register it. This sort of one time configuration is often best done outside the scope of the container itself. As you suggested you can probably just remove the interface entirely and use the concrete class directly.
Same goes for things like logger configuration.
You can register a service factory which accepts the service provider instance and uses that to resolve other services. For example:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddSingleton(typeof(IEntityDtoMappingProvider), typeof(EntityDtoMappingProvider));
services.AddSingleton(sp =>
{
var mappingService = sp.GetRequiredService<IEntityDtoMappingProvider>();
var mappings = mappingService.GetEntityDtoMapping(typeof(Workflow).Assembly);
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new EntitiesToDtosProfile(mappings));
});
return mappingConfig.CreateMapper();
});
// ...
}
You would need to modify your AddDatabaseGenericRepositories and AddMappingHelpers methods to do something similar.
I am using MassTransit to publish\subscribe messages using RabbitMQ. I want to inject dependencies in dependency in consumers so that consumers can insert data to the database. However, I found the examples in the documentation confusing.
public class MessageConsumer : IConsumer<Message>
{
private IDao dao;
public MessageConsumer(IDao dao)
{
this.dao = dao;
}
public async Task Consume(ConsumeContext<Message> context)
{
Console.WriteLine("Order Submitted: {0}", context.Message.MessageId);
}
}
The bus is configured as follows
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConcreteDao>().As<IDao>();
builder.RegisterType<MessageConsumer>().As<IConsumer<Message>>();
builder.AddMassTransit(x => {
// add the bus to the container
x.UsingRabbitMq((context, cfg) => {
cfg.Host("localhost");
cfg.ReceiveEndpoint("MessageQueueName", ec => {
// Configure a single consumer
ec.ConfigureConsumer<MessageConsumer>(context);
});
// or, configure the endpoints by convention
cfg.ConfigureEndpoints(context);
});
});
var container = builder.Build();
var bc = container.Resolve<IBusControl>();
bc.Start();
}
However I am getting an exception when the IBusControl is resolved.
System.ArgumentException: 'The consumer type was not found: StationDashboard.Messaging.Consumer.OperationModeChangedConsumer (Parameter 'T')'
What is wrong with the code above? What is the best way to inject dependency to a consumer? It would help if there was a complete working sample.
You need to register the consumer as explained in the documentation.
Do NOT do this:
builder.RegisterType<MessageConsumer>().As<IConsumer<Message>>();
Instead, as shown in the documentation, do this:
x.AddConsumer<MessageConsumer>();
Your dependency can be registered as you've done it.
I'd like to know if there is a way to globally configure Mapster while using Dependency Injection?
The configuration options appear to be for the static usage and also for a singleton pattern only.
Mapster Configuration
Mapster Dependency Injection
I have created an extension method.
// Extension method
public static IServiceCollection AddMapster(this IServiceCollection services, Action<TypeAdapterConfig> options = null)
{
var config = new TypeAdapterConfig();
config.Scan(Assembly.GetAssembly(typeof(Startup)));
options?.Invoke(config);
services.AddSingleton(config);
services.AddScoped<IMapper, ServiceMapper>();
return services;
}
// Called in Startup.ConfigureServices(IServiceCollection services)
services.AddMapster(options =>
{
options.Default.IgnoreNonMapped(true); // Does not work.
TypeAdapterConfig.GlobalSettings.Default.IgnoreNonMapped(true); // Does not work.
});
I imagine these don't work because the ServiceMapper is creating its own instance without using anything I've configured.
I implemented Mapster in a Blazor Server application, and I struggled to find documentation on how to scan the assembly for mapping registrations.
I have a class in my application that implements the IRegister interface and defines the mappings
public class MappingRegistration : IRegister
{
void IRegister.Register(TypeAdapterConfig config)
{
config.NewConfig<ModelA, ModelB>();
}
}
In the ConfigureServices of the Startup.cs I have this then
var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
// scans the assembly and gets the IRegister, adding the registration to the TypeAdapterConfig
typeAdapterConfig.Scan(Assembly.GetExecutingAssembly());
// register the mapper as Singleton service for my application
var mapperConfig = new Mapper(typeAdapterConfig);
services.AddSingleton<IMapper>(mapperConfig);
I hope this can save someone's time. If anybody is aware of better ways, please let me know.
You can change from
var config = new TypeAdapterConfig();
to
var config = TypeAdapterConfig.GlobalSettings;
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.
I have a simple mvc application that has 3 layer
Ui => has refrence to Common And Services
Common
Services => has refrence to Common
I define my Service Contracts in Common layer and implement it in services layer
//Common layer
public interface IPersonService
{
void Foo();
}
//Services layer
public classPersonService:IPersonService
{
void Foo()
{
//To DO
}
}
In my Global.asax I write this code for initial Structuremap container
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
}
Now,in my controller when I want get instance from IPersonService like this
var personService= ObjectFactory.GetInstance<IPersonService>();
I get this error
No default Instance is registered and cannot be automatically determined for type '*.IPersonService'
Is there any idea for resolve this problem?
You can specify other assemblies to scan using the AssembliesFromApplicationBaseDirectory() function, like so:
scan.AssembliesFromApplicationBaseDirectory(a => a.FullName.StartsWith("YourNamespace.Common"));
scan.AssembliesFromApplicationBaseDirectory(a => a.FullName.StartsWith("YourNamespace.Services"));
Seeing as you tagged this question as a StructureMap 3 question, I would advise against using the ObjectFactory as Jeremy Miller (the author of StructureMap) has made it clear that it will be removed in future version. In fact, you should get a warning that it will be removed in a future version.
Instead you should aim to configure your container like so:
IContainer container = new Container();
container.Configure(c => {
c.IncludeRegistry<YourRegistry>();
});
DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
You can read more about StructureMap's registries here.