AutoMapper IValueResolver: Cannot access a disposed object. Object name: 'IServiceProvider' - c#

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; }
}

Related

Accessing Claims Principal in the Service layer in the API of a Net core 6 App

I need to access ClaimsPrincipal within the service layer of a Net Core 6 app.
I could always just builder.Services.AddTransient<IHttpContextAccessor, HttpContextAccessor>(); in the Startup.cs & go my merry way but this is a no-no. Makes it difficult to test and more importantly this is a great example of leaky abstraction.
So, now what I have is the following
public class ClaimsProvider : IClaimsProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ClaimsProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public ClaimsPrincipal? GetClaimsPrincipal()
{
return _httpContextAccessor.HttpContext?.User;
}
}
public interface IClaimsProvider
{
ClaimsPrincipal? GetClaimsPrincipal();
}
Within my Startup.cs AddScoped() that takes an IHttpContextAccessor and return an IClaimsProvider. Then I simply build all services against IClaimsProvider
builder.Services.AddScoped<IClaimsProvider>(provider =>
{
var httpContextAccessor = provider.GetRequiredService<IHttpContextAccessor>();
return new ClaimsProvider(httpContextAccessor);
});
And the usual route for my services where I inject it as a dependency
private readonly IClaimsProvider _claimsProvider;
public SomeService(
IWebHostEnvironment hostingEnvironment,
IMapper mapper, IClaimsProvider claimsProvider, ...)
{
_hostingEnvironment = hostingEnvironment ??
throw new ArgumentNullException(nameof(hostingEnvironment));
_mapper = mapper ??
throw new ArgumentNullException(nameof(mapper));
_claimsProvider = claimsProvider;
}
public void SomeMethod()
{
var u = _claimsProvider.GetClaimsPrincipal();
foreach (var claim in u.Claims)
{
Console.WriteLine($"{claim.Type} : {claim.Value}");
}
}
My question is that is the above approach ok? Potentially, is there any other approach that is better than the one shown above?
To prevent a leaky abstract (the need for an IHttpContextAsccessor in your service), I would recommend using the Adapter Pattern.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddHttpContextAccessor();
services.AddScoped<IClaimsProvider, HttpContextClaimsProvider>();
}
public IClaimsProvider
{
public ClaimsPrinciple ClaimsPrinciple { get; }
}
// Adapter
public HttpContextClaimsProvider : IClaimsProvider
{
public HttpContextClaimsProvider(IHttpContextAccessor httpContext)
{
ClaimsProvider = httpContext?.User?.Principle as ClaimsPrinciple;
}
public ClaimsPrinciple ClaimsPrinciple { get; private set; }
}
public class YourService : IYourService
{
private readonly IClaimsProvider _claimsProvider;
public YourService(IClaimsProvider claimsProvider)
{
_claimsProvider= claimsProvider;
}
}
In our design each controller action receives an FooRequest. This is a POCO object where the properties are filled from the model binder by using corresponding attributes:
public class FooRequest : RequestBase
{
[FromRoute]
public int Id { get; set; }
[FromQuery]
public DateTime? Start { get; set; }
[FromBody]
public SomeComplexObject Configuration { get; set; }
}
Additionally we made a derived class using the suffix WithUser that has a ClaimsPrincipal as additional property:
public class FooRequestWithUser : FooRequest, IRequest<FooResponse>
{
public ClaimsPrincipal User { get; set; }
}
In a next step we made a helper class that provides a helper method that can receive the request instance, a claims principal and a type T:
public class RequestBase
{
public T Of<T>(ClaimsPrincipal user) where T: class, new()
{
// Check if T has base of own type
// Create instance and iterate all props to get value
// from this and and set value in instance.
// Additionally use reflection to set user property.
}
}
When our normal request class is derived from this one, we can call it within our controller and create a model containing the user as an additional property and forward it into our services by using MediatR:
public IActionResult DoFoo(FooRequest request)
{
var requestWithUser = request.Of<FooRequestWithUser>(User);
var result = mediator.Send(requestWithUser);
return Ok(result);
}
By this approach the claims principal is bound to the request consumed by the service and not something it has to additionally receive. Also it makes clear, that this request must be somehow authenticated and the service should check for some potential permissions or similar.
The approach you have described is generally considered a valid way to access the ClaimsPrincipal in the service layer of a .NET Core 6 app, as it abstracts the implementation details of the IHttpContextAccessor, making it easier to test and maintain.
An alternative approach could be to use the built-in dependency injection in ASP.NET Core to directly inject the ClaimsPrincipal into the service, without the need for a separate IClaimsProvider interface.
You can do this by registering the ClaimsPrincipal as a service in the ConfigureServices method of the Startup class.

Automapper Null on Dependency Injection Using [Inject] Attribute

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>();

Dependency injection between two ASP.NET Core projects

I'm currently developing a web application with ASP.NET Core and handling the database with Entity Framework Core. I have two projects in my VS Solution; WebApp (the main application) and DatabaseHandler (the EF Core handler). I have installed Entity Framework Core with the Pomelo package, since I'm using a MySQL database.
I've been following the Microsoft documentation to setup EF Core, connection strings and all that, and it works fine. I'm able to make migrations, make updates and do stuff with the database. I'm however not sure if I'm doing it correctly, since the latest EF Core tutorials use dependency injection and I'm not familiar with it.
Right now I'm passing the DbContext object as an argument from WebApp to DatabaseHandler, since I want all database-related stuff to only exist in DatabaseHandler. This works, but is it possible to call functions from another project and also share the DbContext object without passing it as an argument? I'm probably not explaining it well, I hope my code explains it better.
WebApp/Startup.cs:
This is where I load the connection string from appsettings.json.
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextPool<DataContext>(
options => options.UseMySql(Configuration.GetConnectionString("DefaultConnection")
));
services.AddRouting(options => options.LowercaseUrls = true);
services.AddControllersWithViews();
}
WebApp/HomeController.cs:
This is where I call the GetAllChallenges() function from the DatabaseHandler project, and I also pass the DataContext object as an argument. This is what I'm trying to avoid!
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly DataContext db;
public HomeController(ILogger<HomeController> logger, DataContext _db)
{
_logger = logger;
db = _db;
}
public IActionResult Challenges()
{
List<Challenge> ChallengesList = DatabaseHandler.HandleChallenges.GetAllChallenges(db);
return View(ChallengesList);
}
}
DatabaseHandler/DataContext.cs:
This is where I initialize the entity classes and so on.
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { }
// Tables
public DbSet<User> Users { get; set; }
public DbSet<Challenge> Challenges { get; set; }
// Data seeding
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Seed();
}
}
DatabaseHandler/HandleChallenges.cs:
This is where I have all my database functions. The results are returned back to the controller within the WebApp project.
public class HandleChallenges
{
public static List<Challenge> GetAllChallenges(DataContext db)
{
var Data = db.Challenges;
List<Challenge> ChallengesList = Data.ToList();
return ChallengesList;
}
}
I have looked into dependency injection, but I'm not sure how I can use this between two projects. Is there a less complicated way of achieving this, perhaps without using DI at all? I'm satisfied as long as I don't need to pass the DataContext object as an argument every time I need to call a function from DatabaseHandler.
Can someone help me understand? Thanks a lot in advance!
You could use Options pattern, which I have already used many times. Its working very well despite of database you use. Thanks to dependency injection you are able to access if from multiple projects. Reading documentation about Option pattern (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-3.1) is useful but I will also provide you with my own example :
First you create model to store you connection string, dbName etc. Remember to add it in a library outside your main project(eg. Web Api) :
public class NameOfYourProject_ApiDbSettings : IIMTTApiDbSettings
{
public NameOfYourProject_ApiDbSettings()
{
}
public string CollectionName { get; set; }
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
public interface I_NameOfYourProject_ApiDbSettings
{
string CollectionName { get; set; }
string ConnectionString { get; set; }
string DatabaseName { get; set; }
}
Secondly you make it available for all you projects :
services.Configure<NameOfYourProjectApiDbSettings>(options =>
{
options.ConnectionString
= Configuration.GetSection("NameOfYourProjectDbSettings:ConnectionString").Value;
options.DatabaseName
= Configuration.GetSection("NameOfYourProjectDbSettings:DatabaseName").Value;
});
Then you can use it in multiple projects. (Rememebr to add referance to you model -> point 1. I keep the model always with repository) I will give you my example where I use MongoDb :
private readonly IMongoDatabase _database = null;
public SomeObjectContext(IOptions<IMyProjectDbSettings> settings)
{
var client = new MongoClient(settings.Value.ConnectionString);
if (client != null)
_database = client.GetDatabase(settings.Value.DatabaseName);
}
public IMongoCollection<MyModel> MyModels
{
get
{
return _database.GetCollection<MyModel>("MyModels");
}
}
You need to extract an interface from the class (note the method is no longer static) and add a constructor for the context:
public interface IHandleChallenges
{
List<Challenge> GetAllChallenges();
}
public class HandleChallenges : IHandleChallenges
{
public HandleChallenges(DataContext context)
{
db = context;
}
private DataContext db;
public List<Challenge> GetAllChallenges()
{
var Data = db.Challenges;
List<Challenge> ChallengesList = Data.ToList();
return ChallengesList;
}
}
Then register it as a service:
services.AddScoped<IHandleChallenges, HandleChallenges>();
Your controller now receives this class in it's constructor instead of the context:
private IHandleChallenges _challengeHandler;
public HomeController(ILogger<HomeController> logger, IHandleChallenges challengeHandler)
{
_logger = logger;
_challengeHandler = challengeHandler;
}
And calls it from the action:
public IActionResult Challenges()
{
List<Challenge> ChallengesList = _challengeHandler.GetAllChallenges();
return View(ChallengesList);
}

use AutoMapper.EF6,but Mapper not initialized error happed

the error info :
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. InnerException:
StackTrace: at AutoMapper.Mapper.get_Configuration()
the asp.net mvc5 controller code
public override ActionResult Kendo_Read(DataSourceRequest request, IQueryable<Activity> results)
{
var data = results.ProjectTo<ActivityViewModel>();
var rdata = data.ToDataSourceResult(request);
return Json(rdata);
}
AutoMapper Configure Code
public class CRMProfile : Profile
{
CreateMap<Activity, ActivityViewModel>();
}
public static class Configuration
{
public static MapperConfiguration MapperConfiguration()
{
return new MapperConfiguration(x =>
{
x.AddProfile(new CRMProfile());
}
}
}
Register Code
internal static MapperConfiguration MapperConfiguration { get; private set; }
public class MvcApplication : System.Web.HttpApplication
{
MapperConfiguration = Configuration.MapperConfiguration();
}
i home return data type is IQueryable in the controller,not List,
I See the Question and Issue in stackoverflow but could not solved my problem.
From AutoMapper's source code you can see that this exception will be thrown when configuration is not initialized at all:
public static IConfigurationProvider Configuration
{
get => _configuration ?? throw new InvalidOperationException(InvalidOperationMessage);
private set => _configuration = value;
}
Make sure that Mapper is correctly configured using static properties or correctly registered using IoC container if you are using that (please, refer to documentation for details). Are you perhaps missing the Mapper.Initialize() call?

IServiceProvider in ASP.NET Core

I starting to learn changes in ASP.NET 5(vNext)
and cannot find how to get IServiceProvider, for example in "Model"'s method
public class Entity
{
public void DoSomething()
{
var dbContext = ServiceContainer.GetService<DataContext>(); //Where is ServiceContainer or something like that ?
}
}
I know, we configuring services at startup, but where all service collection staying or IServiceProvider?
You have to bring in Microsoft.Extensions.DependencyInjection namespace to gain access to the generic
GetService<T>();
extension method that should be used on
IServiceProvider
Also note that you can directly inject services into controllers in ASP.NET 5. See below example.
public interface ISomeService
{
string ServiceValue { get; set; }
}
public class ServiceImplementation : ISomeService
{
public ServiceImplementation()
{
ServiceValue = "Injected from Startup";
}
public string ServiceValue { get; set; }
}
Startup.cs
public void ConfigureService(IServiceCollection services)
{
...
services.AddSingleton<ISomeService, ServiceImplementation>();
}
HomeController
using Microsoft.Extensions.DependencyInjection;
...
public IServiceProvider Provider { get; set; }
public ISomeService InjectedService { get; set; }
public HomeController(IServiceProvider provider, ISomeService injectedService)
{
Provider = provider;
InjectedService = Provider.GetService<ISomeService>();
}
Either approach can be used to get access to the service. Additional service extensions for Startup.cs
AddInstance<IService>(new Service())
A single instance is given all the time. You are responsible for initial object creation.
AddSingleton<IService, Service>()
A single instance is created and it acts like a singleton.
AddTransient<IService, Service>()
A new instance is created every time it is injected.
AddScoped<IService, Service>()
A single instance is created inside of the current HTTP Request scope. It is equivalent to Singleton in the current scope context.
Updated 18 October 2018
See: aspnet GitHub - ServiceCollectionServiceExtensions.cs
I don't think it is a good idea for an entity (or a model) to have access to any service.
Controllers, on the other hand, do have access to any registered service in their constructors, and you don't have to worry about it.
public class NotifyController : Controller
{
private static IEmailSender emailSender = null;
protected static ISessionService session = null;
protected static IMyContext dbContext = null;
protected static IHostingEnvironment hostingEnvironment = null;
public NotifyController(
IEmailSender mailSenderService,
IMyContext context,
IHostingEnvironment env,
ISessionService sessionContext)
{
emailSender = mailSenderService;
dbContext = context;
hostingEnvironment = env;
session = sessionContext;
}
}
use GetRequiredService instead of GetService, like the example on ASP.NET Core tutorials ( https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/working-with-sql )
documentation on the method:
https://learn.microsoft.com/en-us/aspnet/core/api/microsoft.extensions.dependencyinjection.serviceproviderserviceextensions#Microsoft_Extensions_DependencyInjection_ServiceProviderServiceExtensions_GetRequiredService__1_System_IServiceProvider_
using Microsoft.Extensions.DependencyInjection;
using (var context = new ApplicationDbContext(serviceProvicer.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
Do not use GetService()
The difference between GetService and GetRequiredService is related with exception.
GetService() returns null if a service does not exist.
GetRequiredService() will throw exception.
public static class ServiceProviderServiceExtensions
{
public static T GetService<T>(this IServiceProvider provider)
{
return (T)provider.GetService(typeof(T));
}
public static T GetRequiredService<T>(this IServiceProvider provider)
{
return (T)provider.GetRequiredService(typeof(T));
}
}
Generally you want to have the DI do its thing and inject that for you:
public class Entity
{
private readonly IDataContext dbContext;
// The DI will auto inject this for you
public class Entity(IDataContext dbContext)
{
this.dbContext = dbContext;
}
public void DoSomething()
{
// dbContext is already populated for you
var something = dbContext.Somethings.First();
}
}
However, Entity would have to be automatically instantiated for you... like a Controller or a ViewComponent. If you need to manually instantiate this from a place where this dbContext is not available to you, then you can do this:
using Microsoft.Extensions.PlatformAbstractions;
public class Entity
{
private readonly IDataContext dbContext;
public class Entity()
{
this.dbContext = (IDataContext)CallContextServiceLocator.Locator.ServiceProvider
.GetService(typeof(IDataContext));
}
public void DoSomething()
{
var something = dbContext.Somethings.First();
}
}
But just to emphasize, this is considered an anti-pattern and should be avoided unless absolutely necessary. And... at the risk of making some pattern people really upset... if all else fails, you can add a static IContainer in a helper class or something and assign it in your StartUp class in the ConfigureServices method: MyHelper.DIContainer = builder.Build(); And this is a really ugly way to do it, but sometimes you just need to get it working.
I think the OP is getting confused. Entities should be as “thin” as possible. They should try not to contain logic, and or external references other than navigation properties. Look up some common patterns like repository pattern which helps to abstract your logic away from the entities themselves
Instead of getting your service inline, try injecting it into the constructor.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient(typeof(DataContext));
}
}
public class Entity
{
private DataContext _context;
public Entity(DataContext context)
{
_context = context;
}
public void DoSomething()
{
// use _context here
}
}
I also suggest reading up on what AddTransient means, as it will have a significant impact on how your application shares instances of DbContext. This is a pattern called Dependency Injection. It takes a while to get used to, but you will never want to go back once you do.

Categories

Resources