I have problem when I'm trying to get httpcontext from IHttpContextAccessor field is always null in class.
There is my startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDistributedMemoryCache();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IUserContextServices, UserContextService>();
services.AddSession(options =>
{
options.Cookie.Name = ".AdventureWorks.Session";
options.IdleTimeout = TimeSpan.FromSeconds(10);
});
}
This is My UserContext Class which implements IUserContext interface
public class UserContextService : IUserContextServices
{
private readonly IHttpContextAccessor contextAccessor;
PQADBContext _context = new PQADBContext();
public UserContextService(IHttpContextAccessor accessor)
{
contextAccessor = accessor;
}
public UserContextService()
{
}
public HttpContext Context
{
get
{
return contextAccessor.HttpContext;
}
}
public int UserID()
{
return Context.Session.GetID("UserID").ConvertToInt();
}
public bool isLogin()
{
return Context.Session.GetBoolean("isLogin").ConvertToBool();
}
public UserAccount CreateSession(LoginViewModel logindata, bool EncryptPwd = true)
{
string error;
error = "";
try
{
string EncPwd = EncryptPwd ? EncryptDecryptHelper.Encrypt(logindata.Password) : logindata.Password;
var UserDetail =_context.UserAccount.Where(e => e.P_No == logindata.PNo && e.Password == EncPwd).SingleOrDefault();
if (UserDetail != null)
{
//HttpContext.Session.SetInt32()
// ///put all the properties in session variables
Context.Session.SetBoolean("isLogin", true);
Context.Session.SetID("UserID",UserDetail.AccountId);
Context.Session.SetID("P_No",Convert.ToInt32(UserDetail.P_No));
Context.Session.SetBoolean("isActive", true);
Context.Session.SetBoolean("Email", true);
Context.Session.SetID("RoleId", 1);
Context.Session.SetString("userName", "admin");
}
httpContext available in above class and also set the Session values but when i try to access httpcontext in this class it gives me null object reference
public class UserService
{
private readonly IHttpContextAccessor contextAccessor;
public IUserContextServices _userContext = new UserContextService();
public UserService()
{
}
public bool CreateEmployee(AppEmployees appemployee, int RoleId, bool isEmailSend, out string error)
{
appemployee.CreatedBy = _userContext.UserID(); //this line shows null reference exception
appemployee.CreatedDate = DateTime.Now;
}
You are newing up the UserContextService using the parameterless constructor
public IUserContextServices _userContext = new UserContextService();
instead of relying on Dependency Injection.
You need to configure your UserService to be used with your DI container - via constructor injection that would be
public class UserService
{
private readonly IUserServiceContext _userServiceContext;
public UserService(IUserServiceContext userServiceContext)
{
_userServiceContext = userServiceContext;
}
}
You will also need to amend your Startup.cs to register the UserService and you may want it to implement an interface too
Why do you use default constructor in your UserService?
You use next code:
public IUserContextServices _userContext = new UserContextService();
Of course here you have null for IHttpContextAccessor.
You need to use DI in your UserService.
Example:
private readonly IUserContextService _userContextService;
public UserService(IUserContextService userContextService)
{
_userContextService = userContextService;
}
There is good post about DI in .NET Core.
If you have set UserId directly as
public CurrentUserService(IHttpContextAccessor httpContextAccessor)
{
UserId = httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
}
This way you might get null UserId most of the time instead create httpContextAccessor field first like
public CurrentUserService(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public string UserId { get { return httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier); } }
Then get UserId, this way the problem of getting null UserId will be resolved.
Related
I have common DI usage in asp core application.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped(typeof(IUnitOfWork), typeof(UnitOfWork));//DAL
services.AddScoped(typeof(IUpService), typeof(UpService));//BLL
...
}
Controller get UpService by DI:
public BaseController(IUpService upService)
{
_upService = upService;
}
And in its turn BLL (upservice) get unitofwork instance:
public UpService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
In base controller i have property CurrentUser
public User CurrentUser
{
get
{
User user = null;
var tokenCookie = HttpContext.Request.Headers.FirstOrDefault(c => c.Key == "token");
if (!string.IsNullOrEmpty(tokenCookie.Value) && tokenCookie.Value != "undefined")
{
user = _upService.GetUserById(new Guid(tokenCookie.Value));
}
return user;
}
}
What i need is to pass CurrentUser to BL layer (to UpService). How to realise it by DI in asp core?
UPD:
Here the part of UpService, where i need current user.
public class UpService : IUpService
{
private IUnitOfWork _unitOfWork;
public User CurrentUser { get; set; }
public UpService(IUnitOfWork unitOfWork, IUserContext userContext)
{
_unitOfWork = unitOfWork;
//CurrentUser = userContext.CurrentUser;
}
public void Update(Document doc)
{
//here complex BL and then...
Document local;
var entity = _unitOfWork.DocumentsRepository.GetByID(doc.Id);
if (entity != null && HasChanges(local, entity))
{
entity.ChangedById = CurrentUser.Id;
_unitOfWork.Save();
}
}
public User GetUserById(Guid id)
{
return _unitOfWork.UserRepository.GetByID(id);
}
...
You should extract the CurrentUser into an abstraction, let's call it IUserContext. This abstraction can be implemented in your core layer. This way BL and DAL can access it. As part of your web application you create an implementation that adapts to ASP.NET Core and basically contains the logic that your CurrentUser property has. For instance:
public interface IUserContext
{
User CurrentUser { get; }
}
The adapter can look as follows:
public class AspNetUserContextAdapter : IUserContext
{
private readonly IHttpContextAccessor _accessor;
private readonly IUpService _upService;
public AspNetUserContextAdapter(IHttpContextAccessor accessor, IUpService _upService) {
_accessor = accessor;
_upService = upService;
}
public User CurrentUser
{
get
{
var context = _accessor.HttpContext;
var tokenCookie = context.Request.Headers.FirstOrDefault(c => c.Key == "token");
return !string.IsNullOrEmpty(tokenCookie.Value) && tokenCookie.Value != "undefined"
? _upService.GetUserById(new Guid(tokenCookie.Value))
: null;
}
}
}
This adapter can be registered as usual:
services.AddTransient<IUserContext, AspNetUserContextAdapter>();
On top of that, you might need to register ASP.NET Core's IHttpContextAccessor, since (in old versions of ASP.NET Core) it is not registered by default:
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
I'm currently developing a system where dependent on what domain a request comes from different website settings need to be loaded (eg. default language id) which is then used in the rest of the application. These settings are stored in a class WebsiteSettings which are injected into the rest of the application when needed.
The first option I tried was registering a service to access the HttpContext by doing this in my ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
//Register other services
services.TryAddScoped<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddScoped<WebsiteSettingsFiller>();
services.TryAddScoped(typeof(WebsiteSettings), s =>
{
var settingFiller = s.GetService<WebsiteSettingsFiller>();
return settingFiller.Create();
});
}
Next, in my WebsiteSettingsFiller service, I inject the IHttpContextAccessor and some other services that I need to load the site settings.
public class WebsiteSettingsFiller
{
protected readonly IRepository Database;
private readonly IHttpContextAccessor _accessor;
private readonly StartupSitePropertyService _sitePropertyService;
private IQueryable<Site> AllSites => Database.All<Site>();
private IQueryable<SiteLanguage> AllSiteLanguages => Database.All<SiteLanguage>();
public WebsiteSettingsFiller(IRepository db, StartupSitePropertyService siteProperties, IHttpContextAccessor accessor)
{
Database = db;
_accessor = accessor;
_sitePropertyService = siteProperties;
}
public WebsiteSettings Create()
{
var domain = _accessor.HttpContext.Request.Host.Host; //null exception on this line
#if DEBUG
domain = "www.somewebsite.com";
#endif
var config = GetConfigByDomain(domain);
return config;
}
private WebsiteSettings GetConfigByDomain(string domain)
{
var site = AllSites.OrderByDescending(s => s.Created).FirstOrDefault(t => t.Host == domain);
if (site == null) return null;
var languages = AllSiteLanguages.Where(sl => sl.SiteId == site.Id).ToList();
//get more variables
return new WebsiteSettings
{
/* Set variables */
}
}
}
Example injection of WebsiteSettings:
public class RouteService : BaseService
{
private IDictionary<int, string> _routeLanguages = null;
private readonly WebsiteRedisService _websiteRedisService;
public RouteService(IRepository db,
WebsiteSettings settings,
WebsiteRedisService websiteRedisService)
: base(db, settings)
{
_websiteRedisService = websiteRedisService;
}
public async Task<IDictionary<int, string>> RouteLanguagesAsync()
{
return _routeLanguages ??
(_routeLanguages = await _websiteRedisService.SiteLanguagesToAsync(Settings.SiteId));
}
}
Sadly, no matter what I try the HttpContext reference is always null. Does anyone have any idea what I can try to resolve this? Or am I just approaching this problem the wrong way? Any suggestions are greatly appreciated!
I have a class
public class SessionService : ISessionService
{
public const string SessionUserKey = "UserViewModel";
private readonly IHttpContextAccessor _httpContextAccessor;
private ISession _session => _httpContextAccessor.HttpContext.Session;
public SessionService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public UserViewModel LoggedInUser
{
get
{
return _session.GetObjectFromJson<UserViewModel>(SessionUserKey);
}
set
{
_session.SetObjectAsJson(SessionUserKey, value);
}
}
public void Destroy()
{
_session.Clear();
}
}
An instance of this class is injected to my controller like this:
Startup.cs
services.AddScoped<ISessionService, SessionService>();
AccountController.cs
public class AccountController : Controller
{
private readonly IUserAccountService _accountService;
private readonly ISessionService _sessionService;
public AccountController(IUserAccountService accountService, ISessionService sessionService)
{
_accountService = accountService;
_sessionService = sessionService;
}
}
However, in my view I want to access the currently logged in user like this:
#if(SomeClass.LoggedInUser != null){
// code removed for brevity
}
How can I achieve this without making the LoggedInUser property of my SessionService class static. It seems like something needs to me made static to have an access like this. But I was wondering if there was a better way to accomplish this. And no, I don't want to use the ViewBag.
I am not using ASP.Net Identity
You should use #inject if you want to inject services in your views using DI:
#inject WebProject.Services.ISessionService CurrentSessionService
#if (CurrentSessionService.LoggedInUser != null)
{
// ...
}
According to documents when I configure DbContext like below DI register it in scope (per http request)
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DBData>(options => {
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
}
);
The problem appears when I am trying to access it in another thread.
public class HomeController : Controller
{
private readonly DBData _context;
public HomeController(DBData context)
{
_context = context;
}
public IActionResult StartInBackground()
{
Task.Run(() =>
{
Thread.Sleep(3000);
//System.ObjectDisposedException here
var res = _context.Users.FirstOrDefault(x => x.Id == 1);
});
return View();
}
}
I want to configure DbContext creation per each call (AddTransition). It would give me possibility to write next code
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DBData>(options => {
//somehow configure it to use AddTransient
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
}
);
services.AddTransient<IUnitOfWorkFactoryPerCall, UnitOfWorkFactory>();
services.AddScoped<IUnitOfWorkFactoryPerRequest, UnitOfWorkFactory>();
services.AddMvc();
}
public interface IUnitOfWorkFactoryPerCall : IUnitOfWorkFactory { }
public interface IUnitOfWorkFactoryPerRequest : IUnitOfWorkFactory { }
public interface IUnitOfWorkFactory : IDisposable
{
DBData Context { get; }
}
public class UnitOfWorkFactory : IUnitOfWorkFactoryPerCall, IUnitOfWorkFactoryPerRequest
{
public UnitOfWorkFactory(DBData context)
{
Context = context;
}
public DBData Context
{
get; private set;
}
public void Dispose()
{
Context.Dispose();
}
}
So now if I want to create DBContext per request I will use IUnitOfWorkFactoryPerRequest, and when I want to use DBContext in some background thread I can use IUnitOfWorkFactoryPerCall.
My temporary solution.
I created singleton which can create Context "in transient way"
public class AppDependencyResolver
{
private static AppDependencyResolver _resolver;
public static AppDependencyResolver Current
{
get
{
if (_resolver == null)
throw new Exception("AppDependencyResolver not initialized. You should initialize it in Startup class");
return _resolver;
}
}
public static void Init(IServiceProvider services)
{
_resolver = new AppDependencyResolver(services);
}
private readonly IServiceProvider _serviceProvider;
public AppDependencyResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IUnitOfWorkFactory CreateUoWinCurrentThread()
{
var scopeResolver = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
return new UnitOfWorkFactory(scopeResolver.ServiceProvider.GetRequiredService<DBData>(), scopeResolver);
}
}
Then I call init method in Startup Configure method
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
AppDependencyResolver.Init(app.ApplicationServices);
//other configure code
}
And after all I can call AppDependencyResolver.Current.CreateUoWinCurrentThread() in some background thread.
If someone can provide more elegant solution I will be appreciated.
Within your controller, why are you trying to inject into private readonly DBData _context;? If you've registered your IUnitOfWorkFactoryPerCall via DI, you should be injecting that into your controller I believe? You then access your context via the interface.
To expand, this is what I am suggesting you do:
public class HomeController : Controller
{
private readonly IUnitOfWorkFactoryPerCall _contextFactory;
public HomeController(IUnitOfWorkFactoryPerCall contextFactory)
{
_contextFactory = contextFactory;
}
public IActionResult StartInBackground()
{
Task.Run(() =>
{
Thread.Sleep(3000);
//System.ObjectDisposedException here
var res = _contextFactory.Context.Users.FirstOrDefault(x => x.Id == 1);
});
return View();
}
}
Hi i have mi project in MVC5, i am using Identity 2.0, commonRepository and Structuremap to inject dependencies, the problem is when I am in the controller AccountController, i have one Contex and when my UnitOfWork inject the repositories it create other Instance.
how I can inject or replace the context of the identity whit my context from my UnitOfWork.
Regards
Update
AccountController
public class AccountController : Controller
{
private readonly ApplicationSignInManager SignInManager;
private readonly ApplicationUserManager UserManager;
private readonly IAuthenticationManager AuthenticationManager;
// private readonly IUbicationDao _ubicationDao;
private readonly ICultureDao _cultureDao;
private readonly ICurrencyDao _currecieDao;
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, ICultureDao cultureDao, ICurrencyDao currecieDao, IAuthenticationManager authenticationManager)
{
UserManager = userManager;
SignInManager = signInManager;
// _ubicationDao = ubicationDao;
_cultureDao = cultureDao;
_currecieDao = currecieDao;
AuthenticationManager = authenticationManager;
}}
DefaultRegistry StructureMap
public class DefaultRegistry : Registry {
#region Constructors and Destructors
public static IList<string> Assemblies
{
get
{
return new List<string>
{
"Interfaz",
"Persistencia"
};
}
}
public static IList<Tuple<string, string>> ManuallyWired
{
get
{
return new List<Tuple<string, string>>()
{
Tuple.Create("IUserStore<ApplicationUser>", "UserStore<ApplicationUser>>"),
Tuple.Create("DbContext", "ApplicationDbContext"),
Tuple.Create("IAuthenticationManager", "HttpContext.Current.GetOwinContext().Authentication"),
};
}
}
public DefaultRegistry()
{
Scan(
scan =>
{
foreach (var assembly in Assemblies)
{
scan.Assembly(assembly);
}
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
For<IUserStore<ApplicationUser>>().Use<UserStore<ApplicationUser>>();
For<DbContext>().Use<ApplicationDbContext>(new ApplicationDbContext());
For<IAuthenticationManager>().Use(() => HttpContext.Current.GetOwinContext().Authentication);
//DAos
For<ICultureDao>().Use<CultureDao>();
For<ICurrencyDao>().Use<CurrencyDao>();
For<IUbicationDao>().Use<UbicationDao>();
For<IActivatorWrapper>().Use<ActivatorWrapper>();
For<IUnitOfWorkHelper>().Use<UnitOfWorkHelper>();
}
#endregion
}
UnitofWork
public class UnitOfWorkHelper : IUnitOfWorkHelper
{
private ApplicationDbContext _sessionContext;
public event EventHandler<ObjectCreatedEventArgs> ObjectCreated;
public IApplicationDbContext DBContext
{
get
{
if (_sessionContext == null)
{
_sessionContext = new ApplicationDbContext();
((IObjectContextAdapter)_sessionContext).ObjectContext.ObjectMaterialized += (sender, e) => OnObjectCreated(e.Entity);
}
return _sessionContext;
}
}
private void OnObjectCreated(object entity)
{
if (ObjectCreated != null)
ObjectCreated(this, new ObjectCreatedEventArgs(entity));
}
public void SaveChanges()
{
this.DBContext.SaveChanges();
}
public void RollBack()
{
if (_sessionContext != null)
_sessionContext.ChangeTracker.Entries()
.ToList()
.ForEach(entry => entry.State = EntityState.Unchanged);
}
public void Dispose()
{
if (_sessionContext != null)
_sessionContext.Dispose();
}
}
after a lot analyzing and understand, I finally find the solution,
first i have to inject the same context to avoid inject a new instance of the Context. the solution is:
For<DbContext>().Use(()=>System.Web.HttpContext.Current.GetOwinContext().Get<ApplicationDbContext>());
before i was injecting and add a new instance of the DBContex.
For<DbContext>().Use<ApplicationDbContext>(new ApplicationDbContext());