8
votes

I am trying to separate code from controller to service that I created. What I did is to create a User Service with interface IUserService.

Moved RegisterUser code from directly controller to UserService, the next challenge I get was that Url which works directly with Controller doesn't work with Service.

This code from Controller to Service has been changed like this:

if in Controller

var callbackUrl = Url.EmailConfirmationLink(user.Email, token, model.contactno, Request.Scheme);

Changed from Controller to Service:

  private IUrlHelper _urlHelper;

    public UserService (IUrlHelper urlHelper, HttpRequest request) {
_urlHelper = urlHelper;
}

this was constructor,

in Method I am calling it like this:

        var callbackUrl = _urlHelper.EmailConfirmationLink (user.Email, token, U.Email, _request.Scheme);

I mentioned in DI in Startup.cs like this:

services.AddScoped<IUserService, UserService>();

There is no Excpetion at compile time. At run time it is throwing below excption:

Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
      An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.IUrlHelper' while attempting to activate 'erp.Services.UserService'.

Not very clear what exactly has to be done.

here is full stack trace to see the error more closely. well I can't say the exact line as I am not debugging but just copying from the logs:

Executed endpoint 'erp.Controllers.AccountController.Register (erp)'
fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
      An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Http.HttpRequest' while attempting to activate 'erp.Services.UserService'.
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type serviceType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type serviceType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.CreateServiceAccessor(Type serviceType)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
2
In which line are you getting the exception?Niladri
Updated question, I can't say about line as it is not pointing me at that.Jay
@Jay Pass those dependencies via the called method instead of constructor injection as both of those dependencies are not available at the time of injection.Nkosi

2 Answers

14
votes

For Object reference not set to an instance of an object, it is caused by that you did not register IActionContextAccessor.

Try follow steps below:

  1. UserService

    public interface IUserService
    {
        void RegisterUser();
    }
    public class UserService : IUserService
    {
        private IUrlHelper _urlHelper;
        private HttpRequest _request;
    
        public UserService(IUrlHelper urlHelper, IHttpContextAccessor httpContextAccessor)
        {
            _urlHelper = urlHelper;
            _request = httpContextAccessor.HttpContext.Request;
        }
        public void RegisterUser()
        {
            var callbackUrl = _urlHelper.EmailConfirmationLink("user.Email", "token", _request.Scheme);
            //throw new NotImplementedException();
        }
    }
    
  2. Register

    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
    services.AddScoped<IUrlHelper>(x => {
        var actionContext = x.GetRequiredService<IActionContextAccessor>().ActionContext;
        var factory = x.GetRequiredService<IUrlHelperFactory>();
        return factory.GetUrlHelper(actionContext);
    });
    
    services.AddScoped<IUserService, UserService>();
    
5
votes

Register IUrlHelper in your DI Container like this :

services.AddScoped<IUrlHelper>(factory =>
{
    var actionContext = factory.GetService<IActionContextAccessor>()
                               .ActionContext;
    return new UrlHelper(actionContext);
});

And to use HttpContext in your services, you must use IHttpContextAccessor:

public class MyService
{
    private IHttpContextAccessor _httpContextAccessor;
    public MyService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void MyMethod()
    {
        // Use HttpContext like this
        var username = _httpContextAccessor.HttpContext.User.Identity.Name;
    }
}

Also, don't forget to register IHttpContextAccessor in your DI Container :

services.AddHttpContextAccessor();