I have a Asp.Core project with dependency injection. The problem is that when I get an instance of a CustomerService my session is null. But I pass it through with dependency injection.
My Controller looks like this:
private ISessionService _sessionService;
private IContainer _container;
public AuthController(ISessionService sessionService, IContainer container) {
_container = container;
_sessionService = sessionService;
// here my session is NOT NULL
string userName = _sessionService.Username;
}
public IActionResult Index() {
// here I have some code so the line below is not always needeed and therefore not injected in the constructor
IUserService userService = _container.GetInstance<IUserService>();
// here my session is NULL
string name = userService.GetUserName();
}
public class UserService : IUserService {
private ISessionService _sessionService;
private IHttpContextAccessor _httpContextAccessor;
public UserService(ISessionService sessionSerivce, IHttpContextAccessor httpContextAccessor) {
_sessionService = sessionSerivce;
_httpContextAccessor = httpContextAccessor;
}
public string GetUserName() {
return _sessionService.User.Name;
}
}
my StartUp.cs where I'm using StructureMap
Container container = new Container(expr => {
expr.For<IHttpContextAccessor>().Use<HttpContextAccessor>();
expr.For<ISessionService>().Use<SessionService>();
expr.For<IUserService>().Use<UserService>();
});
So why is my session null when I instantiate a UserService object when using the container class ?
UPDATE
I know now that the Session object is null because the life cycle of a MVC page. In my BaseController class I created a method named InitContainer like this:
public void InitContainer(Type typeOfInterface, Type typeOfClass) {
Configure(expr =>
For(typeOfInterface).Use(typeOfClass)
.Ctor<ISessionService>().Is(_sessionService)
.Ctor<IHttpContextAccessor>().Is(_httpContextAccessor)
);
}
}
When I want to use an instance of a service than I do this in my Controller method:
public IActionResult Test() {
InitContainer(typeof(ICustomerService), typeof(CustomerService));
ICustomerService customerService = _container.GetInstance<ICustomerService>();
}
I don't know if it's ugly or the right way to do this, but it works for me now.
Has someone a better way to do this?
Related
I've taken the project I'm currently working on and I have to be deploying soon, however I'm having some issues with WCF. I have the RestService and the IRestService and when I make some test calls everything is working fine. However I want to use some of the data services I have created so I could query the database in order to perform CRUD operations. Here is what I have in the Service:
public readonly ITimesheetService timesheetService;
public readonly IProjectService projectService;
public readonly IUserService userService;
public readonly INotificationService notificationsService;
public readonly IDepartmentService departmentService;
public readonly IUserTokenService userTokenService;
public TimesheetRestService(ITimesheetService timesheetService, IProjectService projectService, IUserService userService, INotificationService notificationService, IDepartmentService departmentService, IUserTokenService userTokenService)
{
this.timesheetService = timesheetService;
this.projectService = projectService;
this.userService = userService;
this.notificationsService = notificationService;
this.departmentService = departmentService;
this.userTokenService = userTokenService;
}
public TimesheetRestService()
{
}
I had to add constructor without parameters otherwise it's not working. And then I have the following 2 methods:
public string[] NewMethod()
{
string[] data = new string[] { "grant_type", "p_username", "p_password" };
return data;
}
public IEnumerable<DepartmentServiceModel> GetDepertment()
{
string userId = GetUserId();
if (userId == null)
{
return null;
}
var deparments = this.departmentService.GetDepartments(userId).ToList().AsQueryable().To<DepartmentServiceModel>().ToList();
return deparments;
}
The NewMethod() is there just for testing purposes. My problem is with the GetDepartment() method as the departmentService and all the other services are null.
The developer before me has been using ninject, so I have tried adding the following code to the RegisterServices(IKernel kernel):
kernel.Bind(b => b.From(Assemblies.RestService).
SelectAllClasses().
BindDefaultInterface());
kernel.Bind<ITimesheetRestService>().To<TimesheetRestService>();
However when the TimeSheetRestService class is being initialized - the constructor with no parameters is being called. How can I call the constructor with the other services so I could use them to pull data from the database?
I'm using the following function to check whether the person that wants to access some database record is an owner of this record:
public class AccessGuard
{
public async Task<bool> IsOwnerOrHaveRightsAsync(UserManager<ApplicationUser> userManager, ApplicationUser claimant, ClaimsPrincipal User)
{
ApplicationUser fullUser = await userManager.GetUserAsync(User);
if (claimant.Id == fullUser.Id)
{
return true;
}
return false;
}
}
It works, but as I've noticed: ApplicationUser is now added to ChangeTracker. What it means is I cannot call userManager.GetUserAsync later in code, because I get this error:
The instance of entity type 'ApplicationUser' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
I usually use .AsNoTracking() while accessing database records, but there is nothing like that in userManager. How would you solve this?
I am using it in MVC Controller method as follows:
if (!await new AccessGuard().IsOwnerOrHaveRightsAsync(_userManager, Post.Author, User))
{
return Unauthorized();
}
You cannot use .AsNoTracking() with await userManager.GetUserAsync(User);. Alternatively you can do as follows:
public class AccessGuard
{
private readonly ApplicationDbContext _context;
private readonly IHttpContextAccessor _httpContextAccessor;
public AccessGuard(ApplicationDbContext context, IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
_context = context;
}
public async Task<bool> IsOwnerOrHaveRightsAsync(UserManager<ApplicationUser> userManager, ApplicationUser claimant, ClaimsPrincipal User)
{
var loggedInUserId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
ApplicationUser fullUser = _context.ApplicationUsers.AsNoTracking()
.FirstOrDefaultAsync(au => au.Id == loggedInUserId);
if (claimant.Id == fullUser.Id)
{
return true;
}
return false;
}
}
Then you should register IHttpContextAccessor in the Startup class as follows:
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Or you can also register as follows
services.AddHttpContextAccessor();
}
Then to access AccessGuard service in your MVC controller method, first register AccessGuard in Startup as follows:
services.AddScoped<AccessGuard>();
Then in your controller method:
public IActionResult Index()
{
AccessGuard accessGuardService = (AccessGuard) HttpContext.RequestServices.GetService(typeof(AccessGuard));
// Now call `accessGuardService` service method here
return View();
}
You can also get AccessGuard service as follows:
AccessGuard accessGuardService = HttpContext.RequestServices.GetService<AccessGuard>();
and it requires namespace using Microsoft.Extensions.DependencyInjection;
I have created a asp.net web api project and implemented the below HTTP GET method in AccountController and the related service method & repository method in AccountService & AccountRepository respectively.
// WEB API
public class AccountController : ApiController
{
private readonly IAccountService _accountService;
public AccountController(IAccountService accountService)
{
_accountService = accountService;
}
[HttpGet, ActionName("UserProfile")]
public JsonResult<decimal> GetUserSalary(int userID)
{
var account = _accountService.GetUserSalary(userID);
if (account != null)
{
return Json(account.Salary);
}
return Json(0);
}
}
Service / Business Layer
public interface IAccountService
{
decimal GetUserSalary(int userId);
}
public class AccountService : IAccountService
{
readonly IAccountRepository _accountRepository = new AccountRepository();
public decimal GetUserSalary(int userId)
{
return _accountRepository.GetUserSalary(userId);
}
}
Repository / Data Access Layer
public interface IAccountRepository
{
decimal GetUserSalary(int userId);
}
public class AccountRepository : IAccountRepository
{
public decimal GetUserSalary(int userId)
{
using (var db = new AccountEntities())
{
var account = (from b in db.UserAccounts where b.UserID == userId select b).FirstOrDefault();
if (account != null)
{
return account.Salary;
}
}
return 0;
}
}
UnityConfig
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IAccountService, AccountService>();
container.RegisterType<IAccountRepository, AccountRepository>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
But when I invoke the API method GetUserSalary() I get an error saying
An error occurred when trying to create a controller of type 'AccountController'. Make sure that the controller has a parameterless public constructor.
Check that you did not forget to register Unity IoC container itself:
if you use ASP.NET Framework it could be - Global.asax or Startap.cs (Owin) via UnityConfig.RegisterComponents() method.
if you use ASP.NET Core then in the Startup.cs file (I was unable to find official guides for its configuting)
Your current constructor has parameters (or args if you prefer).
see:
public AccountController(IAccountService accountService)
{
_accountService = accountService;
}
All you need to do is add a "Parameter-less Constructor" into the controller as well.
public AccountController()
{
}
Parameter-less constructors are usually above the ones that have params, though as far as I am aware this is only due to standards not any actual effect(s) it may cause.
There is also an already existing issue/question similar to this I will link below that may provide further details.
Make sure that the controller has a parameterless public constructor error
All classes that inherit from controller have already inserted the dependencies, but I do not know how to inject those who do not inherit from contoller.
in the example below the SendEmailAndLog() method waits for the ILoggerService interface to be injected. but I do not know how to do this.
public async Task Execute(IJobExecutionContext context)
{
SendEmailHelper sendEmailHelper = new SendEmailHelper();
await sendEmailHelper.SendEmailAndLog(user, "Task", user.UserName, UserManager, loggerService);
}
this task is called the method Application_Start() on Global.asax.cs
I created a constructor to receive this service as a parameter, this is working for the methods within the controllers that call the Class Emailservice. However there are classes that do not inherit from controller so I do not know how to inject.
My Service is:
public interface ILoggerService
{
void Error(ExceptionLogger exception);
void SaveLog();
}
public class LoggerService : ILoggerService
{
private readonly ILoggerRepository loggerRepository;
private readonly IUnitOfWork unitOfWork;
public LoggerService(ILoggerRepository loggerRepository, IUnitOfWork unitOfWork)
{
this.loggerRepository = loggerRepository;
this.unitOfWork = unitOfWork;
}
public void Error(ExceptionLogger exception)
{
loggerRepository.Error(exception);
}
public void SaveLog()
{
unitOfWork.Commit();
}
}
I am trying to learn TDD/BDD using NUnit and Moq.
The design that I have been following passes a DataService class to my controller to provide access to repositories.
I would like to Mock the DataService class to allow testing of the controllers.
There are lots of examples of mocking a repository passed to the controller but I can't work out how to mock a DataService class in this
scenerio.
Could someone please explain how to implement this?
Here's a sample of the relevant code:
[Test]
public void Can_View_A_Single_Page_Of_Lists()
{
var dataService = new Mock<DataService>();
var controller = new ListsController(dataService);
...
}
namespace Services
{
public class DataService
{
private readonly IKeyedRepository<int, FavList> FavListRepository;
private readonly IUnitOfWork unitOfWork;
public FavListService FavLists { get; private set; }
public DataService(IKeyedRepository<int, FavList> FavListRepository,
IUnitOfWork unitOfWork)
{
this.FavListRepository = FavListRepository;
this.unitOfWork = unitOfWork;
FavLists = new FavListService(FavListRepository);
}
public void Commit()
{
unitOfWork.Commit();
}
}
}
namespace MyListsWebsite.Controllers
{
public class ListsController : Controller
{
private readonly DataService dataService;
public ListsController(DataService dataService)
{
this.dataService = dataService;
}
public ActionResult Index()
{
var myLists = dataService.FavLists.All().ToList();
return View(myLists);
}
}
}
Create an interface like this:
public interface DataService
{
FavListService FavLists { get; }
void Commit();
}
Make your DataService implement this interface and your controller should depend on this interface. Problem solved :)
EDIT: This line of code:
dataService.FavLists.All().ToList();
is breaking the law of demeter and will be a pain to unit test your service. Create a method like AllFavList() on your service instead of all these chain of calls, it will be easier to mock.
EDIT2: How to mock you get property
dataService.SetupGet(d => d.FavLists).Returns(your_variable);