Can someone guide me on how I configure Automapper in Structuremap? Currently, I have this service that will be using the IMapper.
public class RequestService : IRequestService
{
private readonly IMapper _mapper;
private readonly IRepositoryWrapper _repositoryWrapper;
public RequestService(IMapper mapper, IRepositoryWrapper repositoryWrapper)
{
_mapper = mapper;
_repositoryWrapper = repositoryWrapper;
}
public void GetSomething()
{
var result = _repositoryWrapper.RequestRepository.GetAll();
_mapper.Map<RequestDto>(result);
}
}
On the other hand, this is my Registry. This is also the place where I configure my automapper.
public class InfrastructureRegistry : Registry
{
public InfrastructureRegistry()
{
var mapperConfiguration = new MapperConfiguration(cfg => {
cfg.AddProfile(typeof(MapperProfile));
});
var mapper = mapperConfiguration.CreateMapper();
For<IMapper>().Use(mapper);
Scan(
scan => {
scan.TheCallingAssembly();
scan.AssemblyContainingType<IRequestService>();
scan.AssemblyContainingType<IRepositoryWrapper>();
scan.WithDefaultConventions();
});
For<IRequestService>().Use<RequestService>();
For<IRepositoryWrapper>().Use<RepositoryWrapper>();
}
}
During testing, I get this error message.
StructureMap.StructureMapConfigurationException: 'No default Instance is registered and cannot be automatically determined for type 'AutoMapper.IMapper'
I'm using the below version of:
Automapper = 10.1.1
StructureMap.WebApi2 = 3.0.4.125
StructureMap = 3.0.4.125
Hope someone can guide me on this.
TIA!
Related
I run Hangfire on ASP.NET Core.
For our other projects we have CorrelationIds that we pass when making API calls to be able to link the caller and callee.
We use the IHttpContextAccessor's TraceIdentifier for this in ASP.NET Core.
Unfortunately it looks like the trick used by ASP.NET Core to get a scoped CorrelationId in the Transient IHttpContextAccessor doesn't work for Hangfire job execution.
Using a Scoped state correlation object doesn't work because it must be Transient to be able to work with the rest of the system (logging etc.)
I used to be able to get away using the ServiceLocator anti-pattern and resolve a scoped state object in a transient service.
In the latest ASP.NET Core that is no longer supported and an exception is thrown making the system too slow because of the huge number of exceptions thrown.
Is there something that Hangfire provides already that would give me a unique ID per job execution?
Cheers.
Thanks to jbl's comment I looked at what I was doing again and managed to get it working through a kludge.
I've got the transient state holder
(basically it's the HttpContextAccessor class renamed):
public class StateHolder
{
private static AsyncLocal<ContextHolder> _contextCurrent = new AsyncLocal<ContextHolder>();
public string State {
get {
return _contextCurrent.Value?.Context;
}
set {
var holder = _contextCurrent.Value;
if (holder != null)
{
holder.Context = null;
}
if (value != null)
{
_contextCurrent.Value = new ContextHolder { Context = value };
}
}
}
private class ContextHolder
{
public string Context;
}
}
and then in Hangfire I hook it up to the activation with
public class LoggingActivator : JobActivator
{
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ContextAccessor _contextAccessor;
public LoggingActivator([NotNull] IServiceScopeFactory serviceScopeFactory, ContextAccessor contextAccessor)
{
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
_contextAccessor = contextAccessor;
}
public override JobActivatorScope BeginScope(JobActivatorContext context)
{
return new LoggingActivatorScope(_serviceScopeFactory.CreateScope(), _contextAccessor);
}
}
and
public class LoggingActivatorScope : JobActivatorScope
{
private readonly IServiceScope _serviceScope;
private readonly ContextAccessor _contextAccessor;
public LoggingActivatorScope(
[NotNull] IServiceScope serviceScope,
ContextAccessor contextAccessor)
{
_serviceScope = serviceScope ?? throw new ArgumentNullException(nameof(serviceScope));
_contextAccessor = contextAccessor;
}
public override object Resolve(Type type)
{
_contextAccessor.Context = Guid.NewGuid().ToString();
return ActivatorUtilities.GetServiceOrCreateInstance(_serviceScope.ServiceProvider, type);
}
public override void DisposeScope()
{
_serviceScope.Dispose();
}
}
That seems to work fine.
I work with the Akka.net. I want to use the DryIoc as an IoC-container, but there is no ready-made using adapters for the actor system in the Akka. The documentation says that to use a custom IoC-container, I need to implement IDependencyResolver. Looked for the libraries for AutoFac and CastleWindsor as examples, I made a DependencyResolver for DryIoc:
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Runtime.CompilerServices;
using Akka.Actor;
using Akka.DI.Core;
using DryIoc;
namespace Akka.DI.DryIoc
{
public sealed class DryIocDependencyResolver : IDependencyResolver, INoSerializationVerificationNeeded
{
private readonly IContainer container;
private readonly ConcurrentDictionary<string, Type> typeCache;
private readonly ActorSystem system;
private readonly ConditionalWeakTable<ActorBase, IResolverContext> references;
public DryIocDependencyResolver(IContainer container, ActorSystem system)
{
this.container = container
?? throw new ArgumentNullException("container");
this.system = system
?? throw new ArgumentNullException("system");
this.typeCache = new ConcurrentDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
this.references = new ConditionalWeakTable<ActorBase, IResolverContext>();
this.system.AddDependencyResolver(this);
}
public Func<ActorBase> CreateActorFactory(Type actorType)
=> () =>
{
var context = this.container.OpenScope();
var key = (ActorBase)context.Resolve(actorType);
this.references.Add(key, context);
return key;
};
public Type GetType(string actorName)
{
var type = actorName.GetTypeValue();
if (type is null)
type = this.container.GetServiceRegistrations()
.Where(registration =>
registration.ImplementationType.Name.Equals(actorName, StringComparison.OrdinalIgnoreCase))
.Select(registration =>
registration.ImplementationType)
.FirstOrDefault();
this.typeCache.TryAdd(actorName, type);
return this.typeCache[actorName];
}
public Props Create<TActor>()
where TActor : ActorBase
=> this.Create(typeof(TActor));
public Props Create(Type actorType)
=> this.system.GetExtension<DIExt>().Props(actorType);
public void Release(ActorBase actor)
{
if (!this.references.TryGetValue(actor, out IResolverContext lifetimeScope))
return;
lifetimeScope.Dispose();
this.references.Remove(actor);
}
}
}
However, I'm not sure that everything is done correctly. I especially doubt the necessity of using the type cache and implementing the GetType method.
I've been following the NServiceBus samples, specifically for how to use an entity framework (core) DbContext integrated with Sql Persistence so that I can save dbcontext state changes along with the outbox messages. This is the sample: https://docs.particular.net/samples/entity-framework-core/
I've modified the unit of work code a little to support creation of an aspnet core DI scoped DbContext. The relevant code follows:
public class UnitOfWork<TDbContext>
where TDbContext : DbContext
{
private Func<SynchronizedStorageSession, IServiceProvider, TDbContext> _contextFactory;
private TDbContext _context;
private IServiceProvider _serviceProvider;
public UnitOfWork(Func<SynchronizedStorageSession, IServiceProvider, TDbContext> contextFactory, IServiceProvider serviceProvider)
{
_contextFactory = contextFactory;
_serviceProvider = serviceProvider;
}
public TDbContext GetDataContext(SynchronizedStorageSession storageSession)
{
if (_context == null)
{
_context = _contextFactory(storageSession, _serviceProvider);
}
return _context;
}
}
public class UnitOfWorkSetupBehavior<TDbContext> : Behavior<IIncomingLogicalMessageContext>
where TDbContext : DbContext
{
private readonly Func<SynchronizedStorageSession, IServiceProvider, TDbContext> _contextFactory;
private readonly IServiceScopeFactory _serviceScopeFactory;
public UnitOfWorkSetupBehavior(Func<SynchronizedStorageSession, IServiceProvider, TDbContext> contextFactory, IServiceScopeFactory serviceScopeFactory)
{
_contextFactory = contextFactory;
_serviceScopeFactory = serviceScopeFactory;
}
public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var uow = new UnitOfWork<TDbContext>(_contextFactory, scope.ServiceProvider);
context.Extensions.Set(uow);
await next().ConfigureAwait(false);
context.Extensions.Remove<UnitOfWork<TDbContext>>();
}
}
}
public static class EndpointConfigurationExtensions
{
public static void RegisterUnitOfWork<TDbContext>(this EndpointConfiguration endpointConfiguration, IServiceScopeFactory serviceScopeFactory)
where TDbContext : DbContext
{
var pipeline = endpointConfiguration.Pipeline;
pipeline.Register(new UnitOfWorkSetupBehavior<TDbContext>((storageSession, serviceProvider) =>
{
var dbConnection = storageSession.SqlPersistenceSession().Connection;
var dbContextFactory = serviceProvider.GetService<IDbContextConnectionFactory<TDbContext>>();
var dbContext = dbContextFactory.GetDbContext(dbConnection);
//Use the same underlying ADO.NET transaction
dbContext.Database.UseTransaction(storageSession.SqlPersistenceSession().Transaction);
//Call SaveChanges before completing storage session
storageSession.SqlPersistenceSession().OnSaveChanges(x => dbContext.SaveChangesAsync());
return dbContext;
}, serviceScopeFactory), "Sets up unit of work for the message");
}
}
public static class UnitOfWorkContextExtensions
{
public static TDbContext DataContext<TDbContext>(this IMessageHandlerContext context)
where TDbContext : DbContext
{
var uow = context.Extensions.Get<UnitOfWork<TDbContext>>();
return uow.GetDataContext(context.SynchronizedStorageSession);
}
}
For this to work the behavior needs an injected IServiceScopeFactory.
Now all examples I've been able to find of behavior registration only show the type manually instantiated and passed in to the endpointconfiguration's pipeline.
Is there a way to either gain access to an IServiceScopeFactory via the behavior's Invoke method (maybe by the context via some extension perhaps?), or is it possible to register the behavior itself such that I can construct it with services created by the DI container?
FYI I took a look at this Q&A which gave me the idea of injecting the IServiceScopeFactory. Unfortunately, the answer doesn't show how to actually get an instance of the interface.
You would use context.builder.Build<T>(); within the Invoke method to resolve any objects like IServiceScopeFactory.
https://docs.particular.net/samples/multi-tenant/di/
Make sure that the IServiceScopeFactory is registered in the DI container. For example, during your endpoint initialization:
endpointConfiguration.RegisterComponents(registration: x =>
{
x.ConfigureComponent<IServiceScopeFactory>(yourServiceScopeFactory);
});
https://docs.particular.net/nservicebus/dependency-injection/
You can also do this by creating a Feature
https://docs.particular.net/nservicebus/pipeline/features
I am writing some unit tests for my AspNetCore 2.2 MVC application. I want to use CustomWebApplicationFactory by XUnit to substitute my dbcontext for an inmemory one, with this standard code:
public class TestWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddDbContext<IDemoDbContext, DemoDbContext>(options =>
{
options.UseInMemoryDatabase(Guid.NewGuid().ToString());
options.UseInternalServiceProvider(serviceProvider);
});
var provider = services.BuildServiceProvider();
using (var scope = provider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var demoContext = (DemoDbContext)scopedServices.GetRequiredService<IDemoDbContext>();
var logger = scopedServices.GetRequiredService<ILogger<TestWebApplicationFactory<TStartup>>>();
demoContext.Database.EnsureCreated();
try
{
var seeder = new DemoDbSeeder(demoContext);
seeder.SeedAllTables();
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred seeding the test database. Error: {ex.Message}");
}
}
});
}
}
My tests keep failing at the FluentValidation validator that needs dbcontext to validate new values against the existing ones.
public class UpdateEntityCommandValidator : AbstractValidator<UpdateEntityCommand>
{
private readonly IDemoDbContext _dbContext;
public UpdateEntityCommandValidator(IDemoDbContext dbContext)
{
_dbContext = dbContext;
RuleFor(j => j.Status).Must((j, status) => BeValidStatusTransition(j.RxJobId, status)).WithMessage("Invalid status transition.");
}
private bool BeValidStatusTransition(Guid entityId, string newStatus)
{
// Do validation against existing status in the database
}
}
While the code works flawlessly with SQL server in StartUp and dbContext gets injected. When I run the test, DbSets in the context are empty. I debugged through and can tell that seeding works and I have entities after SeedAllTables. I can only assume the problem is with dependency injection. Could someone point me in the right direction on how this issue can be solved?
I'm trying to add controllers dynamically, the catch is that their actions are also dynamic, so I can't simply use a generic controller.
I dug through msdn and found that overriding the method Apply#IApplicationModelConvention seems to be my best option. However, the controller I add is never found by the routing system.
This is what I tried:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(opts =>
{
opts.Conventions.Add(new ApplicationDescription("mdesc"));
opts.Conventions.Add(new ControllerDescription("mctrl"));
})
...
}
public class ApplicationDescription : IApplicationModelConvention
{
private readonly string _description;
public ApplicationDescription(string description)
{
_description = description;
}
public void Apply(ApplicationModel application)
{
var basec = application.Controllers[0];
var cm = new ControllerModel(typeof(MyController).GetTypeInfo(), basec.Attributes);
application.Controllers.Add(cm);
application.Properties["description"] = _description;
}
}
Source: https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/application-model?view=aspnetcore-2.0