5

I'm going to use the UserName for tracking Created and Modified fields. To do that I have referenced to the System.Web assembly directly inside DbContext:

public void auditFields()
 {
            var auditDate = DateTime.Now;
            foreach (var entry in this.ChangeTracker.Entries<BaseEntity>())
            {
                switch (entry.State)
                {
                    case EntityState.Detached:
                        break;
                    case EntityState.Unchanged:
                        break;
                    case EntityState.Added:
                        entry.Entity.CreatedOn = auditDate;
                        entry.Entity.ModifiedOn = auditDate;
                        entry.Entity.CreatedBy = HttpContext.Current.User.Identity.Name ?? "anonymouse";
                        entry.Entity.ModifiedBy = HttpContext.Current.User.Identity.Name ?? "anonymouse";
                        break;
                    case EntityState.Deleted:
                        break;
                    case EntityState.Modified:
                        entry.Entity.ModifiedOn = auditDate;
                        entry.Entity.ModifiedBy = HttpContext.Current.User.Identity.Name ?? "anonymouse";
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
}

It works, But it's tightly coupling the DbContext with HttpContext which is not a good idea in case we'll expose the DbContext to a non-web environment. So I use this way:

public class ApplicationDbContext :
        IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>,
        IUnitOfWork
{
    public ApplicationDbContext()
            : base("ConnectionString")
        {
        }
        public ApplicationDbContext(string userName)
            : base("ConnectionString")
        {
            UserName = userName;
        }
    //Other codes
    public string UserName
        {
            get;
            private set;
        }
    public void auditFields()
    {
            var auditDate = DateTime.Now;
            foreach (var entry in this.ChangeTracker.Entries<BaseEntity>())
            {
                switch (entry.State)
                {
                    case EntityState.Detached:
                        break;
                    case EntityState.Unchanged:
                        break;
                    case EntityState.Added:
                        entry.Entity.CreatedOn = auditDate;
                        entry.Entity.ModifiedOn = auditDate;
                        entry.Entity.CreatedBy = UserName ?? "anonymouse";
                        entry.Entity.ModifiedBy = UserName ?? "anonymouse";
                        break;
                    case EntityState.Deleted:
                        break;
                    case EntityState.Modified:
                        entry.Entity.ModifiedOn = auditDate;
                        entry.Entity.ModifiedBy = UserName ?? "anonymouse";
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
    }
}

And in the Ioc config project (I'm using structureMap in another class library):

ioc.For<IUnitOfWork>()
                   .HybridHttpOrThreadLocalScoped()
                   .Use<ApplicationDbContext>()
                   .Ctor<string>().Is(HttpContext.Current.User.Identity.Name);

But when I run the application I'll get this error in above line:

Object reference not set to an instance of an object

It seems that it can't inject HttpContext.

Any idea?

Sirwan Afifi
  • 10,654
  • 14
  • 63
  • 110
  • "ThreadLocalScoped" sounds suspicious... - I have no idea how your container deals with lifetime, but HttpContext.Current valid only during requests only for threads handling request and it is likely your object is resolved at different time. – Alexei Levenkov Jun 27 '15 at 05:25
  • 1
    This would be so much simpler with [Pure DI](http://blog.ploeh.dk/2014/06/10/pure-di), not to mention type-safe. – Mark Seemann Jun 27 '15 at 14:52
  • 1
    Case in point: here's how much trouble I had doing something similar with Castle Windsor: http://stackoverflow.com/q/10854701/126014 – Mark Seemann Jun 27 '15 at 14:59

2 Answers2

4

Take a look at this link http://techbrij.com/service-layer-entity-framework-asp-net-mvc-unit-testing

The Author's solution looks like yours (but he used AutoFac instead of StructureMap). His "trick" to get the "Name" was Thread.CurrentPrincipal.Identity.Name;

Another thing, IMHO, I think you should use DateTimeOffSet instead of DateTime for the audit date. Using DateTimeOffSet you won't have problems with differents time zone. Like this:

DateTimeOffSet auditDate = DateTime.UtcNow;
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
Fabio
  • 11,892
  • 1
  • 25
  • 41
0

The following should help

ASP.NET MVC: HTTPContext and Dependency Injection

http://mikehadlow.blogspot.com/2008/08/taking-httpcontext-out-of-mvc-framework.html

Community
  • 1
  • 1
dshun
  • 223
  • 2
  • 9