I have a class that fetches some data from a database and I then map the data using AutoMapper. However, for some reason, mapper never gets a value injected so I get a NullReferenceException when I try to use mapper.
public class SearchesEditReview
{
[Inject]
public IMapper mapper { get; set; }
public async Task<ViewEvent> GetEditFromId(int id)
{
//unrelated code
return new ViewEvent
{
Data = timelineinfo.FirstOrDefault(),
Medias = media,
//The below line breaks, saying mapper is null
Subjects = mapper.Map<List<DBSubject>, List<ViewSubject>>(subjects)
};
}
}
My relevent Startup.cs looks like:
public void ConfigureServices(IServiceCollection services)
{
// Auto Mapper Configurations
var mapperConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
services.AddHttpContextAccessor();
IMapper mapper = mapperConfig.CreateMapper();
services.AddSingleton(mapper);
}
Lets focus on the construction of SearchesEditReview, which seems to be unable to correctly bind the automapper property, while it's should be registered correctly.
You are using a binding with an [Inject] attribute, but that's not always clear how it works (well at least to me; there are ton's of frameworks, all doing it a little differently). For this reason I tend to use the constructor injection pattern:
public class SearchesEditReview
{
public SearchesEditReview(IMapper mapper) //dependencies here
{
//attach to properties
}
}
Next to the downside of don't having a parameter-less constructor, it has 2 advantages:
you are forced to pass the parameter, so there will be no ambiguity, it is easier to debug
You're independent of the DI framework. Which you'll seldom use.
As mentioned, for .net Core you can use a NuGet package for the .net Core Dependency Injection Framework:
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjections:
And register like this:
public void ConfigureServices(IServiceCollection services)
{
//...
//You'll need to pass in the assemblies containing your profiles,
//or the profiles itself
services.AddAutoMapper(typeof(YourProfile1),typeof(YourProfile2)); //etc
}
Note: The sometimes used loaded assembly scan GetAssemblies can be error prone. Since this occurs at startup, profile containing assemblies might not be loaded yet.
See this blog post or this documentation for more details.
Also keep in mind that you need to make sure the framework is able to construct SearchesEditReview.
You cannot Inject into a class like that. The syntax your using would work fine on a .razor page however.
Please see docs
Change your class. Note the constructor.
public class SearchesEditReview
{
public SearchesEditReview(IMapper mapper)
{
this.mapper = mapper;
}
IMapper mapper { get; set; }
public async Task<ViewEvent> GetEditFromId(int id)
{
//unrelated code
return new ViewEvent
{
Data = timelineinfo.FirstOrDefault(),
Medias = media,
//The below line breaks, saying mapper is null
Subjects = mapper.Map<List<DBSubject>, List<ViewSubject>>(subjects)
};
}
}
Startup.cs
...
services.AddSingleton(mapper);
services.AddSingleton<SearchesEditReview>();
Related
In Entity Framework (EF) Core, SCOPED OBJECTS are the same within a request (but different across different requests). Calling AddDbContext is supposed to be SCOPED by-default...so I am expecting each DbContext instance to be the same instance when marked as SCOPED...and it's not.
I know this because every DbContext handed-up using Dependency Injection (DI) has a different ContextId...and "save changes" no longer works across all Repository's in my UnitOfWork. As such, it seems like DbContext creation is acting TRANSIENT not SCOPED.
Q: How do I guarantee each instance of the concrete DbContext is the same object in EF Core's DI-model?
Why do I want this?
Calling the UnitOfWork's "save changes" used to work across all Repository's...but not anymore because each DbContxet is different (and has a separate change tracker)
Lamar Service Registry Code:
public class ContainerRegistry : ServiceRegistry
{
public ContainerRegistry()
{
Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.LookForRegistries();
scan.SingleImplementationsOfInterface();
});
// --------
// DATABASE
//ForSingletonOf<WorkflowComponentDbContext>(); //<-- Doesnt work b/c each DbContext is still a separate instance
For<DbContext>().Use<WorkflowComponentDbContext>();
For(typeof(IAuditableRepository<>)).Use(typeof(GenericAuditableRepository<>));
// Policies (are used to map Constructor args)
Policies.Add<GenericRepositoryConfiguredInstancePolicy>();
Policies.Add<UnitOfWorkConfiguredInstancePolicy>();
}
}
Host Builder Code:
private IHostBuilder CreateHostBuilder(string[] args)
{
var builder = new HostBuilder()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
})
.UseServiceProviderFactory<ServiceRegistry>(new LamarServiceProviderFactory())
.ConfigureServices((hostContext, services) =>
{
var connectionString = hostContext.Configuration.GetConnectionString(JsonSettings.ConnectionStrings.WorkflowComponentDb);
services.AddLamar(IoC.Build());
services.AddScoped<IWindowsIdentityHelper, WindowsIdentityHelper>();
// This is supposedly SCOPED by-default?
// And while, this passes-back OPTIONS correctly...it isn't passing a "singleton object" throughout the request
services.AddDbContext<ProjectManagementDbContext>((provider, options) =>
{
options.UseSqlServer(connectionString);
});
services.AddDbContext<WorkflowComponentDbContext>((provider, options) =>
{
options.UseSqlServer(connectionString);
});
// This doesnt work either b/c it hands-back a new instacne of the Factoty each time (I tested this)....
//services.AddDbContextFactory<WorkflowComponentDbContext, WorkflowComponentDbContextFactory>((provider, options) =>
//{
// options.UseSqlServer(connectionString);
//}, ServiceLifetime.Scoped);
});
return builder;
}
LOW-TECH OPTION: Pass-in the IContainer
I really don't want to do this...but can
// -----
// NOTE: Some code omitted for brevity
public class WorkflowComponentUnitOfWork : IUnitOfWork
{
// OPTION: I could pass the IContainer to build some dependecies?
public WorkflowComponentUnitOfWork(DbContext dbContext, IContainer container)
{
DbContext = dbContext;
ContextType = new GenericAuditableRepository<ContextType>(DbContext);
ContextType.AuditResolver = container.GetRequiredService<IAuditResolverOf<ContextType>>();
ObjectState = new GenericAuditableRepository<ObjectState>(DbContext);
ObjectState.AuditResolver = container.GetRequiredService<IAuditResolverOf<ObjectState>>();
ObjectStateEvent = new GenericAuditableRepository<ObjectStateEvent>(DbContext);
ObjectStateEvent.AuditResolver = container.GetRequiredService<IAuditResolverOf<ObjectStateEvent>>();
Workflow = new GenericAuditableRepository<Workflow>(DbContext);
Workflow.AuditResolver = container.GetRequiredService<IAuditResolverOf<Workflow>>();
WorkflowEvent = new GenericAuditableRepository<WorkflowEvent>(DbContext);
WorkflowEvent.AuditResolver = container.GetRequiredService<IAuditResolverOf<WorkflowEvent>>();
WorkflowTransition = new GenericAuditableRepository<WorkflowTransition>(DbContext);
WorkflowTransition.AuditResolver = container.GetRequiredService<IAuditResolverOf<WorkflowTransition>>();
}
public virtual void SubmitChanges()
{
DbContext.SaveChanges();
}
}
LOW-TECH OPTION: Call "save changes" across all repository's
I really don't want to do this...but can
// -----
// NOTE: Some code omitted for brevity
public class WorkflowComponentUnitOfWork : IUnitOfWork
{
[SetterProperty]
public IAuditableRepository<ContextType> ContextType { get; set; }
[SetterProperty]
public IAuditableRepository<ObjectState> ObjectState { get; set; }
[SetterProperty]
public IAuditableRepository<ObjectStateEvent> ObjectStateEvent { get; set; }
[SetterProperty]
public IAuditableRepository<Workflow> Workflow { get; set; }
[SetterProperty]
public IAuditableRepository<WorkflowEvent> WorkflowEvent { get; set; }
[SetterProperty]
public IAuditableRepository<WorkflowTransition> WorkflowTransition { get; set; }
// OPTION: I could call "Save Changes" across each Repository
public virtual void SubmitChanges()
{
ContextType.SaveChanges();
ObjectState.SaveChanges();
ObjectStateEvent.SaveChanges();
Workflow.SaveChanges();
WorkflowEvent.SaveChanges();
WorkflowTransition.SaveChanges();
}
}
UPDATES:
Using the following does not work...
For<DbContext>().Use<WorkflowComponentDbContext>().Scoped();
As an answer to your question: The documentation here (https://jasperfx.github.io/lamar/guide/ioc/lifetime.html) adds an extra .Scoped() to the For<I>().Use<T>().. Looks like you should add this to your Lamar-definition of ProjectManagementDbContext.
Additionally: Why do you use services.AddDbContext<ProjectManagementDbContext>(..) and For<DbContext>().Use<ProjectManagementDbContext>(). There is an overload for AddDbContext<T1, T2>(..).
As an advice (from my own error): The way you have to setup your DI would mean, that all DbContexts share state. Consider refactoring your code to be able use transient DbContexts (from the names given maybe your repositories are just wrapping DbSets ... but that's just a guess).
This is the correct code conceptually:
For<DbContext>().Use<WorkflowComponentDbContext>().Scoped()
When StructureMap / Lamar was first created, an HTTP request was processed on a single thread. In those days Scoped objects used thread local storage.
C# usage has switched to an async await model since then. So it is possible that you are getting a problem after an await call, when code resumes on a new thread. This might cause a new DbContext to be created. I could be wrong - but worth checking if this is the cause.
If so, you may need to create a custom scope that stores objects against the HTTP request. I ran into this a while back with Spring Boot, and had to write this custom scope.
I am using AutoMapper to convert my models into view models. I have the configuration all setup, tested, and working. For reference, this is what my configure method looks like:
public static MapperConfiguration Configure()
{
MapperConfiguration mapperConfiguration = new MapperConfiguration(cfg => {
cfg.CreateMap<Ebill, EbillHarvesterTaskVM>()
cfg.CreateMap<Note, NoteVM>();
cfg.CreateMap<LoginItem, LoginCredentialVM>()
cfg.CreateMap<Login, ProviderLoginVM>()
});
mapperConfiguration.CreateMapper();
return mapperConfiguration;
}
This is what my test looks like:
public void ValidConfigurationTest()
{
var config = AutoMapperConfig.Configure();
config.AssertConfigurationIsValid();
}
What I don't understand is how to access it to actually map one object to another from within my Controller. I know I can call this config method when my app starts up, I have an application configuration class that is called from global.asax that calls my automapper configuration method. I'm not sure how to access all of this from within the controller though. I've read things that say dependency injection, but I'm not familiar enough with what that means to know how to apply it.
I've used Automapper in the past, but I think I implemented the now unavailable static API. Where the config method looks like this:
public static void RegisterMappings()
{
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<ManagementCompany, ManagementCompanyViewModel>();
cfg.CreateMap<ManagementCompanyViewModel, ManagementCompany>();
});
}
The configuration is called in Global.asax
AutoMapperConfig.RegisterMappings();
And where you can call this within a controller to utilize mapping:
AutoMapper.Mapper.Map(managementCompany, managementCompanyVM);
This way doesn't work anymore. When I type AutoMapperMapper there is no Map method to call. What do I need to do to be able to access my mappings and use them?
public static MapperConfiguration Configure() {
MapperConfiguration mapperConfiguration = new MapperConfiguration(cfg => {
cfg.CreateMap<Ebill, EbillHarvesterTaskVM>()
cfg.CreateMap<Note, NoteVM>();
cfg.CreateMap<LoginItem, LoginCredentialVM>()
cfg.CreateMap<Login, ProviderLoginVM>()
});
return mapperConfiguration;
}
build the mapper and register it with the dependency container used by your application.
global.asax
MapperConfiguration config = AutoMapperConfig.Configure();;
//build the mapper
IMapper mapper = config.CreateMapper();
//..register mapper with the dependency container used by your application.
myContainer.Register<IMapper>(mapper); //...this is just an oversimplified example
Update your controllers to explicitly depend on the mapper via constructor injection
private readonly IMapper mapper;
public MyController(IMapper mapper, ...) {
this.mapper = mapper;
//...
}
And call the mapper as needed in the controller actions.
//...
Note model = mapper.Map<Note>(noteVM);
//...
I want to use IValueResolver in AutoMapper to map two class, and one value will be take from HttpRequest Context, so I want to use IValueResolver
CreateMap<Dto, ViewModel>().ForMember(x=>x.MemberID, opt=>opt.Mapfrom<SpecialResolver>())
and Resolver is simple
public string Resolve(ViewModel viewModel, Dto dto, string destMember, ResolutionContext context)
{
return "test";
}
inside startup class i put this:
services.AddAutoMapper(typeof(Startup));
but every time I map them for MemberID will throw out error say IServiceProvider been disposed.
so how to make these work? I tried inject this SpecialResolver in startup but also not work. BTW, I'm use .net core 3.0
I'm strongly convinced that a bug has crept in your code somewhere and hence your problems. On my side, everything works just fine. I tried to recrete what you are trying to do, based on your question and comments. It will surely differ more or less, but you should be able to grasp the idea and get it going on your own.
I'm starting with a mapping profile, where I'm explicitly specifying usage of HttpContextValueResolver for MemberId property of ViewModel class:
public class MyMappingProfile : Profile
{
public MyMappingProfile()
{
CreateMap<Dto, ViewModel>()
.ForMember(x => x.MemberId, opt => opt.MapFrom<HttpContextValueResolver>());
}
}
Then the value resolver:
public class HttpContextValueResolver : IValueResolver<Dto, ViewModel, string>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public HttpContextValueResolver(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string Resolve(Dto source, ViewModel destination, string destMember, ResolutionContext context)
{
// Obtain whatever you need from HTTP context.
// Warning! HTTP context may be null.
return _httpContextAccessor.HttpContext?.Request.Path;
}
}
To acess HttpContext outside a controller I used a dedicated for that purpose service called IHttpContextAccessor. Read more about it in the docs.
It isn't automatically available, so I need to register it in Startup alongside with the AutoMapper:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAutoMapper(typeof(Startup));
services.AddHttpContextAccessor();
}
Take notice that registering AutoMapper while passing just one type (of Startup), requires that all mapping profiles need to be in the same assembly (project) as the Startup. With mapping profiles in more than one assembly, you need to specify those assemblies or types with a suitable overload of the AddAutoMapper() method.
And finally usage in an example controller:
public class HomeController : Controller
{
private readonly IMapper mapper;
public HomeController(IMapper mapper)
{
this.mapper = mapper;
}
public IActionResult Index()
{
var source = new Dto
{
MemberID = "123",
};
var result = mapper.Map<ViewModel>(source);
return View();
}
}
And here are the dto and view model I used:
public class Dto
{
public string MemberID { get; set; }
}
public class ViewModel
{
public string MemberId { get; set; }
}
I´m trying to use Automapper with Dependency Injection configuration on a n-layer application.
public class ApplicationMapping : Profile
{
public ApplicationMapping()
{
RegisterMappings();
Mapper.AssertConfigurationIsValid();
}
private void RegisterMappings()
{
CreateMap<IEnumerable<App>, ListAppsDto>()
.ForMember(dest => dest.Apps,
opt => opt.MapFrom(src =>
Mapper.Map<IEnumerable<App>, List<App>>(src.ToList())
)
);
}
}
This class is inside my Application dll, where I put my services and DTOs. Also in this dll, I have an extension method to register the mapping:
public static class MappingServiceExtension
{
public static void AddApplicationMappings(this IServiceCollection services)
{
var mapperConfig = new MapperConfiguration(config =>
{
config.AddProfile<ApplicationMapping>();
});
IMapper mapper = mapperConfig.CreateMapper();
services.AddSingleton(mapper);
}
}
Then in my WebAPI project, on the Startup.cs class I put:
services.AddApplicationMappings();
And I use it normally with DI in my services:
public class AppService : IAppService
{
private readonly IAppRepository _appRepository;
private readonly IMapper _mapper;
public TruckService(IAppRepository appRepository, IMapper mapper)
{
_appRepository = appRepository;
_mapper = mapper;
}
}
I would like to use like this. But I'm getting an exception when the Mapper.AssertConfigurationIsValid(); line runs, saying that:
'Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance.'
What am I missing here? The problem seems to be in the Mapper.Map<IEnumerable<App>, List<App>>(src.ToList()) line of code.
But how can I get an instance of the Mapper there without using the static Mapper?
Mapper.AssertConfigurationIsValid();
This calls the static IMapper instance which is used in situations where you don’t use dependency injection. Since you never set up the static mapper, using it there will fail.
What you want to do instead is call AssertConfigurationIsValid on the actual mapper instance, the one that you are registering as a singleton. So you should remove the assert from the mapper profile and instead call it within your AddApplicationMappings method:
IMapper mapper = mapperConfig.CreateMapper();
mapper.AssertConfigurationIsValid();
services.AddSingleton(mapper);
Try using AddAutoMapper from AutoMapper.Extensions.Microsoft.DependencyInjection which you can add as a NuGet package.
So, you'd completely remove the MappingServiceExtension class, and then in Startup.cs add these two lines:
AutoMapper.Mapper.Reset();
services.AddAutoMapper(typeof(ApplicationMapping).Assembly);
I forgot the exact reason, but when using AutoMapper in across multiple projects/assemblies, you need to register it for DI this way. Read more here.
Similar to what #johnluke.laue suggested. In AddApplicationMappings simply replace the code with the following:
services.AddAutoMapper(config =>
{
config.AddProfile<ApplicationMapping>();
});
The above will automatically add the IMapper to the DI. In addition, modify the RegisterMappings function as below. You don't need to explicitly map the IEnumerable<T>. It will be mapped implicitly if the source/destination mappings exist.
private void RegisterMappings()
{
CreateMap<IEnumerable<App>, ListAppsDto>()
.ForMember(dest => dest.Apps, opt => opt.MapFrom(src => src.ToList());
}
It would be helpful to see the actual App and ListAppDto classes, as you don't explicitly need the above mappings. I hope this helps
I'm updating a project of mine to use AutoMapper 4.2, and I'm running into breaking changes. While I seem to have resolved said changes, I'm not entirely convinced I've done so in the most appropriate way.
In the old code, I have a NinjectConfiguration, and an AutoMapperConfiguration class that are each loaded by WebActivator. In the new version the AutoMapperConfiguration drops out and I instead instance a MapperConfiguration directly in the NinjectConfiguration class where the bindings are happening, like so:
private static void RegisterServices(
IKernel kernel) {
var profiles = AssemblyHelper.GetTypesInheriting<Profile>(Assembly.Load("???.Mappings")).Select(Activator.CreateInstance).Cast<Profile>();
var config = new MapperConfiguration(
c => {
foreach (var profile in profiles) {
c.AddProfile(profile);
}
});
kernel.Bind<MapperConfiguration>().ToMethod(
c =>
config).InSingletonScope();
kernel.Bind<IMapper>().ToMethod(
c =>
config.CreateMapper()).InRequestScope();
RegisterModules(kernel);
}
So, is this the appropriate way of binding AutoMapper 4.2 using Ninject? It seems to be working so far, but I just want to make sure.
In before IMapper interface didn't existed in the library so you had to implement interface and class below and bound them as a singleton pattern.
public interface IMapper
{
T Map<T>(object objectToMap);
}
public class AutoMapperAdapter : IMapper
{
public T Map<T>(object objectToMap)
{
//Mapper.Map is a static method of the library!
return Mapper.Map<T>(objectToMap);
}
}
Now you simply bind library's IMapper interface to single instance of mapperConfiguration.CreateMapper()
The Problem with your code tho, you should use a single instance(or as Ninject says, a constant) bind.
// A reminder
var config = new MapperConfiguration(
c => {
foreach (var profile in profiles) {
c.AddProfile(profile);
}
});
// Solution starts here
var mapper = config.CreateMapper();
kernel.Bind<IMapper>().ToConstant(mapper);