0

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);
}
Stefan
  • 17,448
  • 11
  • 60
  • 79
Bubinga
  • 613
  • 12
  • 29

2 Answers2

1

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:

  1. you are forced to pass the parameter, so there will be no ambiguity, it is easier to debug
  2. 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.

Stefan
  • 17,448
  • 11
  • 60
  • 79
  • 1
    No, the mapper is `ServiceLifetime.Transient` in AM.DI for a while now. – Lucian Bargaoanu Nov 06 '20 at 07:35
  • 1
    And the `GetAssemblies` stuff is clearly error prone, so most of the times it's easier to simply pass the assemblies that define profiles. – Lucian Bargaoanu Nov 06 '20 at 07:48
  • Good point, I addessed that in the comment, but corrected it – Stefan Nov 06 '20 at 08:02
  • Thanks for the answer, what is the accepted way to create an instance of that class now that it has a constructor? – Bubinga Nov 06 '20 at 19:53
  • Well, there are several of options: 1) create is manually, using `new` (not really reccommended), 2) create it in your controller, this will work by default: just add the `SearchesEditReview` to the controllers constructor 3) resolve it from the services provider. This should give you some hints, or search for Razor and dependency injection, also see: https://stackoverflow.com/questions/50788272/how-to-instantiate-a-dbcontext-in-ef-core/50788386#50788386 – Stefan Nov 07 '20 at 14:43
0

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>();
Brian Parker
  • 11,946
  • 2
  • 31
  • 41
  • How would I create an instance of ```SearchesEditReview``` with a constructor though? – Bubinga Nov 06 '20 at 21:24
  • @Bubinga Injection works in Controllers , Razor pages, Middleware etc. In a razor page you can use the [Inject] attribute. In a controller or middleware you simply put injectables in the constructor they will automatically be supplied. – Brian Parker Nov 06 '20 at 21:44
  • 1
    @Bubinga For example in a `.razor` page if you `[Inject] SearchesEditReview SearchesEditReview` In a controller create a constructor if it doesn't already have one and add SearchesEditReview as a parameter. In my example the injection system will know how to construct it. – Brian Parker Nov 06 '20 at 22:07
  • Ahhhh, that make sense. I just tried that and it works. Thank you :) – Bubinga Nov 07 '20 at 01:51