In my C# Web API application I've added CreatedDate and CreatedBy columns in all of the tables. Now I want to populate these columns whenever a new record is added in any of the tables.
For this purpose I've overridden SaveChanges and SaveChangesAsync functions in the DbContext class like below:
public class AuthDbContext : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public override int SaveChanges()
{
AddTimestamps();
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync()
{
AddTimestamps();
return await base.SaveChangesAsync();
}
private void AddTimestamps()
{
var entities = ChangeTracker.Entries().Where(x => (x.State == EntityState.Added));
var currentUsername = !string.IsNullOrEmpty(HttpContext.Current?.User?.Identity?.Name)
? HttpContext.Current.User.Identity.Name
: "SYSTEM";
foreach (var entity in entities)
{
foreach (var propName in entity.CurrentValues.PropertyNames)
{
if (propName == "CreatedBy" && entity.State == EntityState.Added)
{
entity.CurrentValues[propName] = currentUsername;
}
else if (propName == "CreatedDate" && entity.State == EntityState.Added)
{
entity.CurrentValues[propName] = DateTime.Now;
}
}
}
}
}
Now when I call SaveChanges or SaveChangesAsync from anywhere in my controllers, HttpContext.Current is assigned and I can get the User name from it by using ttpContext.Current.User.Identity.Name. But when I use UserManager.UpdateAsync function (which internally calls SaveChangesAsync function in our DbContext class) to make changes to underlying user table, HttpContext.Current is set to null.
How can I access HttpContext in this particular case to fetch the User name?
The problem is that with SaveChangesAsync you do not know if you will have access to the HttpContext.Current because you might not be executing on the thread that the request is being served on.
The best way to solve this problem is with DI. You can create an interface and matching class where the implementation relies on HttpContextBase. Configure the DI framework to inject an IUserContext instance into your DbContext and to create a new instance of UserContext per request.
As far as which DI framework to use I am partial to Autofac but there are plenty out there to choose from and mostly have similar functionality.
public interface IUserContext {
bool IsAuthenticated {get;}
// additional properties like user id / name / etc
}
public class UserContext : IUserContext
{
public UserContext(HttpContextBase httpContext) {
this.IsAuthenticated = httpContext.User.Identity.IsAuthenticated;
// any other properties that you want to use later
}
}
Related
I'm working on an Asp.Net Core project targeted .Net 5 with Microsoft Identity and Entity framework core (Code first approache).
In my project some entities will inherit from IAuditProperties interface.
IAuditProperties:
This interface used to read/write some audition info from/in any Entity that implement it.
string CreatedBy { get; set; }
DateTime CreatedOn { get; set; }
bool IsEdited { get; set; }
string LastEditor { get; set; }
DateTime LastEditDate { get; set; }
In my project I wrote some extension methods that will write some auditon infos, all those extensions for any Entity that implemented the IAuditProperties interface.
WriteCreationAudit extension method as example
/// <summary>
/// Write audit properties for an <see cref="IAuditProperties"/> for the first creation
/// </summary>
/// <param name="obj"><see cref="IAuditProperties"/> object to write in</param>
/// <param name="appUser">Current user</param>
public static void WriteCreationAudit( this IAuditProperties obj,AppUser appUser)
{
obj.CreatedBy = appUser.FullName;
obj.CreatedOn = DateTime.Now.InMorocco();
obj.IsEdited = false;
}
What is exactly the core issue?
As you notice that the extention method WriteCreationAudit is recieves a appUser parameter, this parameter's type (AppUser) inherit from IdentityUser.
So, the exact issue is How can I create object from AppUser without pass it as parameter from the Controller ?
How I handle this issue at this time?
At this time I'm depending on Controllers and DI to get AppUser object and pass it to WriteCreationAudit method, and I don't love this technique.
So please, How can I achieve my goal about creating new object from AppUser from the extension method ? or if I can't achieve it is there any other good way ?
Massive thanks in advance.
Depend on the circumstance, I would suggest 2 approaching ways, then take whichever that suit your case most... or even better, take the idea and implement it your way.
Simple data was required
As your purposed, I saw every thing was required just a FullName and might be in the future userId. So, why just not simply put them somewhere in Jwt or even cookie depend on your authentication mechanism ? They're not such as ultra-secure information to guard. We can easily saw them here, even Jwt was designed to hold that kind of information. So, just inject IHttpContextAccessor into DbContext or repository if we make use of Repository pattern, take out User Info, then tweak a bit on the SaveChanges things.
Data required to process was some kiind complex or need to be secured.
Make something like BaseInfoRequest object that contain all the infomations we need, set them on some upper middleware and store in cache, with absolute expiration that equivalent to request timeout, the key should be HttpContext.Session.Id + "some constants string" that represent request infoObject. Then take them out from the cache wherever we need.
Just a small note: If we doesn't expose the UserName for example, but userId only, which mean foreach request we need to take UserName from somewhere. That's not a good idea in production scenarios. Take some consider about them to balance things out.
What's wrong with DI inject to controller then pass the param to the extension method?
I just recalled a while back Microsoft said don't inject SignInManager and UserManager in razor component (also not razor page/the razor component with #page). Instead, extend UserClaimsPrincipalFactory to add claims like:
public class AdditionalUserClaimsPrincipalFactory
: UserClaimsPrincipalFactory<AppUser, IdentityRole>
{
public AdditionalUserClaimsPrincipalFactory(
UserManager<AppUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{ }
public async override Task<ClaimsPrincipal> CreateAsync(AppUser user)
{
var principal = await base.CreateAsync(user);
var identity = (ClaimsIdentity)principal.Identity;
var claims = new List<Claim>();
claims.Add(new Claim("FullName", user.FullName?? ""));
identity.AddClaims(claims);
return principal;
}
}
I agree with #Gordon Khanh Ng. and this is just implementation difference
This is a very common behaviour and there are many ways to achieve this. Here is how you can do this. This is probably the easiest way
Override your SaveChanges()/ SaveChangesAsync() method in DbContext class. Also inject the IHttpContextAccessor in the constructor.
Then use this code inside your DbContext class.
The GetCurrentUserId() method may differ depending on your Identity implementation.
private string GetCurrentUserId()
{
var httpContext = _httpContextAccessor?.HttpContext ?? null;
if (httpContext.HasValue() && httpContext.User.HasValue())
{
var authenticatedUsername = httpContext.User.Claims.Where(c => c.Type == "sub")
.Select(c => c.Value).SingleOrDefault();
return authenticatedUsername;
}
return null;
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
ChangeTracker.DetectChanges();
var entries = ChangeTracker.Entries()
.Where(e => e.State != EntityState.Detached && e.State != EntityState.Unchanged);
foreach (var entry in entries)
{
if (entry.Entity is IAuditProperties trackable)
{
var now = DateTime.UtcNow;
var user = GetCurrentUserId();
switch (entry.State)
{
case EntityState.Added:
trackable.CreatedAt = now;
trackable.CreatedBy = user;
trackable.IsEdited = false;
break;
}
}
}
return base.SaveChangesAsync(cancellationToken);
}
I have the following code:
public void someMethod(){
...
var accounts = myRepo.GetAccounts(accountId)?.ToList();
...
foreach (var account in accounts)
{
account.Status="INACTIVE";
var updatedAccount = myRepo.AddOrUpdateAccounts(account);
}
}
public Account AddOrUpdateAccounts(Account account){
//I want to compare account in the Db and what is passed in. So get the account from DB
var accountFromDb = myRepo.GetAccounts(account.Id); //this doesn't return whats in the database.
//here accountFromDb.Status is returned as INACTIVE, but in the database the column value is ACTIVE
...
...
}
public IEnumerable<Account> GetAccounts(int id){
return id <= 0 ? null : m_Context.Accounts.Where(x => x.Id == id);
}
Here, inside someMethod() I am calling GetAccounts() that returns data from the Accounts table.
Then I am changing the Status of the account, and calling AddOrUpdateAccounts().
Inside AddOrUpdateAccounts(), I want to compare the account that was passed in and whats in the database. When I call GetAccounts(), it returned a record with STATUS="INACTIVE". I haven't done SaveChanges(). Why didn't GetAccounts() returned the data from the database? In the Db the status is still "ACTIVE"
The repository method should return IQueryable<Account> rather than IEnumerable<Account> as this will allow the the consumer to continue to refine any criteria or govern how the account(s) should be consumed prior to any query executing against the database:
I would consider:
public IQueryable<Account> GetAccountsById(int id){
return m_Context.Accounts.Where(x => x.Id == id);
}
Don't return #null, just the query. The consumer can decide what to do if the data is not available.
From there the calling code looks like:
var accounts = myRepo.GetAccounts(accountId).ToList();
foreach (var account in accounts)
{
account.Status="INACTIVE";
}
Your addOrUpdate wouldn't work:
public Account AddOrUpdateAccounts(Account account){
...
var account = myRepo.GetAccounts(account.Id); //this doesn't return whats in the database.
You pass in the Account as "account" then try declaring a local variable called "account". If you remove the var keyword you would load the DbContext's record over top your modified account and your changes would be lost. Loading the account into another variable isn't necessary as long as the account is still associated with the DbContext.
Edit: After changing the var account = ... statement to look like:
public Account AddOrUpdateAccounts(Account account){
...
var accountToUpdate = myRepo.GetAccounts(account.Id); //this doesn't return whats
accountToUpdate will show the modified status rather than what is in the database because that DbContext is still tracking the reference to the entity that you modified. (account) For instance if I do this:
var account1st = context.Accounts.Single(x => x.AccountId == 1);
var account2nd = context.Accounts.Single(x => x.AccountId == 1);
Console.WriteLine(account1st.Status); // I get "ACTIVE"
Console.WriteLine(account2nd.Status); // I get "ACTIVE"
account1st.Status = "INACTIVE";
Console.WriteLine(account2nd.Status); // I get "INACTIVE"
Both references point to the same instance. It doesn't matter when I attempt to read the Account the 2nd time, as long as it's coming from the same DbContext and the context is tracking instances. If you read the row via a different DbContext, or use AsNoTracking() with all of your reads then the account can be read fresh from the database. You can reload an entity, but if those variables are pointing at the same reference it will overwrite your changes and set the entity back to Unmodified. This can be a little confusing when watching an SQL profiler output because in some cases you will see EF run a SELECT query for an entity, but the entity returned has different, modified values than what is in the database. Even when loading from the tracking cache, EF can still execute queries against the DB in some cases, but it returns the tracked entity reference.
/Edit
When it comes to saving the changes, it really just boils down to calling the SaveChanges on the DbContext that the account is associated. The "tricky" part is scoping the DbContext so that this can be done. The recommended pattern for this is the Unit of Work. There are a few different ones out there, and the one I recommend for EF is Mehdime's DbContextScope, however you can implement simpler ones that may be easier to understand and follow. Essentially a unit of work encapsulates the DbContext so that you can define a scope that repositories can access the same DbContext, then commit those changes at the end of the work.
At the most basic level:
public interface IUnitOfWork<TDbContext> : IDisposable where TDbContext : DbContext
{
TDbContext Context { get; }
int SaveChanges();
}
public class UnitOfWork : IUnitOfWork<YourDbContext>
{
private YourDbContext _context = null;
TDbContext IUnitOfWork<YourDbContext>.Context
{
get { return _context ?? (_context = new YourDbContext("YourConnectionString"); }
}
int IUnitOfWork<YourDbContext>.SaveChanges()
{
if(_context == null)
return 0;
return _context.SaveChanges();
}
public void Dispose()
{
try
{
if (_context != null)
_context.Dispose();
}
catch (ObjectDisposedException)
{ }
}
}
With this class available, and using dependency injection via an IoC container (Autofac, Unity, or MVC Core) you register the unit of work as Instance per Request so that when the controller and repository classes request one in their constructor, they receive the same instance.
Controller / Service:
private readonly IUnitOfWork<YourDbContext> _unitOfWork = null;
private readonly IYourRepository _repository = null;
public YourService(IUnitOfWork<YourDbContext> unitOfWork, IYourRepository repository)
{
_unitOfWork = unitOfWork ?? throw new ArgumentNullException("unitOfWork");
_repository = repository ?? throw new ArgumentNullException("repository");
}
Repository
private readonly IUnitOfWork<YourDbContext> _unitOfWork = null;
public YourService(IUnitOfWork<YourDbContext> unitOfWork)
{
_unitOfWork = unitOfWork ?? throw new ArgumentNullException("unitOfWork");
}
private YourDbContext Context { get { return _unitOfWork.Context; } }
Big Disclaimer: This is a very crude initial implementation to explain roughly how a Unit of Work can operate, it is no way production suitable code. It has limitations, specifically around disposing the DbContext but should serve as a demonstration. Definitely look to implement a library that's already out there and addresses these concerns. These implementations properly manage the DbContext disposal and will manage a scope beyond the context, like a TransactionScope so that their SaveChanges is required even if the unitOfWork.Context.SaveChanges() is called.
With a unit of work available to the Controller/Service and Repository, the code to use the repository and update your changes becomes:
var accounts = myRepo.GetAccountsById(accountId).ToList();
foreach (var account in accounts)
{
account.Status="INACTIVE";
}
UnitOfWork.SaveChanges();
With a proper unit of work it will look more like:
using (var unitOfWork = UnitOfWorkFactory.Create())
{
var accounts = myRepo.GetAccountsById(accountId).ToList(); // Where myRepo can resolve the unit of work via locator.
foreach (var account in accounts)
{
account.Status="INACTIVE";
}
unitOfWork.SaveChanges();
}
This way if you were to call different repos to fetch data, perform a number of different updates, the changes would be committed all in one call at the end and rolled back if there was a problem with any of the data.
For every page request, I need to get some current user permissions data from the database and then use this information several times in the logic
(which is made of several objects)
I want to get data from the database once per request, and reuse it where needed by injecting it.
Is the following the best way to do this?
1) define a object like this
class ApplicationUser : IApplicationUserProvider
{
private UserPermissions _userPermissions;
...
public ApplicationUser(IDatabase userService)
{
_userPermissions = userService.GetUserPermission(); // this would executed for every request once
}
UserRoles GetRoles()
{
return _userPermissions;
}
}
2) define the ioc something like this
var container1 = new Container(c =>
{
c.For<IDatabase>().Use<Database>();
c.For<IApplicationUser>().Use<ApplicationUser >();
c.For<IApplicationLogic1>().Use<ApplicationLogic1>(); // this has IApplicationUser in ctor
c.For<IApplicationLogic2>().Use<ApplicationLogic2>(); // this has IApplicationUser in ctor
});
3) the controller would be
void PageController(IApplicationLogic1 l1)
{
l1.Process();
l2.Process();
}
The UserPermissions information is runtime data, and as explained here, runtime data should not be injected or resolved during construction of the object graphs.
Instead, the call to userService.GetUserPermission() should be moved out of the constructor. For instance by delaying the call by using a Lazy<T>:
class ApplicationUser : IApplicationUserProvider
{
private Lazy<UserPermissions> _userPermissions;
public ApplicationUser(IDatabase userService) {
_userPermissions = new Lazy<UserPermissions>(userService.GetUserPermission);
}
UserRoles GetRoles() {
return _userPermissions.Value.GetRoles();
}
}
Another option is to define a decorator on IDatabase that will implement the caching:
public class PerRequestCacheDatabaseDecorator : IDatabase
{
private IDatabase _decoratee;
public PerRequestCacheDatabaseDecorator(IDatabase decoratee) {
_decoratee = decoratee;
}
public UserPermissions GetUserPermission() {
var items = HttpContext.Current.Items;
if (items["permissions"] == null)
items["permissions"] = _decoratee.GetUserPermission();
return (UserPermissions)items["permissions"];
}
}
By wrapping the real database inside the PerRequestCacheDatabaseDecorator, you can simplify the ApplicationUser to the following:
class ApplicationUser : IApplicationUserProvider
{
private IDatabase _userService;
public ApplicationUser(IDatabase userService) {
_userService = userService;
}
public UserRoles GetRoles() {
return _userService.GetUserPermission().GetRoles();
}
}
Now neither the ApplicationUser nor the Database class are concerned with this performance optimization, which is a good thing. The decorator allows us to plugin this performance optimization without having to change any of the existing classes.
I want to access some data within my overriden SaveChanges() in my dbcontext without passing any parameters. Any suggestions? I'm working with MVC4 and Entity Framework Database-First.
public partial class Entities : DbContext
{
public override int SaveChanges()
{
// i want to get user info from MVC model but no need to pass any parameters when call SaveChanges()
var UserInfo = userInfo;
// Call the original SaveChanges(), which will save both the changes made and the audit records
return base.SaveChanges();
}
}
Solution 1: Dependency Injection
This solution is pretty extensible, but you would have to modify the code in your repositories and controllers to use the injected dependencies instead of creating new instances with new.
Install Ninject. In Visual Studio, find the Package Manager Console and run Install-Package Ninject.MVC4 -dependencyVersion Highest in there.
Add constructor injection. Modify your controller, so that it gets an instance of your repository in its constructor. Modify your repository, so that it gets an instance of your entity context in its constructor. Cache your dependencies in private fields. Example code:
// In your controller:
public MyController(MyRepository repo)
{
this.repo = repo;
}
// In your repository:
public MyRepository(Entities context)
{
this.context = context;
}
// In your entities:
public Entities(UserInfo userInfo)
{
this.userInfo = userInfo;
}
Add a UserInfo provider. We need to tell Ninject where to get the UserInfo from. We can use the provider interface here:
public class UserInfoProvider : Provider<UserInfo>
{
protected override UserInfo CreateInstance(IContext context)
{
UserInfo UserInfo = new UserInfo();
// Do some complex initialization here.
return userInfo;
}
}
Add bindings. We need to tell Ninject to use the provider. We also want the lifetime of a UserInfo instance and of our entity context to be bound to the request cycle of MVC. Update your App_Start\NinjectWebCommon.cs:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<Entities>()
.ToSelf()
.InRequestScope();
kernel.Bind<UserInfo>()
.ToProvider<UserInfoProvider>()
.InRequestScope();
}
Run your app. Ninject should use your constructors and provide the requested dependencies.
For further information, visit the Ninject Wiki.
Solution 2: Thread local context
This requires no modification to your repositories, but it makes the code less testable and resembles an anti pattern somehow. This won't work if your controller calls multithreaded code.
Add context class.
public class UserInfoContext : IDisposable
{
private static readonly ThreadLocal<UserInfo> UserInfos = new ThreadLocal<UserInfo>();
public static UserInfo Current
{
get
{
if (UserInfos == null)
{
throw new InvalidOperationException("UserInfoContext has not been set.");
}
return UserInfos.Value;
}
}
public static UserInfoContext Create(UserInfo userInfo)
{
if (userInfo == null)
{
throw new ArgumentNullException("userInfo");
}
if (UserInfos.Value != null)
{
throw new InvalidOperationException("UserInfoContext should not be nested.");
}
UserInfos.Value = userInfo;
return new UserInfoContext();
}
private UserInfoContext() { }
public void Dispose()
{
UserInfos.Value = null;
}
}
Wrap your controller code. Example:
public ActionResult Index()
{
using (UserInfoContext.Create(myUserInfo))
{
// do stuff that calls your repositories
return View();
}
}
Update your Entities class.
public partial class Entities : DbContext
{
public override int SaveChanges()
{
var UserInfo = UserInfoContext.Current;
// Call the original SaveChanges(), which will save both the changes made and the audit records
return base.SaveChanges();
}
}
I have a generic repository:
public class GenericRepository<TEntity> : AbstractRepository<TEntity>, IRepository<TEntity> where TEntity : class
{
private DbContext _context;
[...]
public GenericRepository(DbContext context)
{
_context = context;
context.Configuration.AutoDetectChangesEnabled = true;
_dbSet = _context.Set<TEntity>();
}
[...]
public void SaveChanges()
{
_context.SaveChanges();
}
[...]
public void Add(TEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_dbSet.Add(entity);
}
[...]
public virtual void Update(TEntity entity)
{
_context.Entry(entity).State = EntityState.Modified;
}
In my controller, there is this code:
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
if (project.Id == 0)
{
ProjectRepository.Add(project);
}
else
{
ProjectRepository.Update(project);
}
ProjectRepository.SaveChanges();
[...]
Selecting and Inserting works fine, but Updating not: I get an InvalidOperationException (english translation of the german error message is "An object with the same key already exists in the Object State Manager. The Object State Manager can not track multiple objects with the same key.").
I don't understand that, because I'm the only user on my development machine, and I did not modify the record at another place.
Any idea what I'm doing wrong here?
Take a look at these answers:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
"An object with the same key already exists in the ObjectStateManager..." exception is thrown when setting an entity state to modified
Basically you need to do this:
var entity = _context.Projects.Find(project.ProjectId);
_context.Entry(entity).CurrentValues.SetValues(project);
Hopefully this helps.
The project instance is created by model binding, not loaded from your repository, so you need to actually load a project instance from the repository, and then change the properties on that.
Disclaimer: i never used Entity Framework, i am writing this based on my experience with ASP.NET MVC and nHibernate but you should be able to apply the same pattern
Ok first off your actual problem is in fact a double key insert, this is the case because the project-object which get passed to your edit action is not the same object you are trying to update (this one has been created from your modelbinder based on the values in the FormValueProvider). it may has the excat same values and therefor the same id but for your ORM it`s a brand new object which has never been persisted to the DB.
you can prevent this by using the following pattern (quick'n dirty sample-code)
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
if (project.Id == 0)
{
ProjectRepository.Add(project);
}
else
{
Project dbProject = ProjectRepository.GetById(project.Id); //or whatever your method to retrieve entities by Id is named
UpdateModel(dbProject, "project"); //http://msdn.microsoft.com/en-us/library/dd470933.aspx
ProjectRepository.Update(project);
}
}
}
This is my solution for this problem in EF Core. Not sure if it can be done on EF6.
Repository Interface
public interface IRepository<TEntity> where TEntity : class
{
void Update(TEntity entity);
// I ommited the rest of the methos for simplicity
}
Repository Implementation
public class GenericRepository<T> : IRepository<T> where T : class
{
public void Update(T entity)
{
dbContext.Set<T>().Attach(entity);
dbContext.Entry(entity).State = EntityState.Modified;
// don't forget to dbContext.SaveChanges() here if needed
}
}