Change applied in database but DbContext does not show - asp.net

I have an asp.net webapi hosted using OWin and I use ninject for DI. Here is how I inject DbContext:
kernel.Bind<IAuthRepository>().To<AuthRepository>();
kernel.Bind<AuthDbContext>().ToSelf().InRequestScope();
I have a repository class that I use the context as below:
private AuthDbContext _ctx { get; set; }
public AuthRepository(AuthDbContext ctx)
{
_ctx = ctx;
}
There is this method in repository that I call to change some data:
var res = await _ctx
.Clients
.FirstOrDefaultAsync(clientEntity => clientEntity.UserRef.Id == p);
//do the modification
_ctx.Entry(res).State = EntityState.Modified;
_ctx.SaveChanges();
return res;
It saves the data correctly in db, but after, when I call another action and get that saved entity, I see that changes are not in the context.
public async Task<Client> FindClientById(Guid clientId)
{
return await _ctx
.Clients
.FirstOrDefaultAsync(clientEntity => clientEntity.Id == clientId);
}

Related

Quartz .NET The instance of entity type 'TABLENAME' cannot be tracked because

We have built an API with .NET Core 3.1 that extracts data from an Excel and stores it via
EF Core into a MS SQL database. We use Quartz. NET so that it is handled in a background thread. For DI we use Autofac.
We use Scoped Services to be able to use the DBContext via DI (as described here https://andrewlock.net/creating-a-quartz-net-hosted-service-with-asp-net-core/).
Unfortunately, saving the data still does not work when multiple users are using the application at the same time. We get the following error message:
The instance of entity type 'TABLENAME' cannot be tracked because another instance with the same key value for {'TABLEKEY'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
Here our related code:
Startup.cs
// Add DbContext
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default"), b => b.MigrationsAssembly("XY.Infrastructure")));
// Add Quartz
services.AddQuartz(q =>
{
// as of 3.3.2 this also injects scoped services (like EF DbContext) without problems
q.UseMicrosoftDependencyInjectionJobFactory();
// these are the defaults
q.UseSimpleTypeLoader();
q.UseDefaultThreadPool(tp =>
{
tp.MaxConcurrency = 24;
});
});
services.AddQuartzServer(options =>
{
// when shutting down we want jobs to complete gracefully
options.WaitForJobsToComplete = true;
});
// Add Services
services.AddHostedService<QuartzHostedService>();
services.AddSingleton<IJobFactory, SingletonJobFactory>();
services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
services.AddTransient<ImportJob>();
services.AddScoped<IApplicationDbContext, ApplicationDbContext>();
services.AddScoped<IMyRepository, MyRepository>();
Logic.cs
// Grab the Scheduler instance from the Factory
var factory = new StdSchedulerFactory();
var scheduler = await factory.GetScheduler();
var parameters = new JobDataMap()
{
new KeyValuePair<string, object>("request", message),
new KeyValuePair<string, object>("sales", sales),
};
var jobId = $"processJob{Guid.NewGuid()}";
var groupId = $"group{Guid.NewGuid()}";
// defines the job
IJobDetail job = JobBuilder.Create<ImportJob>()
.WithIdentity(jobId, groupId)
.UsingJobData(parameters)
.Build();
// defines the trigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity($"Trigger{Guid.NewGuid()}", groupId)
.ForJob(job)
.StartNow()
.Build();
// schedule Job
await scheduler.ScheduleJob(job, trigger);
// and start it off
await scheduler.Start();
QuartzHostedService.cs
public class QuartzHostedService : IHostedService
{
private readonly ISchedulerFactory _schedulerFactory;
private readonly IJobFactory _jobFactory;
private readonly ILogger<QuartzHostedService> _logger;
private readonly IEnumerable<JobSchedule> _jobSchedules;
public QuartzHostedService(
ISchedulerFactory schedulerFactory,
IJobFactory jobFactory,
IEnumerable<JobSchedule> jobSchedules,
ILogger<QuartzHostedService> logger)
{
_schedulerFactory = schedulerFactory;
_jobSchedules = jobSchedules;
_jobFactory = jobFactory;
_logger = logger;
}
public IScheduler Scheduler { get; set; }
public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
Scheduler.JobFactory = _jobFactory;
foreach (var jobSchedule in _jobSchedules)
{
var job = CreateJob(jobSchedule);
var trigger = CreateTrigger(jobSchedule);
await Scheduler.ScheduleJob(job, trigger, cancellationToken);
}
await Scheduler.Start(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await Scheduler?.Shutdown(cancellationToken);
}
private static IJobDetail CreateJob(JobSchedule schedule)
{
var jobType = schedule.JobType;
return JobBuilder
.Create(jobType)
.WithIdentity(jobType.FullName)
.WithDescription(jobType.Name)
.Build();
}
private static ITrigger CreateTrigger(JobSchedule schedule)
{
return TriggerBuilder
.Create()
.WithIdentity($"{schedule.JobType.FullName}.trigger")
.StartNow()
.Build();
}
}
SingletonJobFactory.cs
public class SingletonJobFactory : IJobFactory
{
private readonly IServiceProvider _serviceProvider;
public SingletonJobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
}
catch (Exception ex)
{
throw;
}
}
public void ReturnJob(IJob job) { }
}
Importjob.cs
[DisallowConcurrentExecution]
public class ImportJob : IJob
{
private readonly IServiceProvider _provider;
private readonly ILogger<ImportJob> _logger;
public ImportJob(IServiceProvider provider, ILogger<ImportJob> logger)
{
_provider = provider;
_logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
try
{
using (var scope = _provider.CreateScope())
{
var jobType = context.JobDetail.JobType;
var job = scope.ServiceProvider.GetRequiredService(jobType) as IJob;
var repo = _provider.GetRequiredService<MYRepository>();
var importFactSales = _provider.GetRequiredService<IImportData>();
var savedRows = 0;
var request = (MyRequest)context.JobDetail.JobDataMap.Get("request");
var sales = (IEnumerable<MyData>)context.JobDetail.JobDataMap.Get("sales");
await importFactSales.saveValidateItems(repo, request, sales, savedRows);
}
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
}
I have found a solution in the meantime. As #marko-lahma described in the comment, use the built-in hosted service and don't implement your own JobFactory.
Remove the SingletonJobFactory.cs and QuartzHostedService.cs
Use the Autofac.Extras.Quartz and Quartz.Extensions.Hosting Nuget Package
Don't use CreateScope anymore, inject all needed Dependencies over the Constructor
Register QuartzAutofacFactoryModule and QuartzAutofacJobsModule in the Startup.

AspNet.Security.OpenIdConnect vs OAuthAuthorizationProvider

I had an app in .NET Framework in which I implemented OAuthAuthorizationServer. Now I want to upgrade my app to .NET Core 2.1, so I did some R&D and decided to use ASOS. Now the issue is I have implemented ASOS and it is working fine but I have some chunks that I can't figure out how to convert.
private Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType),
context.Scope.Select(x => new Claim("claim", x)));
context.Validated(identity);
return Task.FromResult(0);
}
private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context)
{
var identity = new ClaimsIdentity(new GenericIdentity(context.ClientId, OAuthDefaults.AuthenticationType),
context.Scope.Select(x => new Claim("claim", x)));
context.Validated(identity);
return Task.FromResult(0);
}
private readonly ConcurrentDictionary<string, string> _authenticationCodes =
new ConcurrentDictionary<string, string>(StringComparer.Ordinal);
private void CreateAuthenticationCode(AuthenticationTokenCreateContext context)
{
context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
_authenticationCodes[context.Token] = context.SerializeTicket();
}
private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context)
{
string value;
if (_authenticationCodes.TryRemove(context.Token, out value))
{
context.DeserializeTicket(value);
}
}
private void CreateRefreshToken(AuthenticationTokenCreateContext context)
{
context.SetToken(context.SerializeTicket());
}
private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
}
Now I have couple of question:
Client Credentials and Resource owner password grant types are two different grant types so how can we differentiate in them using ASOS?
GrantResourceOwnerCredentials takes OAuthGrantResourceOwnerCredentialsContext as a param and GrantClientCredentials takes OAuthGrantClientCredentialsContext as a param. Both these contexts contains scope which is not available in ASOS.
How can I serialize and deserialize access and refresh tokens like I was doing OAuthAuthorizationProvider?
How do we handle refresh tokens in ASOS? I can see refresh tokens in response but I haven't write any logic for refresh token my self.
Client Credentials and Resource owner password grant types are two different grant types so how can we differentiate in them using ASOS?
public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
if (context.Request.IsClientCredentialsGrantType())
{
// ...
}
else if (context.Request.IsPasswordGrantType())
{
// ...
}
else
{
throw new NotSupportedException();
}
}
GrantResourceOwnerCredentials takes OAuthGrantResourceOwnerCredentialsContext as a param and GrantClientCredetails takes OAuthGrantClientCredentialsContext as a param. Both these contexts contains scope which is not available in ASOS
public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
var scopes = context.Request.GetScopes();
// ...
}
How can I serialize and deserialize access and refresh tokens like I was doing OAuthAUthorizationProvider?
By using the OnSerializeAccessToken/OnDeserializeAccessToken and OnSerializeRefreshToken/OnDeserializeRefreshToken events.
How do we handle refresh tokens in ASOS? I can see refresh tokens in response but I haven't write any logic for refresh token my self.
Unlike Katana's OAuth server middleware, ASOS provides default logic for generating authorization codes and refresh tokens. If you want to use implement things like token revocation, you can do that in the events I mentioned. Read AspNet.Security.OpenIdConnect.Server. Refresh tokens for more information.
Here's an example that returns GUID refresh tokens and stores the associated (encrypted) payload in a database:
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace AuthorizationServer
{
public class MyToken
{
public string Id { get; set; }
public string Payload { get; set; }
}
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options) { }
public DbSet<MyToken> Tokens { get; set; }
}
public class MyProvider : OpenIdConnectServerProvider
{
private readonly MyDbContext _database;
public MyProvider(MyDbContext database)
{
_database = database;
}
public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
{
if (!context.Request.IsPasswordGrantType() && !context.Request.IsRefreshTokenGrantType())
{
context.Reject(error: OpenIdConnectConstants.Errors.UnsupportedGrantType);
}
else
{
// Don't enforce client authentication.
context.Skip();
}
return Task.CompletedTask;
}
public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
if (context.Request.IsPasswordGrantType())
{
if (context.Request.Username == "bob" && context.Request.Password == "bob")
{
var identity = new ClaimsIdentity(context.Scheme.Name);
identity.AddClaim(new Claim(OpenIdConnectConstants.Claims.Subject, "Bob"));
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), identity.AuthenticationType);
ticket.SetScopes(OpenIdConnectConstants.Scopes.OfflineAccess);
context.Validate(ticket);
}
else
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The username/password couple is invalid.");
}
}
else
{
var token = await _database.Tokens.FindAsync(context.Request.RefreshToken);
_database.Tokens.Remove(token);
await _database.SaveChangesAsync();
context.Validate(context.Ticket);
}
}
public override async Task SerializeRefreshToken(SerializeRefreshTokenContext context)
{
context.RefreshToken = Guid.NewGuid().ToString();
_database.Tokens.Add(new MyToken
{
Id = context.RefreshToken,
Payload = context.Options.RefreshTokenFormat.Protect(context.Ticket)
});
await _database.SaveChangesAsync();
}
public override async Task DeserializeRefreshToken(DeserializeRefreshTokenContext context)
{
context.HandleDeserialization();
var token = await _database.Tokens.FindAsync(context.RefreshToken);
if (token == null)
{
return;
}
context.Ticket = context.Options.RefreshTokenFormat.Unprotect(token.Payload);
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDbContext>(options =>
{
options.UseInMemoryDatabase(nameof(MyDbContext));
});
services.AddAuthentication()
.AddOpenIdConnectServer(options =>
{
options.TokenEndpointPath = "/token";
options.ProviderType = typeof(MyProvider);
options.AllowInsecureHttp = true;
})
.AddOAuthValidation();
services.AddMvc();
services.AddScoped<MyProvider>();
}
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvcWithDefaultRoute();
}
}
}

How to get the current logged in user ID in ASP.NET Core?

I've done this before with MVC5 using User.Identity.GetUserId() but that doesn't seem to work here.
The User.Identity doesn't have the GetUserId() method.
I am using Microsoft.AspNet.Identity.
Update in ASP.NET Core Version >= 2.0
In the Controller:
public class YourControllerNameController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public YourControllerNameController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<IActionResult> YourMethodName()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier) // will give the user's userId
var userName = User.FindFirstValue(ClaimTypes.Name) // will give the user's userName
// For ASP.NET Core <= 3.1
ApplicationUser applicationUser = await _userManager.GetUserAsync(User);
string userEmail = applicationUser?.Email; // will give the user's Email
// For ASP.NET Core >= 5.0
var userEmail = User.FindFirstValue(ClaimTypes.Email) // will give the user's Email
}
}
In some other class:
public class OtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public OtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethodName()
{
var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
}
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();
}
For more readability write extension methods as follows:
public static class ClaimsPrincipalExtensions
{
public static T GetLoggedInUserId<T>(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(T) == typeof(string))
{
return (T)Convert.ChangeType(loggedInUserId, typeof(T));
}
else if (typeof(T) == typeof(int) || typeof(T) == typeof(long))
{
return loggedInUserId != null ? (T)Convert.ChangeType(loggedInUserId, typeof(T)) : (T)Convert.ChangeType(0, typeof(T));
}
else
{
throw new Exception("Invalid type provided");
}
}
public static string GetLoggedInUserName(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Name);
}
public static string GetLoggedInUserEmail(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Email);
}
}
Then use as follows:
public class YourControllerNameController : Controller
{
public IActionResult YourMethodName()
{
var userId = User.GetLoggedInUserId<string>(); // Specify the type of your UserId;
var userName = User.GetLoggedInUserName();
var userEmail = User.GetLoggedInUserEmail();
}
}
public class OtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public OtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethodName()
{
var userId = _httpContextAccessor.HttpContext.User.GetLoggedInUserId<string>(); // Specify the type of your UserId;
}
}
Until ASP.NET Core 1.0 RC1 :
It's User.GetUserId() from System.Security.Claims namespace.
Since ASP.NET Core 1.0 RC2 :
You now have to use UserManager.
You can create a method to get the current user :
private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(HttpContext.User);
And get user information with the object :
var user = await GetCurrentUserAsync();
var userId = user?.Id;
string mail = user?.Email;
Note :
You can do it without using a method writing single lines like this string mail = (await _userManager.GetUserAsync(HttpContext.User))?.Email, but it doesn't respect the single responsibility principle. It's better to isolate the way you get the user because if someday you decide to change your user management system, like use another solution than Identity, it will get painful since you have to review your entire code.
you can get it in your controller:
using System.Security.Claims;
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
or write an extension method like before .Core v1.0
using System;
using System.Security.Claims;
namespace Shared.Web.MvcExtensions
{
public static class ClaimsPrincipalExtensions
{
public static string GetUserId(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
}
}
and get wherever user ClaimsPrincipal is available :
using Microsoft.AspNetCore.Mvc;
using Shared.Web.MvcExtensions;
namespace Web.Site.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return Content(this.User.GetUserId());
}
}
}
I included using System.Security.Claims and I could access the GetUserId() extension method
NB: I had the using Microsoft.AspNet.Identity already but couldn't get the extension method. So I guess both of them have to be used in conjunction with one another
using Microsoft.AspNet.Identity;
using System.Security.Claims;
EDIT:
This answer is now outdated. Look at Soren's or Adrien's answer for a dated way of achieving this in CORE 1.0
For .NET Core 2.0 Only The following is required to fetch the UserID of the logged-in User in a Controller class:
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
or
var userId = HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
e.g.
contact.OwnerID = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
As stated somewhere in this post, the GetUserId() method has been moved to the UserManager.
private readonly UserManager<ApplicationUser> _userManager;
public YourController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public IActionResult MyAction()
{
var userId = _userManager.GetUserId(HttpContext.User);
var model = GetSomeModelByUserId(userId);
return View(model);
}
If you started an empty project you might need to add the UserManger to your services in startup.cs. Otherwise this should already be the case.
you have to import Microsoft.AspNetCore.Identity & System.Security.Claims
// to get current user ID
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
// to get current user info
var user = await _userManager.FindByIdAsync(userId);
For ASP.NET Core 2.0, Entity Framework Core 2.0, AspNetCore.Identity 2.0 api (https://github.com/kkagill/ContosoUniversity-Backend):
The Id was changed to User.Identity.Name
[Authorize, HttpGet("Profile")]
public async Task<IActionResult> GetProfile()
{
var user = await _userManager.FindByIdAsync(User.Identity.Name);
return Json(new
{
IsAuthenticated = User.Identity.IsAuthenticated,
Id = User.Identity.Name,
Name = $"{user.FirstName} {user.LastName}",
Type = User.Identity.AuthenticationType,
});
}
Response:
In .net core 3.1 (and other more recent versions), you can use:
private readonly UserManager<IdentityUser> _userManager;
public ExampleController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
Then:
string userId = _userManager.GetUserId(User);
Or async:
var user = await _userManager.GetUserAsync(User);
var userId = user.Id;
At this point, I'm trying to figure out why you'd use one over the other. I know the general benefits of async, but see both of these used frequently. Please post some comments if anyone knows.
For ASP.NET 5.0, I have an extension method as follow:
using System;
using System.ComponentModel;
using System.Security.Claims;
namespace YOUR_PROJECT.Presentation.WebUI.Extensions
{
public static class ClaimsPrincipalExtensions
{
public static TId GetId<TId>(this ClaimsPrincipal principal)
{
if (principal == null || principal.Identity == null ||
!principal.Identity.IsAuthenticated)
{
throw new ArgumentNullException(nameof(principal));
}
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(TId) == typeof(string) ||
typeof(TId) == typeof(int) ||
typeof(TId) == typeof(long) ||
typeof(TId) == typeof(Guid))
{
var converter = TypeDescriptor.GetConverter(typeof(TId));
return (TId)converter.ConvertFromInvariantString(loggedInUserId);
}
throw new InvalidOperationException("The user id type is invalid.");
}
public static Guid GetId(this ClaimsPrincipal principal)
{
return principal.GetId<Guid>();
}
}
}
So you can use it like:
using Microsoft.AspNetCore.Mvc;
using YOUR_PROJECT.Presentation.WebUI.Extensions;
namespace YOUR_PROJECT.Presentation.WebUI.Controllers
{
public class YourController :Controller
{
public IActionResult YourMethod()
{
// If it's Guid
var userId = User.GetId();
// Or
// var userId = User.GetId<int>();
return View();
}
}
}
in the APiController
User.FindFirst(ClaimTypes.NameIdentifier).Value
Something like this you will get the claims
Although Adrien's answer is correct, you can do this all in single line. No need for extra function or mess.
It works I checked it in ASP.NET Core 1.0
var user = await _userManager.GetUserAsync(HttpContext.User);
then you can get other properties of the variable like user.Email. I hope this helps someone.
For getting current user id in razor views, we can inject UserManager in the view like this:
#inject Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> _userManager
#{ string userId = _userManager.GetUserId(User); }
I hope you find it useful.
User.Identity.GetUserId();
does not exist in asp.net identity core 2.0. in this regard, i have managed in different way. i have created a common class for use whole application, because of getting user information.
create a common class PCommon & interface IPCommon
adding reference using System.Security.Claims
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Common.Web.Helper
{
public class PCommon: IPCommon
{
private readonly IHttpContextAccessor _context;
public PayraCommon(IHttpContextAccessor context)
{
_context = context;
}
public int GetUserId()
{
return Convert.ToInt16(_context.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
}
public string GetUserName()
{
return _context.HttpContext.User.Identity.Name;
}
}
public interface IPCommon
{
int GetUserId();
string GetUserName();
}
}
Here the implementation of common class
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using Pay.DataManager.Concreate;
using Pay.DataManager.Helper;
using Pay.DataManager.Models;
using Pay.Web.Helper;
using Pay.Web.Models.GeneralViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Pay.Controllers
{
[Authorize]
public class BankController : Controller
{
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger _logger;
private readonly IPCommon _iPCommon;
public BankController(IUnitOfWork unitOfWork, IPCommon IPCommon, ILogger logger = null)
{
_unitOfWork = unitOfWork;
_iPCommon = IPCommon;
if (logger != null) { _logger = logger; }
}
public ActionResult Create()
{
BankViewModel _bank = new BankViewModel();
CountryLoad(_bank);
return View();
}
[HttpPost, ActionName("Create")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Insert(BankViewModel bankVM)
{
if (!ModelState.IsValid)
{
CountryLoad(bankVM);
//TempData["show-message"] = Notification.Show(CommonMessage.RequiredFieldError("bank"), "Warning", type: ToastType.Warning);
return View(bankVM);
}
try
{
bankVM.EntryBy = _iPCommon.GetUserId();
var userName = _iPCommon.GetUserName()();
//_unitOfWork.BankRepo.Add(ModelAdapter.ModelMap(new Bank(), bankVM));
//_unitOfWork.Save();
// TempData["show-message"] = Notification.Show(CommonMessage.SaveMessage(), "Success", type: ToastType.Success);
}
catch (Exception ex)
{
// TempData["show-message"] = Notification.Show(CommonMessage.SaveErrorMessage("bank"), "Error", type: ToastType.Error);
}
return RedirectToAction(nameof(Index));
}
}
}
get userId and name in insert action
_iPCommon.GetUserId();
Thanks,
Maksud
TLDR:
In the Controler add:
using System.Security.Claims;
and then you can use:
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
endof TLDR;
Just an easy way in dot net 6 to test how to get the userID and test it in the default Blazor WebAssembly Core Hosted:
I added a String in WeatherForecast class named userId
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string userId { get; set; } = "nope";
}
Then in the WeatherForecastController
I add
using System.Security.Claims;
In the GET method I set WeatherForecast.userId to User.FindFirstValue(ClaimTypes.NameIdentifier):
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)],
userId = User.FindFirstValue(ClaimTypes.NameIdentifier)
})
.ToArray();
}
And finally in the FetchData.razor I modify the table to:
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
<th>User Id</th>
</tr>
</thead>
<tbody>
#foreach (var forecast in forecasts)
{
<tr>
<td>#forecast.Date.ToShortDateString()</td>
<td>#forecast.TemperatureC</td>
<td>#forecast.TemperatureF</td>
<td>#forecast.Summary</td>
<td>#forecast.userId</td>
</tr>
}
</tbody>
</table>
And then finally I get:
I hope it helps because in net core 6 sometimes it's quite difficult to find the answers
If you are using JWT tokens this code works:
User.FindFirstValue("sub");
use can use
string userid = User.FindFirst("id").Value;
for some reason NameIdentifier now retrieve the username (.net core 2.2)
Make sure that you have enable windows authentication. If you have anonymous authentication enabled you may be getting a null string.
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&tabs=visual-studio
I know there are many answers posted already, but maybe it will help someone as it did for me.
I mixed two solutions into one, and I am able to get the logged-in User and its Data.
I was using DotNet 5.
Following code, help to get the logged-in User.
var user = await _userManager.FindByNameAsync(HttpContext.User.Identity.Name);
I used the following package for _userManager
using Microsoft.AspNetCore.Identity;
And for HttpContext, I inherit my Controller from ControllerBase, and for ControllerBase Class I was using the following package
using Microsoft.AspNetCore.Mvc;
As an administrator working on other people's profile and you need to get the Id of the profile you are working on, you can use a ViewBag to capture the Id e.g ViewBag.UserId = userId; while userId is the string Parameter of the method you are working on.
[HttpGet]
public async Task<IActionResult> ManageUserRoles(string userId)
{
ViewBag.UserId = userId;
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
ViewBag.ErrorMessage = $"User with Id = {userId} cannot be found";
return View("NotFound");
}
var model = new List<UserRolesViewModel>();
foreach (var role in roleManager.Roles)
{
var userRolesViewModel = new UserRolesViewModel
{
RoleId = role.Id,
RoleName = role.Name
};
if (await userManager.IsInRoleAsync(user, role.Name))
{
userRolesViewModel.IsSelected = true;
}
else
{
userRolesViewModel.IsSelected = false;
}
model.Add(userRolesViewModel);
}
return View(model);
}
If you want this in ASP.NET MVC Controller, use
using Microsoft.AspNet.Identity;
User.Identity.GetUserId();
You need to add using statement because GetUserId() won't be there without it.

How to properly inject dependencies with the built in Funq container?

I have a cached repository
public interface IRepository
{
void LogWebUsage(string html);
IEnumerable<ApiKey> GetApiKeys();
ApiKey GetApiKey(Guid key);
}
public class Repository : IRepository
{
private static readonly ILog Log = LogManager.GetLogger("API.Repository");
public IDbConnectionFactory DbFactory { get; set; }
public void LogWebUsage(string request)
{
Log.Debug(request);
}
public virtual IEnumerable<ApiKey> GetApiKeys()
{
List<ApiKey> result = null;
using (var db = DbFactory.OpenDbConnection())
{
result = db.SelectParam<ApiKey>(q => q.Active);
}
return result;
}
public ApiKey GetApiKey(Guid key)
{
ApiKey result = null;
using (var db = DbFactory.OpenDbConnection())
{
result = (db.SelectParam<ApiKey>(q => q.Id == key)).FirstOrDefault();
}
return result;
}
}
public class CachedRepository : Repository
{
public ICacheClient Cache { get; set; }
public override IEnumerable<ApiKey> GetApiKeys()
{
const string cacheKey = "GetApiKeys";
var result = Cache.Get<IEnumerable<ApiKey>>(cacheKey);
if (result == null)
{
result = base.GetApiKeys();
if (result.Any())
{
Cache.Add(cacheKey, result, TimeSpan.FromMinutes(30));
}
}
return result;
}
}
And I configure it like so.
//Register any dependencies you want injected into your services
container.Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(ConfigUtils.GetConnectionString("DBConnstr"), true, SqlServerOrmLiteDialectProvider.Instance));
container.Register<ICacheClient>(new MemoryCacheClient());
container.Register<IRepository>(new CachedRepository());
container.RegisterAutoWired<CachedRepository>();
So what I was hoping for is that both the IDbConnectionFactory and ICacheClient would be injected at run-time, but they are null. How to you properly account for this type of dependency graph?
Thank you,
Stephen
Updated
After googling for a couple of hours I finally found a solution that works. Constructor injection though the config.
public class CachedRepository : Repository
{
private ICacheClient Cache { get; set; }
public CachedRepository(IDbConnectionFactory dbFactory, ICacheClient cache) : base(dbFactory)
{
Cache = cache;
}
public override IEnumerable<ApiKey> GetApiKeys()
{
const string cacheKey = "GetApiKeys";
var result = Cache.Get<IEnumerable<ApiKey>>(cacheKey);
if (result == null)
{
result = base.GetApiKeys();
if (result.Any())
{
Cache.Add(cacheKey, result, TimeSpan.FromMinutes(30));
}
}
return result;
}
}
Configuration
//Register any dependencies you want injected into your services
container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(ConfigUtils.GetConnectionString("DBConnstr"), true, SqlServerOrmLiteDialectProvider.Instance));
container.Register<ICacheClient>(c => new MemoryCacheClient());
container.Register<IRepository>(c => new CachedRepository(c.Resolve<IDbConnectionFactory>(), c.Resolve<ICacheClient>()));
It works, but I'd still like to know how to wire up the property injection.
Take care,
Stephen... again
The APIs for AutoWiring in ServiceStack's Funq IOC are here:
Using Generic API:
container.RegisterAutoWired<MyType>();
container.RegisterAutoWiredAs<MyType,IMyType>();
Using Run-time typed API:
container.RegisterAutoWiredType(typeof(MyType));
container.RegisterAutoWiredType(typeof(MyType),typeof(IMyType));
container.RegisterAutoWiredTypes(typeof(MyType),typeof(MyType2),typeof(MyType3));
So basically you can use any of the above APIs to auto-wire your dependencies, e.g:
container.Register<IDbConnectionFactory>(c => new
OrmLiteConnectionFactory(ConfigUtils.GetConnectionString("DBConnstr"), true,
SqlServerDialect.Provider));
container.Register<ICacheClient>(c => new MemoryCacheClient());
container.RegisterAutoWiredAs<CachedRepository,IRepository>(); //auto-wired

Mocking HttpSessionState in ASP.net for nunit testing

I've see n a lot of discussions surrounding HttpSessionState and asp.net MVC.
I'm trying to write tests for an asp.net application and wondering if it's possible to mock the HttpSessionState and if so, how?
I'm currently using Rhino Mocks and Nunit
Gilbert,
Maybe I'm too late for you. I'm using MSpec, but I think the concepts are similar. I needed to mock several components of the HttpContext in the controllers under test.
I started with these following classes to mock up the necessary (for my purposes) components in the HttpContextBase. I overrode only the necessary pieces inside the classes. Your needs will vary as to the mocks you need in the controller. It's fairly easy to add mocks as needed once you understand the pattern.
public class MockHttpContext : HttpContextBase
{
private readonly HttpRequestBase _request = new MockHttpRequest();
private readonly HttpServerUtilityBase _server = new MockHttpServerUtilityBase();
private HttpSessionStateBase _session = new MockHttpSession();
public override HttpRequestBase Request
{
get { return _request; }
}
public override HttpServerUtilityBase Server
{
get { return _server; }
}
public override HttpSessionStateBase Session
{
get { return _session; }
}
}
public class MockHttpRequest : HttpRequestBase
{
private Uri _url = new Uri("http://www.mockrequest.moc/Controller/Action");
public override Uri Url
{
get { return _url; }
}
}
public class MockHttpServerUtilityBase : HttpServerUtilityBase
{
public override string UrlEncode(string s)
{
//return base.UrlEncode(s);
return s; // Not doing anything (this is just a Mock)
}
}
public class MockHttpSession : HttpSessionStateBase
{
// Started with sample http://stackoverflow.com/questions/524457/how-do-you-mock-the-session-object-collection-using-moq
// from http://stackoverflow.com/users/81730/ronnblack
System.Collections.Generic.Dictionary<string, object> _sessionStorage = new System.Collections.Generic.Dictionary<string,object>();
public override object this[string name]
{
get { return _sessionStorage[name]; }
set { _sessionStorage[name] = value; }
}
public override void Add(string name, object value)
{
_sessionStorage[name] = value;
}
}
Here is how I setup the Controller Context to use the mocks (MSpec). This is setup for the actual tests on the contoller (the tests derive from this class)
public abstract class BlahBlahControllerContext
{
protected static BlahBlahController controller;
Establish context = () =>
{
controller = new BlahBlahController();
controller.ControllerContext = new ControllerContext()
{
Controller = controller,
RequestContext = new RequestContext(new MockHttpContext(), new RouteData()),
};
};
}
To further illustrate here is a test (Specification in MSpec world) that uses the mock session:
[Subject("ACCOUNT: Retrieve Password")]
public class retrieve_password_displays_retrieve_password2_page_on_success : BlahBlahControllerContext
{
static ActionResult result;
static RetrievePasswordModel model;
Establish context = () =>
{
model = new RetrievePasswordModel()
{
UserName = "Mike"
};
};
Because of = () =>
{
result = controller.RetrievePassword(model);
};
It should_return_a_RedirectToRouteResult = () =>
{
result.is_a_redirect_to_route_and().action_name().ShouldEqual("RetrievePassword2");
};
It session_should_contain_UN_value = () =>
{
controller.HttpContext.Session["UN"].ShouldEqual("Mike");
};
It session_should_contain_PQ_value = () =>
{
controller.HttpContext.Session["PQ"].ShouldEqual("Question");
};
}
I realize this doesn't use Rhino Mocks. I hope it illustrates the principles and readers can adopt it to their specific tools and methods.
If you need to instantiate exactly HttpSessionState for legacy code tests, you can leverage FormatterServices mechanism to get uninitialized object. To get it working it is needed to set private _container field though, like in internal constructor
Example:
var state = (HttpSessionState) System.Runtime.Serialization
.FormatterServices.GetUninitializedObject(typeof(HttpSessionState));
var containerFld = typeof(HttpSessionState).GetField(
"_container", BindingFlags.Instance | BindingFlags.NonPublic);
var itemCollection = new SessionStateItemCollection();
itemCollection["element"] = 1;
containerFld.SetValue(
state,
new HttpSessionStateContainer(
"1",
itemCollection,
new HttpStaticObjectsCollection(),
900,
true,
HttpCookieMode.UseCookies,
SessionStateMode.InProc,
false
)
);
look at the HttpSessionStateBase and HttpSessionStateWrapper classes in System.Web.Abstractions. HttpSessionStateBase is the abstract class from which HttpSessionState inherits, and HttpSessionStateWrapper is used to wrap a sealed class in an abstract class, which you can then mock in your tests.
A lot of the System.Web classes are sealed (for example, HttpSessionState), so it's a real pain to test your code when you have methods and classes that interact with them. One pattern I like to use to get around this looks like the following:
public void DoSomething(HttpSessionState state)
{
// take this HttpSeassionState and create an abstract HttpSessionStateBase
// instance
DoSomething(new HttpSessionStateWrapper(state));
}
internal void DoSomething(HttpSessionStateBase state)
{
// my actual logic for working with the session state
}
The public method is difficult to test, because HttpSessionState is sealed, and you can't mock it. However, the internal method operates on an HttpSessionStateBase instance, which you can mock. Note that I've marked it as internal because I don't want the outside world to be able to access that method. However, I do want my tests to be able to access that, so I'll modify my AssemblyInfo.cs to include something like this:
[assembly: InternalsVisibleTo("Vendor.Utilities.Tests")]
Finally, my test for this would look something like this:
[Test]
public void Test_DoSomething()
{
HttpSessionStateBase state = MockRepository.PartialMock<HttpSessionStateBase>();
state.Expect(s => ...);
MyClass.DoSomething(state);
state.VerifyAllExpectations();
}
Hope that helps. Good luck!
This is what I made up based on others contribution...
public class MockWebContext
{
public Mock<RequestContext> RoutingRequestContext { get; private set; }
public Mock<HttpContextBase> Http { get; private set; }
public Mock<HttpServerUtilityBase> Server { get; private set; }
public Mock<HttpResponseBase> Response { get; private set; }
public Mock<HttpRequestBase> Request { get; private set; }
public Mock<HttpSessionStateBase> Session { get; private set; }
public Mock<ActionExecutingContext> ActionExecuting { get; private set; }
public HttpCookieCollection Cookies { get; private set; }
private IDictionary items;
public MockWebContext()
{
RoutingRequestContext = new Mock<RequestContext>(MockBehavior.Loose);
ActionExecuting = new Mock<ActionExecutingContext>(MockBehavior.Loose);
Http = new Mock<HttpContextBase>(MockBehavior.Loose);
Server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
Response = new Mock<HttpResponseBase>(MockBehavior.Loose);
Request = new Mock<HttpRequestBase>(MockBehavior.Loose);
Session = new Mock<HttpSessionStateBase>(MockBehavior.Loose);
Cookies = new HttpCookieCollection();
items = new Dictionary<string, object>();
RoutingRequestContext.SetupGet(c => c.HttpContext).Returns(Http.Object);
ActionExecuting.SetupGet(c => c.HttpContext).Returns(Http.Object);
Http.SetupGet(c => c.Request).Returns(Request.Object);
Http.SetupGet(c => c.Response).Returns(Response.Object);
Http.SetupGet(c => c.Server).Returns(Server.Object);
Http.SetupGet(c => c.Session).Returns(Session.Object);
Http.SetupGet(c => c.Items).Returns(items);
Request.Setup(c => c.Cookies).Returns(Cookies);
Request.Setup(c => c.RequestContext).Returns(RoutingRequestContext.Object);
Response.Setup(c => c.Cookies).Returns(Cookies);
Session.Setup(c =>
c.Add(It.IsAny<string>(), It.IsAny<object>())
).Callback((string key, object value)=> items.Add(key, value));
Session.Setup(c =>
c.Remove(It.IsAny<string>())
).Callback((string key) => items.Remove(key));
Session.Setup(c =>
c.Clear()
).Callback(() => items.Clear());
Session.Setup(c =>
c[It.IsAny<string>()]
).Returns((string key)=> items[key]);
}
}
Check out the MvcContrib project.

Resources